第一章 UNIX基础知识
1.UNIX体系结构
系统调用:内核的接口;
公用函数库:构建在系统调用接口之上;
shell:为运行其他应用程序提供了一个接口。

UNIX操作系统的体系结构
2.登陆
2.1用户名
系统是用数值id来表示用户的,但为方便可读性,id和用户名之间存在一一映射。
系统口令文件在/etc/passwd中,为保证密码安全,加密口令转移到另一个文件中了。
2.2shell
我们linux常用的Bash,全名是Bourne-again shell,是GNU shell,被设计成遵循POSIX的,支持C shell和Korn shell两者的特色功能。
(在linux中,/bin/sh将链接到/bin/bash)
3.文件和目录
3.1文件系统
3.2文件名
文件名中不能出现斜线(/)和空操作符(null)。
斜线用来分隔构成路径名的各文件名,空操作符用来终止一个路径名。
. 表示当前目录;
.. 表示上一层目录。
3.3路径名
绝对路径:以斜线开头;
相对路径:相对于当前目录的路径。
ls(1)命令的简单实现:
/*
列出一个目录中的所有文件
*/
#include "apue.h"
#include <dirent.h>
int
main(int argc, char *argv[])
{
DIR *dp;
struct dirent *dirp;
if (argc != )
err_quit("usage: ls directory_name");
if ((dp = opendir(argv[])) == NULL)
err_sys("can't open %s", argv[]);
while ((dirp = readdir(dp)) != NULL)
printf("%s\n", dirp->d_name);
closedir(dp);
exit();
}
家目录:登陆时的默认目录,一般为/home/用户名。
4.输入和输出
4.1文件描述符号
文字描述符是一个小的非负整数,内核用以标识一个特定进程正在访问(读或者写)的文件。当内核打开一个或创建一个文件时,它就返回一个文件描述符。然后当读、写文件时,就可使用它。
4.2标准输入、标准输出和标准错误
一般,每当运行一个新程序时,所有的shell都为其打开三个文件描述符:标准输入、标准输出以及标准错误。
4.3不带缓冲的I/O
函数open,read,write,lseek,close提供了不带缓冲的I/O。
所谓不带缓冲,就是告诉系统调用操作多少,那么它就会尽量操作这些数据然后返回。
而对于带缓冲的,一般会等到缓冲区满了才进行操作。
4.4标准I/O
提供了带缓冲的接口。
5.程序和进程
程序:存放在磁盘上,可执行;
进程:实例化的程序,每个进程有唯一个数字标识符,为进程ID;
3个主要的进程控制函数:fork,exec和waitpid:
fork创建一个新的子进程,是父进程的复制品,fork向父进程返回子进程ID,向子进程返回0;
在子进程中,调用execlp执行新程序文件,这就用新的程序文件来替换了子进程原先执行的程序文件,因此产生了一个新的进程;
如果父进程希望等待子进程终止,则用waitpid。
我们可以使用getpid()来获得进程id,它返回一个pid_t数据类型,我们不知道它的大小,但是标准保证它能存放在一个long int中。
从标准输入读入命令并执行(不能传参数):
#include "apue.h"
#include <sys/wait.h>
int
main(void)
{
char buf[MAXLINE]; /* from apue.h */
pid_t pid;
int status;
printf("%% "); /* print prompt (printf requires %% to print %) */
while (fgets(buf, MAXLINE, stdin) != NULL) {
if (buf[strlen(buf) - ] == '\n')
buf[strlen(buf) - ] = ; /* replace newline with null */
if ((pid = fork()) < ) {
err_sys("fork error");
} else if (pid == ) { /* child */
execlp(buf, buf, (char *));
err_ret("couldn't execute: %s", buf);
exit();
}
/* parent */
if ((pid = waitpid(pid, &status, )) < )
err_sys("waitpid error");
printf("%% ");
}
exit();
}
一些个人的理解:我们使用bash 下达命令时,产生的新进程是bash的子进程。
线程:同一进程的所有线程共享同一地址空间,文件描述符,栈以及进程相关属性。
6.出错处理
当UNIX函数出错时,往常返回一个负值,同时将整型变量errno设置为具有特定信息的一个值。例如,open函数如成功执行则返回一个非负文件描述符,如出错则返回-1,同时设置errno。在open出错时,有大约15种不同的errno值(文件不存在,许可权问题等)。
C标准定义了两个函数来帮助打印错误信息:
char *strerror(int errnum); //返回:指向消息字符串的指针
void perror(const char *msg); //输出由msg指向的字符串,然后是一个冒号,一个空格,接着是对应于errno值的出错信息,最后换行。
例子:
#include "apue.h"
#include <errno.h>
int
main(int argc, char *argv[])
{
fprintf(stderr, "EACCES: %s\n", strerror(EACCES));
errno = ENOENT;
perror(argv[]);
exit();
}
编译送到a.out,执行和结果:
$ ./a.out
EACCES: Permission denied
./a.out: No such file or directory
出错分类:致命错误和非致命错误;
7.用户标识
用户id(/etc/passwd),组id(/etc/group),附加组id(一个用户可以属于多个组)。
(用id来存储,假设每个值用双字节整型值存放,则这两个值只需要4个字节)
8.信号
信号处理:
- 忽略该信号;
- 按系统默认方式处理;
- 提供一个函数,信号发生时调用该函数——捕捉该信号。
当向一个进程发信号时,我们需要是该进程的拥有者或者超级用户。
9.时间值
时钟时间, 用户CPU时间, 系统CPU时间。
10.系统调用和函数
应用程序代码——系统调用 or
应用程序代码——C库函数 or
应用程序代码——C库函数——系统调用;
例子: 应用程序——malloc分配内存——sbrk系统调用。
系统调用通常提供一个最小接口,函数通常实现比较复杂的功能。
(UNIX中,每个系统调用在标准C库中设置一个具有相同名字的函数,所以我们可以通过标准C库来进行系统调用)