moc 发表于 2018-11-5 20:37:38

010-fork、exit

本帖最后由 moc 于 2018-11-5 20:39 编辑

1、fork系统调用
1. 进程原语 fork
        头文件:              <sys/types.h> 、 <unistd.h>
        函数功能:        创建一个子进程
        函数原型:        pid_tfork(void);
        参数:                无参数
        返回值:                如果成功创建一个子进程,对于父进程来说返回子进程ID, 对于子进程来说返回值为0;如果为-1表示创建失败。
2. 复制一个进程映象fork
使用fork函数得到的子进程从父进程的继承了整个进程的地址空间,包括:进程上下文、进程堆栈、内存信息、打开的文件描述符、信号控制设置、进程优先级、进程组号、当前工作目录、根目录、资源限制、控制终端等。
子进程与父进程的区别在于:
        1. 父进程设置的锁,子进程不继承
        2. 各自的进程ID和父进程ID不同
        3. 子进程的未决告警被清除;
        4. 子进程的未决信号集设置为空集。
3. 写时复制,读时共享copy on write, read on share
        如果多个进程要读取它们自己的那部分资源的副本,那么复制是不必要的。
        每个进程只要保存一个指向这个资源的指针就可以了。
        如果一个进程要修改自己的那份资源的“副本”,那么就会复制那份资源。这就是写时复制的含义.
4. 注意点:
        理解1:fork系统调用之后,父子进程将同时执行。
        理解2:理解一次调用2次返回 => 两次返回,是在各自的进程空间中返回的。
        理解3:理解,fork返回值大于零的是父进程,父进程:子进程 = 1:n。
        理解4:理解分支在fork之后,父进程不是main函数的开始,而是紧接着向下执行,子进程从父进程的继承了整个进程的地址空间及数据。
示例:#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <errno.h>

int main(void )
{
        pid_t pid;
        signal(SIGCHLD, SIG_IGN);
        printf("befor fork pid:%d \n", getpid());
        int abc = 10;
        pid = fork();   //errno
        if (pid == -1)       
        {
                //printf("pid < 0 err.\n");
                perror("tile");
                return -1;
        }
        if (pid > 0)
        {
                abc ++;
                printf("parent: pid:%d \n", getpid());
                printf("abc: %d \n", abc);
                sleep(20);
        }
        else if (pid == 0)
        {
                abc ++;
                printf("child: %d, parent: %d \n", getpid(), getppid());
                printf("abc: %d \n", abc);
                //sleep(10);
        }
        printf("fork after....\n");
        return 0;
}2、孤儿进程和僵尸进程
孤儿进程:
        如果父进程先退出,子进程还没退出那么子进程的父进程将变为init进程(1号进程)。(注:任何一个进程都必须有父进程)
        父亲进程先结束,子进程(孤儿进程)会托孤给1号进程。
僵尸进程:
        如果子进程先退出,父进程还没退出,那么子进程必须等到父进程捕获到了子进程的退出状态才真正结束,否则这个时候子进程就成为僵死进程。
避免僵尸进程
        父进程忽略子进程的退出消息,交给操作系统管理。
        #include <signal.h>==>   signal(SIGCHLD, SIG_IGN);
3、fork之后父子进程共享文件
如果父进程中打开了一个文件,那么子进程不需要再打开;子进程可以使用fork之前open返回的文件描述符。因为调用fork之后,只拷贝了PCB本身,拷贝的只是指针,没有拷贝指针所指向的内容,这种情况叫做浅拷贝。子进程的指针依旧指向struct file,所以父子进程对于文件描述符和文件偏移量是共享的。

4、fork和vfork
1)在fork还没实现copy on write之前。Unix设计者很关心fork之后立刻执行exec所造成的地址空间浪费,所以引入了vfork系统调用。
2)vfork有个限制,子进程必须立刻执行_exit或者exec函数。
即使fork实现了copy on write,效率也没有vfork高,但是我们不推荐使用vfork,因为几乎每一个vfork的实现,都或多或少存在一定的问题。
vfork现在已不推荐使用。
5、进程终止的5种方式
正常退出:
        ① 从main函数返回
        ② 调用exit
        ③ 调用_exit
异常退出:
        ④ 调用abort   产生SIGABOUT信号
        ⑤ 由信号终止ctrl+c   SIGINT等
1. exit与_exit区别
① _exit是一个系统调用,exit是一个c库函数;
② exit会执行清除I/O缓存;
③ exit会执行调用终止处理程序。
int main(void)
{
        printf("hello itcast");
        //return 0;
        //exit(0);
        //fflush(stdout);
        _exit(0);   // 不会清除缓存,不会调用进程终止函数,所以没有打印结果
}注册终止函数:
头文件: #include <stdio.h>
原型: int atexit(void (*func)(void))
作用: 在main函数return时或exit退出时,会自动调用。
页: [1]
查看完整版本: 010-fork、exit