Linux C语言 进程详解——fork()/wait()/waitpid()
fork()函数#include<unistd.h>#include<sys/types.h>函数原型pid_t fork( void);(pid_t 是一个宏定义,其实质是int 被定义在#includesys/types.h>中)返回值:若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1fork系统调用用于创建一个新进程,称为子进程
·
一、fork()函数
1. 头文件
#include<unistd.h>
#include<sys/types.h>
2. 函数原型及功能介绍
函数原型
pid_t fork( void);
(pid_t 是一个宏定义,其实质是int 被定义在#includesys/types.h>中)
返回值:若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1
fork系统调用用于创建一个新进程,称为子进程,它与父进程 同时运行(并发),且运行顺序不定(异步)。
fork()函数如果成功调用一次返回两个值,一共可能有三种不同的返回值:(1)在父进程中,fork返回新创建子进程的进程ID;
(2)在子进程中,fork返回0;
(3)如果出现错误,fork返回一个负值。
3.1 测试代码
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
pid_t child;
child = fork();
if(child == 0)//子进程抢到处理机资源并运行
{//子进程运行代码
for(int i = 0; i < 3; i++)
{
printf("I am child!\n");
sleep(1);
}
}
else if(child > 0)//父进程抢到处理机资源并运行
{//父进程运行代码
for(int i = 0; i < 3; i++)
{
printf("I am parent!\n");
sleep(1);
}
}
return 0;
}
3.2 测试结果
父进程与子进程并发运行,且运行顺序不一定(可以调整循环次数观察结果)
4.1 创建多个子进程(注意事项)
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
pid_t child1, child2;
child1 = fork(); //执行到这段代码时,子进程同时开始和父进程一起向下执行
child2 = fork(); //该段代码被父进程和子进程(child1)同时执行,即子进程多创建了一个进程
if(child1 == 0)
{//因为子进程(child1)会还会创建一个子进程(孙进程), 故这段代码会被执行两次
printf("I am child1!\n");
}
else if(child1 > 0)
{
if(child2 == 0)
{
printf("I am child2!\n");
}
else if(child2 > 0)
{
printf("I am parent!\n");
}
}
return 0;
}
4.2 测试结果:
因为子进程(child1)下已经创建了一个子进程(孙进程),当子进程(child1)抢到处理机资源后
从父进程(child1)和子进程(孙进程)的角度来推导,父进程(child1)就会和它的子进程同时执行进程(child1)的代码段
4.3 解决方案:
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
int i, n;
pid_t child;
printf("请输入需要创建的子进程个数:");
scanf("%d", &n);
for(i = 0; i < n; i++)
{
child = fork();
if(child == 0) //如果子进程抢到处理机后就退出循环,不然子进程的循环下还会i个创建进程
break; //若不退出,最终加上父进程一共有2^n个进程
}
//循环创建完子进程后,(i+1)值对应着每个子进程
if(i < n)
{
printf("I am %d child!(pid = %d)\n", i+1, getpid());
}
else
{
printf("I am parent!\n");
}
return 0;
}

