系统调用:内核的接口;
公用函数库:构建在系统调用接口之上;
shell:为运行其他应用程序提供了一个接口。
系统是用数值id来表示用户的,但为方便可读性,id和用户名之间存在一一映射。
系统口令文件在/etc/passwd中,为保证密码安全,加密口令转移到另一个文件中了。
我们linux常用的Bash,全名是Bourne-again shell,是GNU shell,被设计成遵循POSIX的,支持C shell和Korn shell两者的特色功能。
(在linux中,/bin/sh将链接到/bin/bash)
文件名中不能出现斜线(/)和空操作符(null)。
斜线用来分隔构成路径名的各文件名,空操作符用来终止一个路径名。
. 表示当前目录;
.. 表示上一层目录。
绝对路径:以斜线开头;
相对路径:相对于当前目录的路径。
ls(1)命令的简单实现:
/*
列出一个目录中的所有文件
*/
#include "apue.h"
#include <dirent.h>
int
main(int argc, char *argv[])
{
DIR *dp;
struct dirent *dirp;
if (argc != 2)
err_quit("usage: ls directory_name");
if ((dp = opendir(argv[1])) == NULL)
err_sys("can't open %s", argv[1]);
while ((dirp = readdir(dp)) != NULL)
printf("%s\n", dirp->d_name);
closedir(dp);
exit(0);
}
家目录:登陆时的默认目录,一般为/home/用户名。
文字描述符是一个小的非负整数,内核用以标识一个特定进程正在访问(读或者写)的文件。当内核打开一个或创建一个文件时,它就返回一个文件描述符。然后当读、写文件时,就可使用它。
一般,每当运行一个新程序时,所有的shell都为其打开三个文件描述符:标准输入、标准输出以及标准错误。
函数open,read,write,lseek,close提供了不带缓冲的I/O。
所谓不带缓冲,就是告诉系统调用操作多少,那么它就会尽量操作这些数据然后返回。
而对于带缓冲的,一般会等到缓冲区满了才进行操作。
提供了带缓冲的接口。
程序:存放在磁盘上,可执行;
进程:实例化的程序,每个进程有唯一个数字标识符,为进程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) - 1] == '\n')
buf[strlen(buf) - 1] = 0; /* replace newline with null */
if ((pid = fork()) < 0) {
err_sys("fork error");
} else if (pid == 0) { /* child */
execlp(buf, buf, (char *)0);
err_ret("couldn't execute: %s", buf);
exit(127);
}
/* parent */
if ((pid = waitpid(pid, &status, 0)) < 0)
err_sys("waitpid error");
printf("%% ");
}
exit(0);
}
一些个人的理解:我们使用bash 下达命令时,产生的新进程是bash的子进程。
线程:同一进程的所有线程共享同一地址空间,文件描述符,栈以及进程相关属性。
当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[0]);
exit(0);
}
编译送到a.out,执行和结果:
$ ./a.out
EACCES: Permission denied
./a.out: No such file or directory
出错分类:致命错误和非致命错误;
用户id(/etc/passwd),组id(/etc/group),附加组id(一个用户可以属于多个组)。
(用id来存储,假设每个值用双字节整型值存放,则这两个值只需要4个字节)
信号处理:
忽略该信号;
按系统默认方式处理;
提供一个函数,信号发生时调用该函数——捕捉该信号。
当向一个进程发信号时,我们需要是该进程的拥有者或者超级用户。
时钟时间, 用户CPU时间, 系统CPU时间。
应用程序代码——系统调用 or
应用程序代码——C库函数 or
应用程序代码——C库函数——系统调用;
例子: 应用程序——malloc分配内存——sbrk系统调用。
系统调用通常提供一个最小接口,函数通常实现比较复杂的功能。
(UNIX中,每个系统调用在标准C库中设置一个具有相同名字的函数,所以我们可以通过标准C库来进行系统调用)