转载:https://blog.csdn.net/qq_28840229/article/details/79844763
fork之后:
父子相同处: 全局变量、.data、.text、栈、堆、环境变量、用户ID、宿主目录、进程工作目录、信号处理方式...
父子不同处: 1.进程ID 2.fork返回值 3.父进程ID 4.进程运行时间 5.闹钟(定时器) 6.未决信号集
似乎,子进程复制了父进程0-3G用户空间内容,以及父进程的PCB,但pid不同。真的每fork一个子进程都要将父进程的0-3G地址空间完全拷贝一份,然后在映射至物理内存吗?
当然不是!父子进程间遵循读时共享写时复制的原则。这样设计,无论子进程执行父进程的逻辑还是执行自己的逻辑都能节省内存开销。
1、fork函数时调用一次,返回两次。在父进程和子进程中各调用一次。子进程中返回值为0,父进程中返回值为子进程的PID。程序员可以根据返回值的不同让父进程和子进程执行不同的代码。子进程是父进程的副本,获得了父进程数据空间、堆和栈的副本;父子进程并不共享这些存储空间,共享正文段(即代码段);因此子进程对变量的所做的改变并不会影响父进程。一般来说,fork之后父、子进程执行顺序是不确定的,这取决于内核调度算法。进程之间实现同步需要进行进程通信。
子进程对数据进行减一操作,父进程做加一操作:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid_t pid;
char *message;
int n = 0;
pid = fork();
while(1){
if(pid < 0){
perror("fork failed\n");
exit(1);
}
else if(pid == 0){
n--;
printf("child's n is:%d\n",n);
}
else{
n++;
printf("parent's n is:%d\n",n);
}
sleep(1);
}
exit(0);
}
2、进程等待
孤儿进程: 父进程生成子进程,但是父进程比子进程先结束;子进程会变成孤儿进程,由系统1号init 进程进行接管。init 进程接管后,在该孤儿进程结束的时候,负责“收尸”,回收系统资源及进程信息。
僵尸进程:子进程已经退出,但是没有父进程回收它的资源(父进程生成的子进程,但是子进程比父进程先挂掉,如果父进程没有收回它的资源时,那么子进程挂掉后就变成了僵尸进程;应该尽量避免产生僵尸进程,可以在父进程调用wait 或者waitpid 进程回收)。
进程一旦调用了wait,就立即阻塞自己,由wait 自动分析是否当前进程的某个子进程已经退出。如果让它找到了这样一个已经变成僵尸的子进程,wait 就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait 就会一直阻塞在这里,直到有一个出现为止。
补充:
进程0
内核是一个大的程序,可以控制硬件,也可以创建、运行、终止、控制所有的进程。当内核被加载到内存后,首先就会有完成内核初始化的函数start_kernel()从无到有的创建一个内核线程swap,并设置其PID为0,即进程0;它也叫闲逛进程;进程0执行的是cpu_idle()函数,该函数仅有一条hlt汇编指令,就是在系统闲置时用来降低电力的使用和减少热的产生。同时进程0的PCB叫做init_task,在很多链表中起了表头的作用。
当就绪队列中再没有其他进程时,闲逛进程就会被调度程序选中,以此来省电,减少热量的产生。
进程1
即init进程。首先内核线程kernel_init执行内核的一些初始化函数,以将内核初始化。那么此内核态的线程又是怎样变为一个用户进程的?实际上,kernel_int()内核函数中调用了execve()系统调用,该系统调用装入用户态下的一个可执行程序init,从而启动用户进程init进程。注意,内核函数kernel_init()与用户态下的可执行文件init是不同的,位置不同,运行状态不同,代码也不同。init进程只是内核线程kernel_init启动起来的一个普通的用户进程,当然也是用户态下的第一个进程,并且init进程从不终止,用来创建和监控操作系统外层的所有进程的活动。
3、父进程创建多个子进程问题
void createsubprocess(int num)
{
pid_t pid;
int i;
for(i=0;i<num;i++)
{
pid=fork();
if(pid==0||pid==-1) //子进程或创建进程失败均退出,这里是关键所在,子进程中跳出循环。
{
break;
}
}
if(pid==-1)
{
perror("fail to fork!\n");
exit(1);
}
else if(pid==0)
{
printf("子进程id=%d,其对应的父进程id=%d\n",getpid(),getppid());
exit(0);
}
else
{
printf("父进程id=%d\n",getpid());
exit(0);
}
}