Linux进程控制:fork、exec、wait/waitpid

1、fork创建子进程

fork函数比较特殊,“调用一次返回两次”,调用它可以创建出一个子进程。可以看下例子:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	pid_t pid;
	char *message;
	int n;
	pid = fork();
	if (pid < 0)
	{
		perror("fork failed");
		exit(1);
	}
	if (pid == 0) 
	{
		message = "This is the child\n";
		n = 6;
	}
	else 
	{
		message = "This is the parent\n";
		n = 3;
	}
	for(; n > 0; n--) 
	{
		printf(message);
		sleep(1);
	}
	return 0;
}

它的执行流程如图所示:
在这里插入图片描述输出结果:

This is the parent
This is the child
This is the parent
This is the child
This is the parent
This is the child
book@book:/work/test/myprocess/forktest$ This is the child
This is the child
This is the child

book@book:/work/test/myprocess/forktest$

之所以出现第7行这种现象,是因为此时父进程已经运行结束了,返回控制台了,而子进程还没有结束,继续打印输出,所以就出现这种现象,当然这种情况也不一定发生,它取决于系统的进程调度与竞争。


2、exec族函数

当进程调用一种 exec 函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用 exec 并不创建新进程,所以调用 exec 前后该进程的id并未改变。

int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);

它包含一系列的函数,不过也很容易区分:

p(path)必须是程序绝对路径;
l(list)每个命令行参数都当作参数传给它;
v(vector)先构造指向个参数的指针数组,以首地址发给它;
e(environment)把新的环境变量传递给它;

例如:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

int main(void)
{
	// error usage
	// execlp("ls", "-l", "-a", "-h", NULL);	
	
	// true usage
	//       file	cmd	   params	 end	
	//execlp("ls", "ls", "-l", "-h", NULL);
	// 		  path	   cmd     params	 end
	//execl("/bin/ls", "ls", "-l", "-h", NULL);

	char *argv[] = {"ls", "-l", "-h", NULL};
	// 	   file  vector
	execvp("ls", argv);

	// Usually not here
	perror("exec ps");	
	exit(1);
}

输出结果:

total 16K
-rwxrwxr-x 1 book book 8.5K 424 21:02 exec
-rw-rw-r-- 1 book book  416 424 21:02 exec.c

3、wait和waitpid函数回收子进程资源

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);

参数说明

pid取值说明
< -1任何进程组ID等于pid的绝对值的子进程。
-1任何子进程
0进程组ID与调用进程的ID相等的任何子进程
>0对应pid的子进程
status对应的宏说明
WIFEXITED如果子项正常终止则返回true
WEXITSTATUS返回子项的退出状态
WIFSIGNALED如果子进程被信号终止,则返回true
WTERMSIG返回导致子进程终止的信号编号
WCOREDUMP如果子项产生了核心转储(段错误)则返回true
WIFSTOPPED如果子进程由于传递信号而停止,则返回true
WSTOPSIG返回导致子进程停止的信号编号
WIFCONTINUED如果子进程通过SIGCONT的交付而恢复,则返回true。
options取值说明(重点关注 0 和 WNOHANG )
0阻塞等待
WNOHANG如果没有子进程退出立即返回(不阻塞)
WUNTRACED如果子进程已停止也会返回。即使未指定此选项,也会提供已停止的跟踪子项的状态。
WCONTINUED如果已通过交付SIGCONT恢复了被阻止的孩子,也将返回。

返回值说明

函数返回值
wait成功返回pid,失败返回-1
waitpid成功返回pid,失败返回-1,非阻塞未回收返回0

wait/waitpid函数一次只能回收一个进程的资源,需要回收多个可以使用循环来进行处理。

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(void)
{
	pid_t pid, wpid;
	int status;

	pid = fork();
	if (pid < 0)
	{
		perror("fork failed");
		exit(1);
	}
	else if(pid == 0)
	{
		// 子进程
		printf("child: pid is %d\n", getpid());
		sleep(2);
		printf("child: process end!\n");
		return 100;
	}
	else if(pid > 0)
	{
		// 父进程
		// wait函数回收任意一个,不指定子进程
		// 等价于 waitpid(-1, &status, 0);
		wpid = wait(&status);
		if(wpid == -1)
		{
			perror("parents: wait error");
			exit(1);
		}
		
		// 可以打开man手册使用一系列的宏查看status对应的子进程状态
		if(WIFEXITED(status))
		{
			printf("parents: child pid is %d, and exit with value: %d\n", wpid, WEXITSTATUS(status));
		}
	}
	return 0;
}

输出结果:

child: pid is 2938
child: process end!
parents: child pid is 2938, and exit with value: 100

另外可以了解一下进程的一些状态:
  孤儿进程是指父进程已经运行结束,而子进程仍然在运行,此时子进程会由init进程接管;
  僵尸进程是指子进程运行结束,等待父进程回收时的状态。需要注意的是,任何进程终止时都有一段短暂的时间为僵尸进程,等待父进程彻底清除,此时不能用kill清除,因为kill是终止进程而不是清除进程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

R-QWERT

你的鼓励是我最大的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值