图片展示网站建设,wordpress+分享后下载,网站关键词 查询,网站开发招聘要求一、fork入门知识 fork#xff08;#xff09;函数通过系统调用创建一个与原来进程几乎完全相同的进程#xff0c;也就是两个进程可以做完全相同的事#xff0c;但如果初始参数或者传入的变量不同#xff0c;两个进程也可以做不同的事。可以简单地说fork()的作用就是创建一…一、fork入门知识 fork函数通过系统调用创建一个与原来进程几乎完全相同的进程也就是两个进程可以做完全相同的事但如果初始参数或者传入的变量不同两个进程也可以做不同的事。可以简单地说fork()的作用就是创建一个子进程。 一个进程调用fork函数后系统先给新的进程分配资源例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中只有少数值与原来的进程的值不同。相当于克隆了一个自己。 什么是进程 1进程是动态的程序是静态的 当一个程序调用的时候就创建了一个进程进程在运行的时候是具有独立性的不影响其他进程 2进程内核数据结构进程的代码和数据 代码只读 数据当有一个执行流尝试修改数据的时候OS会自动给我们当前进程触发写时拷贝 写时拷贝copy-on-write COW就是等到修改数据时才真正分配内存空间这是对程序性能的优化可以延迟甚至是避免内存拷贝当然目的就是避免不必要的内存拷贝。 这里引入一个概念叫做PCB进程控制块。也就是linux自己创建的Task_struct结构体。进程控制块是系统为了管理进程设置的一个专门的数据结构用它来记录进程的外部特征描述进程的运动变化过程。
子进程完全拷贝父进程的PCB但并不是同一个父子进程代码共享数据独有同一个变量在父子进程 的地址完全一样OS中虚拟内存机制保证父子进程运行独立互不干扰
创建一个子进程实际上是创建了一个PCB父进程与子进程看到的是同一份代码和数据
创建新进程成功后系统中出现两个基本完全相同的进程这两个进程执行没有固定的先后顺序哪个进程先执行要看系统的进程调度策略。
fork调用的一个奇妙之处就是它仅仅被调用一次却能够返回两次它可能有三种不同的返回值
1在父进程中fork返回新创建子进程的进程ID 2在子进程中fork返回0 3如果出现错误fork返回一个负值
fork出错可能有两种原因
1当前的进程数已经达到了系统规定的上限这时errno的值被设置为EAGAIN。 2系统内存不足这时errno的值被设置为ENOMEM。 二、fork进阶知识
以下面的代码为例使用unistd.h头文件调用fork()函数。其中getpid()函数的作用是获取进程idgetppid()函数的作用是获取父进程id
#includestdio.h
#includeunistd.h
#includesys/types.hint main()
{printf(AAAAA\n);fork();printf(BBBBBBBBB pid:%d ppid:%d\n,getpid(),getppid()); sleep(1);return 0;
}运行后的结果是 可以看出AAAAA只打印了一行因为此时并没有执行fork()函数只有初始执行的代码。之后通过fork()函数创建了一个子进程因此BBBBBBBB打印了两次。
第一次打印B的时候pid25251 ppid25728。而第二次打印B的时候pid28252 ppid25251父进程的进程id与第一次打印的进程id相同。
因此可以说明fork()函数创建了一个id为28252的子进程它的父进程id为28251
下面来考虑循环中使用fork()的效果
#include unistd.h
#include stdio.hint main(void)
{int i 0;printf(i son/pa ppid pid fpid/n);//ppid指当前进程的父进程pid//pid指当前进程的pid,//fpid指fork返回给当前进程的值for(i 0; i 2; i){pid_t fpid fork();if(fpid 0)printf(%d child %4d %4d %4d/n, i, getppid(), getpid(), fpid);elseprintf(%d parent %4d %4d %4d/n, i, getppid(), getpid(), fpid);}return 0;
} 运行结果是
i son/pa ppid pid fpid
0 parent 2043 3224 3225
0 child 3224 3225 0
1 parent 2043 3224 3226
1 parent 3224 3225 3227
1 child 1 3227 0
1 child 1 3226 0
第一步在父进程中指令执行到for循环中i0接着执行forkfork执行完后系统中出现两个进程分别是p3224和p3225后面我都用pxxxx表示进程id为xxxx的进程。可以看到父进程p3224的父进程是p2043子进程p3225的父进程正好是p3224。我们用一个链表来表示这个关系
p2043-p3224-p3225
第一次fork后p3224父进程的变量为i0fpid3225fork函数在父进程中返向子进程id
p3225子进程的变量为i0fpid0fork函数在子进程中返回0
第二步假设父进程p3224先执行当进入下一个循环时i1接着执行fork系统中又新增一个进程p3226
对于此时的父进程p2043-p3224当前进程-p3226被创建的子进程
对于子进程p3225执行完第一次循环后i1接着执行fork系统中新增一个进程p3227
p3224-p3225当前进程-p3227被创建的子进程
从输出可以看到p3225原来是p3224的子进程现在变成p3227的父进程。父子是相对的
第三步第二步创建了两个进程p3226p3227这两个进程执行完printf函数后就结束了因为这两个进程无法进入第三次循环无法fork该执行return 0;了其他进程也是如此。
细心的读者可能注意到p3226p3227的父进程难道不该是p3224和p3225吗怎么会是1呢这里得讲到进程的创建和死亡的过程在p3224和p3225执行完第二个循环后main函数就该退出了也即进程该死亡了因为它已经做完所有事情了。p3224和p3225死亡后p3226p3227就没有父进程了这在操作系统是不被允许的所以p3226p3227的父进程就被置为p1了p1是永远不会死亡的 对于这种N次循环的情况执行printf函数的次数为2*124……2N-1次创建的子进程数为124……2N-1个。 printf的缓冲机制printf某些内容时操作系统仅仅是把该内容放到了stdout的缓冲队列里了,并没有实际的写到屏幕上。
但是,只要看到有/n 则会立即刷新stdout,因此就马上能够打印了