一、概念
fork函数调用一次,返回两次。父进程返回子进程的pid,子进程返回0,失败则返回-1。
函数头文件:#include <unistd.h>
函数原型:pid_t fork(void);其中pid_t是一个宏定义:#define pid_t int
二、写时拷贝技术
传统的fork()系统调用直接把所有的资源复制给新创建的进程。但是写时拷贝技术会好很多,fork函数在生成子进程时,用到了写时拷贝技术:父子进程共享数据段、堆、栈。内核将共享区域的访问权限修改为只读的。当父子进程任意一个进程试图修改数据时,就会将要修改的数据所在的“页”拷贝一份。
父子进程并不共享全局数据、堆栈数据。(但是代码段继续共享父进程的)
fork时,子进程的PCB是由父进程拷贝(浅拷贝)而来的,父子进程共享fork之前打开的文件。
详细内容:https://blog.youkuaiyun.com/zztingfeng/article/details/83351718
三、fork和vfork的区别
fork函数生成的子进程会写时复制的技术对父进程的数据进行复制。父子进程调度根据操作系统的调度来确定。
vfork函数生成的子进程与父进程共享数据。父进程在子进程结束或是调用exec后才会执行。
四、僵死进程
1.父进程未结束,子进程结束。父进程未获取子进程的退出数据。
2.一个进程的主体释放,pcb没有释放。
由于因为在unix系统中提供了一种保护机制:每个进程在退出(一般是调用exit、运行时发生致命错误或收到终止信号所导致)的时候内核释放该进程所有的资源,包括打开的文件,占用的内存等. 但是仍然为其保留一定的信息(包括进程号,退出状态,运行时间等), 直到父进程通过wait / waitpid来取时才释放。
解决方法:
方法一:wait/waitpid函数:pid_t wait(int *status)
wait函数就是用来应对这种情况的,父进程在调用wait函数之后就可以将自己阻塞,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。
其中的参数status用来保存被收集进程退出时的一些状态,它是一个指向int类型的指针。但如果我们对这个子进程是如何死掉的毫不在意,只想把这个僵尸进程消灭掉,(事实上绝大多数情况下,我们都会这样想),我们就可以设定这个参数为NULL,就象下面这样:
pid = wait(NULL);
缺点也很明显就是,wait函数阻塞着只为消灭僵死进程。
方法二:将父进程中对SIGCHLD信号的处理函数设为SIG_IGN(忽略)
这里使用到了信号,需要信号的头文件:signal.h.
同样,只需要在父进程块中加入一行代码:signal(SIGCHLD,SIG_IGN);就可以不产生僵死进程了。调用这个signal函数就定义了父进程对子进程结束后返回的SIGCHLD信号的响应方式:忽略。
这种方式可以保持异步,即处理僵死进程的同时,父进程还可以继续运行不受影响。
————————————————————————华丽丽的分割线—————————————————————————
小柒自语:就算在很丧的日子里,也要让自己努力开心,因为,你是一个人。一个人在战斗着。^^