二、wait()和waitpid()函数
1. 头文件
#include<sys/types.h>
#include<sys/wait.h>
2. 函数原型及功能介绍
函数原型:pid_t wait(int *status);
返回值:
成功:返回结束的子进程pid,终止回收子进程,
失败:返回-1(没有子进程)失败原因存于errno 中
参数:
(1)wait()会暂时停止目前进程的执行, 即阻塞父进程,等待子进程结束或者其他信号.
(2)如果在调用wait()时子进程已经结束, 则wait()会立即返回子进程结束状态值.
(3)子进程的结束状态值会由参数status返回, 而子进程的进程识别码也会一并返回.
(4)如果不考虑结束状态值, 则参数 status 可以设成NULL.
函数原型:pid_t waitpid(pid_t pid, int *status, int options);
返回值:
正常:返回已成功结束运行的子进程的进程号
使用选项WNOHANG且没有子进程退出:0
失败:-1
参数
(1)pid:
pid > 0|只等待进程ID等于pid的子进程,不管其他子进程是否结束,只要指定子进程未结束,waitpid()就会一直等下去
pid =-1|等待任何一个子进程退出,此时和wait()作用一样
pid = 0|等待其组ID等于调用进程的组ID的任一子进程
pid <-1|等待其组ID等于pid的绝对值的任一子程序
(2)options:
WNOHANG:若pid指定的子进程未结束,则waitpid()不阻塞父进程,立即返回0;
WUNTRACED:若pid指定进程已被暂停,且其状态自暂停以来还未报告过,则返回其状态
0:同wait(),阻塞父进程,等待子进程退出
(3)stasus: 同wait
3.1 测试代码:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
int status;
pid_t child1, child2, parent;
printf("parent(pid = %d, ppid = %d\n", getpid(), getppid());
if((child1 = fork()) == 0)//子进程child1抢到处理机资源并运行
{//子进程child1运行代码
for(int i = 1; i <= 10; i++)
{
printf("child1(进度 = %d%) is working!(pid = %d,ppid = %d)\n",i * 10, getpid(), getppid());
sleep(1);
}
printf("Child1 had completed!\n");
}
else if(child1 > 0)//返回到父进程时还需要判断子进程child2是否抢到资源
{
if((child2 = fork()) == 0)//子进程child2抢到处理机资源并运行
{//子进程child2运行代码
for(int i = 1; i <= 10; i++)
{
printf("child2(进度 = %d%) is working!(pid = %d,ppid = %d)\n",i * 10, getpid(), getppid());
sleep(1);
}
printf("Child2 had completed!\n");
}
else if(child2 > 0)//父进程parent抢到处理机资源并运行
{
printf("The last child's pid = %d\n", wait(&status));
for(int i = 1; i <= 3; i++)
{
printf("parent(进度 = %d%) is working(pid = %d ppid = %d)\n", i * 10, getpid(), getppid());
sleep(1);
}
printf("Parent had Complete!\n");
}
else if(child2 == -1)
{
printf("Error!\n");
}
}
else
{
printf("Error!\n");
}
return 0;
}
3.2 测试结果:
如下运行结果可以证明进程的并发性和异步性,进程之间并发运行,且运行的顺序不定
wait()会阻塞父进程,等待子进程全部结束后,父进程才开始运行
4.1 测试代码:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
int status;
pid_t child1, child2, parent;
printf("parent(pid = %d, ppid = %d\n", getpid(), getppid());
if((child1 = fork()) == 0)//子进程child1先抢到处理机资源并运行
{//子进程child1运行代码
for(int i = 1; i <= 10; i++)
{
printf("child1(进度 = %d%) is working!(pid = %d,ppid = %d)\n",i * 10, getpid(), getppid());
sleep(1);
}
printf("Child1 had completed!\n");
}
else if(child1 > 0)//子进程没有抢到处理机资源,还需要判断子进程child2是否抢到
{
if((child2 = fork()) == 0)//子进程child2抢到处理机资源并运行
{//子进程child2运行代码
for(int i = 1; i <= 10; i++)
{
printf("child2(进度 = %d%) is working!(pid = %d,ppid = %d)\n",i * 10, getpid(), getppid());
sleep(1);
}
printf("Child2 had completed!\n");
}
else if(child2 > 0)//父进程parent抢到处理机资源并运行
{
//父进程不阻塞,与子进程并发
waitpid(child1, NULL, WNOHANG);
waitpid(child2, NULL, WNOHANG);
for(int i = 1; i <= 3; i++)//父进程parent运行到30%时就提前退出
{
printf("parent(进度 = %d%) is working\n", i * 10);
sleep(1);
}
printf("Parent had Complete!\n");
}
else if(child2 == -1)
{
printf("Error!\n");
}
}
else
{
printf("Error!\n");
}
return 0;
}
4.2 运行结果:
waitpid()不会阻塞父进程,父进程与子进程共同竞争资源,父进程完成后提前退出
更多推荐






所有评论(0)