unix环境高级编程第八章读书笔记

来源:互联网 发布:ajax 返回json eval 编辑:程序博客网 时间:2024/06/06 03:28

终于又拿起了apue这本好书,再读第二遍真的是思路清晰了好多,人生真的是螺旋式的上升啊。哈哈!

进程标志:

系统中的每一个进程都会有自己的独一无二的进程标志(PID),书上说了几个重要的进程。0号进程是调度进程,1号进程是init进程是系统在自举完成后加载系统所需要的进程,它会依据一些在磁盘上的重要文件,来进行初始化操作。

函数fork:

这个函数还是很有意思的,它是一个会返回两次的函数,在父进程中返回一次,同时也会在子进程中返回一次。在子进程中返回0,而在父进程中返回子进程的PID。在fork之后子进程就是父进程的一个副本,这个副本中复制了父进程的栈,变量等,但是和父进程共享正文段,在一些实现当中在fork之后,父子进程都被设置为只读的,一旦父子进程有写入操作,进入另外开启一个物理页进行映射,这种技术叫做写时复制。书上为了更好的解释,给出了一个非常精妙的案例。

/*************************************************************************    > File Name: fork_1.c    > Author: jeff zhu    > Mail: 908190355@qq.com     > Created Time: 2016年10月21日 星期五 21时59分46秒 ************************************************************************/#include "apue.h"int globevar = 6;char buf[] = "a write to stdout\n";int main () {    int var;    pid_t pid;        var = 88;    if (write (STDOUT_FILENO , buf , sizeof (buf)-1) != sizeof(buf)-1) //write系统调用是不会有缓冲的,直接输出        err_sys ("write error");    printf ("before fork.\n"); //在被定向到文件时,我们并没有对其进行冲洗。    if ((pid = fork()) < 0) //从此出开始有两个进程,一个文件在系统中生产出了两个进程。        err_sys ("write error");    else if (pid == 0) {        globevar++;        var++;    }else {        sleep (2); //父进程睡眠两秒    }    printf ("pid = %ld, glob = %d, var = %d\n" , (long)getpid () , globevar , var);    exit (0);}
如果直接交互式的输出,那么“before fork\n”这句话只能输出一次,但是如果用shell操作使其重定向至文件,那么前面这个字符串就会打印两次。这是因为,直接和命令行交互的话,那么IO是行缓冲的一旦遇到换行符就会冲洗缓冲区,但是重定向至文件之后,IO就变成全缓冲的了,那么之后整个程序结束才会冲洗缓冲区。(当然了write是系统调用不会经过这种缓冲区的”优化“,无论何时都会立马从缓冲区中冲洗出来)。但是在这里又牵扯出一个更加深层次的问题,就是父进程的输出为什么没有和子进程的输出混淆那?这是因为文件描述符表只有在两个进程中都是一样的,但是文件指针都指向同一个文件表,这样子进程更新文件偏移量的时候父进程也会共享到这个更新。所以父进程不会覆盖子进程的输出。父进程不仅文件描述符表被子进程继承,同时还有很多属性也可以被继承。(具体见书上P186)

exit函数:

进程有五种正常的终止方式:(1)在mian函数中执行return函数,这个是return函数将返回值传递给exit函数。(等效调用exit函数)(2)在执行流当中执行exit函数,这个时候会执行用户注册的退出函数,再执行标准IO清理函数,最后再将执行权交给_exit或者_Exit函数。(3)直接执行_exit函数或者_Exit函数。(4)最后一个线程在其启动例程当中执行return函数.(5)最后一个线程执行pthread_exit函数。

进程同时有三种异常终止的情况:(1)调用abort函数。(2)由于接收到信号,而进程对此信号采取的措施是终止程序。

(3)对最后一个线程采取取消操作。

无论那种情况进程终止,其父进程都可以取得其状态信息。如果父进程没有对已经终止的进程采取善后处理,那么这个进程就被称之为僵尸进程。这里也有问题,如果父进程在子进程终止之前终止,那么怎么办那?这个时候操作系统就会将该进程的父进程设置为1号进程(一旦一个进程要终止,操作系统就把它的子进程交给init进程管理),同时这里也需要注意,init进程永远不会对僵尸进程坐视不管。(但是遗憾的是,我自己做了实验情况貌似不是这么简单,当父进程结束之后,子进程交给了1125,我是ubuntu14.04,这个进程叫做init --user,可以理解为init进程的副本吧)

wait和waitpid:

wait和waitpid函数都是父进程等待子进程使用的函数。wait函数在任意一个子进程返回之前,父进程都会将处于阻塞张太,当任意一个子进程终止之后该函数返回子进程的PID,如果想要判断进程是否是想要等待的子进程只能在返回值之后判断。但是waitpid就十分的灵活可以等待任意一个子进程,同时可以设置参数让其不阻塞,一旦没有特定的进程终止立即返回。书上给出了waitpid的使用方式。

/*************************************************************************    > File Name: waitpid_t.c    > Author: jeff zhu    > Mail: 908190355@qq.com     > Created Time: 2016年10月21日 星期五 23时54分16秒 ************************************************************************/#include "apue.h"#include <sys/wait.h>int main () {    pid_t pid;    if ((pid = fork ()) < 0)//产生一个子进程。        err_sys ("fork error _1");    else if (pid == 0) {         if ((pid = fork ()) < 0) //子进程再产生一个子进程。            err_sys ("fork error _2");        else if (pid > 0) //产生子进程的子进程退出。            exit (0);        sleep (2);//子进程的子进程睡眠2S。        printf ("second child , parent pid = %ld\n" , (long)getppid ());        exit (0);    }    if (waitpid (pid , NULL , 0) != pid) //父进程可以等到子进程的结束。所以下面这句话不会打印出来。        err_sys ("waitpid error");    exit (0);}
我加上了注释。就不再过多解释了。但是我们如果稍微改变一下代码那。

/*************************************************************************    > File Name: waitpid_t.c    > Author: jeff zhu    > Mail: 908190355@qq.com     > Created Time: 2016年10月21日 星期五 23时54分16秒 ************************************************************************/#include "apue.h"#include <sys/wait.h>int main () {    pid_t pid;    if ((pid = fork ()) < 0)        err_sys ("fork error _1");    else if (pid == 0) {        if ((pid = fork ()) < 0)            err_sys ("fork error _2");        else if (pid > 0) {            sleep (5);            exit (0);        }        sleep (2);        printf ("second child , parent pid = %ld\n" , (long)getppid ());        exit (0);    }    if (waitpid (pid , NULL , WNOHANG) == 0) //执行此函数的时候,它所等待的子进程正在睡眠,所以立马返回0        printf ("we can't get the first child\n");    exit (0);}
这个时候由于父进程执行waitpid使用了非阻塞的参数,所以由于第一个子进程睡眠,所以此函数立马返回0,
无法捕捉到第一个子进程。

竞争条件:

这个问题就是除了IO之外这本书第二个核心。一旦一个程序依赖于父子进程哪一个谁先运行,那么就存在竞争条件了。这个时候就需要采取手段将这种情况抑制。这里就不在多说,因为这是这本书之后最主要的内容。

exec函数:

exec函数是一个类的函数,它有很多类型,fork这个函数用来产生一个进程,exec函数就用来将磁盘上的一个程序替代当前进程的堆段,栈段,数据段,正文段。通常和fork连同一起使用。

 


0 0
原创粉丝点击