目录
一、初识fork函数
二、fork函数的返回值
三、fork之后,父子进程谁先运行
四、fork的使用示例
一、初识fork函数
在linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
进程调用fork,当控制转移到内核中的fork代码后,内核会做以下的事情:
(1)分配新的内存块和内核数据结(pcb)构给子进程
(2)将父进程部分数据结构(pcb)内容拷贝至子进程
(3)添加子进程到系统进程列表(运行队列,阻塞队列等)当中
(4)fork返回,开始调度器调度
当一个进程调用fork之后,就有两个二进制代码相同的进程。而且它们都运行到相同的地方。但每个进程都将可以开始它们自己的旅程,看如下程序。
int main( void )
{
pid_t pid;
printf("Before: pid is %d\n", getpid());
if ( (pid=fork()) == -1 )perror("fork()"),exit(1);
printf("After:pid is %d, fork return %d\n", getpid(), pid);
sleep(1);
return 0;
}
运行结果:
[root@localhost linux]# ./a.out
Before: pid is 43676
After:pid is 43676, fork return 43677
After:pid is 43677, fork return 0
这里看到了三行输出,一行before,两行after。进程43676先打印before消息,然后它有打印after。另一个after消息有43677打印的。注意到进程43677没有打印before,为什么呢?如下图所示:
所以,fork之前父进程独立执行,fork之后,(每一个进程的pcb中都会保存一个指针,他指向这个程序运行到了哪一步,即父子进程的指针此时都在fork后面)父子两个执行流分别执行。注意,fork之后,谁先执行完全由调度器决定。
二、fork函数的返回值
(1) 子进程返回0,
(2)父进程返回的是子进程的pid。
为什么一个fork函数会两个不同的返回值,因为return也是代码,父子进程都需要返回一个值。这里我们说原本父子进程的代码和数据是共用的,只有pcb是独立的,但是由于父子进程现在都需要返回一个值,所以会采取写时拷贝的策略,让子进程中也有一份单独的pid变量。写时拷贝是在子进程需要修改变量的时候,才会给子进程单独创建一份,如果子进程不修改,则和父进程使用同一份代码和数据,由此提高了效率。
写时拷贝概念图:
三、fork之后,父子进程谁先运行
我们所有的体系结构都是依赖的冯诺依曼结构,父子进程的存在正是因为他们有了不同的pcb,其中保存了各自的信息(如pid,ppid,队列指针等),而每一个进程在运行的时候,其实都会被调度到运行队列中,即队列指针链入到了运行队列里面,在等待某些资源的时候,也会被链入该资源的阻塞队列中,所以进程的状态是在不断变化的。
父子进程谁先运行,其实是操作系统先把谁链入到运行队列中,这个是不确定的行为。
四、fork的使用示例
fork会给父进程返回子进程的pid,给子进程返回0,所以我们用这个就能区分父子进程,从而使用if else语句来让父子进程执行不同的代码。