目录
函数名
fork:创建一个子进程
函数原型
pid_t fork(void);
调用该函数时,需包含以下头文件
#include <unistd.h>
返回值
fork函数调用成功,返回两次PID
(1)返回值为0,代表当前进程是子进程
(2)返回值为非负数,代表当前进程是父进程
(3)调用失败,则返回-1
代码展示
1.fork函数直接调用
简单输出父进程和子进程的值
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;//定义一个pid
pid = getpid();//调用函数获取进程的值
fork();
printf("pid = %d,current pid = %d\n",pid,getpid());
return 0;
}
pid是原先进程的值,而getpid()输出的是当前进程的值.
fork会打印两次结果,一次是父进程,一次是子进程。
这里pid是父进程,所以第一行打印的是父进程,其结果原先pid和当前pid的值一样;而第二行打印的是子进程,pid的值还是原先进程的值(即父进程的值),而当前的pid值则不一样。
fork函数前后值的变化
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;
pid_t pid2;
pid = getpid();
printf("before fork pid is %d\n",pid);
fork();
pid2 = getpid();
printf("after fork ppid is %d\n",pid2);
if(pid = pid2)//fork函数后会输出父进程和子进程,将后来的进程值与一开始的父进程的值进行判断
{
printf("This is father pid,pid is %d\n",pid2);//相等为父进程
}
else
{
printf("This is child pid,pid is %d\n",pid2);//不等为子进程
}
return 0;
}
在fork函数之前,只有一个父进程pid,由结果可知打印了一次父进程。
在fork函数之后,函数分别打印了父进程和子进程的值。可见二三行的值是父进程的值,四五行是子进程的值。
2.fork返回值调用
可以用于判断当前进程是父进程还是子进程
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;
printf("father pid is %d\n",getpid());
pid = fork();
if(pid > 0)
{
printf("This is father pid,pid is %d\n",getpid());
}
else if(pid == 0)
{
printf("This is child pid,pid is %d\n",getpid());
}
return 0;
}
一开始先将当前的进程值(父进程)打印出来。通过if判断fork函数的返回值:返回值为非负整数,则将其当前的进程值输出,为父进程;返回值为0,则将其当前的进程值输出,为子进程。可见意儿行的pid值相同,而一三行的pid值不同。
由fork创建的新进程被称为子进程 (child process)。fork函数被调用一次,但返回两次。两次返回的唯一区别是子进程的返回值是0,而父进程的返回值则是新子进程的进程PID。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;
pid_t pid2;
pid_t retpid;
pid = getpid();
printf("before fork pid is %d\n",pid);
retpid = fork();
pid2 = getpid();
printf("after fork pid is %d\n",pid2);
if(pid == pid2)
{
printf("This is father pid,retpid is %d\n",retpid);//此时fork父进程的返回值是子进程的值
}
else
{
printf("This is child pid,retpid is %d,pid is %d\n",retpid,pid2);//此时fork子进程的返回值为0,而自身pid不为0。说明父进程调用fork后将返回值赋给子进程自身,使得fork返回值为0,而子进程有了自身的pid
}
return 0;
}
子进程拷贝了父进程的内容是什么?
早期的Unix内核: 把父进程的所有内容拷贝给子进程 Unix系统,当调用fork函数时,内核原样复制父进程的整个地址空间并把复制的那一份分配给子进程。
现在的Unix内核(包括Linux) 子进程没有更改数据段,则共享数据段;子进程更改了数据段,才会在子进程地址里面拷贝一份数据段。 采用一种更为有效的方法称之为写时复制(或COW)。 这种思想相当简单:父进程和子进程共享页面而不是复制页面。
案例如下
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
int fork_t = 0;
int a = 10;
fork_t = fork();
if(fork_t != 0)
{
printf("This is father pid\n");//a在父进程中不会发生改变,打印的a的值还是为原来的值
}
else
{
printf("This is child pid\n");
a = a+100;//子进程中a的值发生改变,会打印改变后的值
}
printf("%d\n",a);
return 0;
}
fork创建子进程的目的
(1)一个父进程希望复制自己,使父、子进程同时执行不同的代码段。
这在网络服务进程中是常见的一一父进程等待客户端的服务请求。当这种请求到达时,父进程调用fork,使子进程处理此请求。父进程则继续等待下一个服务请求到达。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
int fork_t = 0;//定义一个fork的返回值,便于后续判断
int data;
int pid = 0;
while(1)
{
printf("please input a data\n");
scanf("%d",&data);
if(data == 1)
{
fork_t = fork();//创建子进程
if(fork_t > 0)//返回值大于0为父进程,则不做任何事
{
}
else if(fork_t == 0)//返回值等于0为子进程,执行下列代码
{
while(1)
{
pid = getpid();
printf("do some things,pid is %d\n",pid);
sleep(4);//延时4秒
}
}
}
else
{
printf("input error!\n");
}
}
return 0;
}
结果分析: 输入1以外的值,无效
输入1后第一个红框:每隔4秒打印一次子进程的pid;第二个红框:输入值是父进程,而打印是子进程,父进程和子进程能同时工作;第三个红框:多次输入1会创建多个子线程,打印子线程pid时会有多个值
(2)一个进程要执行一个不同的程序。
这对shell是常见的情况。在这种情况下,子进程从fork返回后立即调用exec。