Linux开发中操控进程(fork exit/_exit exec wait/wait_pid...)
拥有梦想是一种智力,实现梦想是一种能力。
概述
并发程序是应用开发中非常重要的一部分内容,如何实现程序的并发?包括多进程编程、进程间通信机制、多线程编程、线程间同步和异步机制等等。本次介绍多进程编程:
- fork创建进程
- exit/_exit结束进程
- exec函数族让进程执行指定程序
- wait/waitpid回收一个已经结束了的进程
- 如何创建一个守护进程
系统调用fork允许一个进程(父进程)创建一个新进程(子进程)。通过fork,子进程几乎是父进程的复制版本,子进程获得父进程的栈、数据段、堆和执行文本段的拷贝。通常,调用fork产生子进程后,子进程随便会调用execve函数簇执行新的任务,随后执行exit相关函数退出。而父进程则通常会调用wait函数等待子进程终止。此外,我会通过程序的方向来介绍,如何让一个子进程脱离它原有的终端成为一个守护进程运行。
fork()创建一个子进程
pid_t fork(void);
- 创建新的进程,失败时返回-1
- 成功时父进程返回子进程的进程号,子进程返回0
- 通过fork的返回值区分父进程和子进程
示例
-
#include <stdio.h>
-
#include <unistd.h>
-
#include <sys/types.h>
-
-
int main()
-
{
-
pid_t pid;
-
-
if ((pid = fork()) < 0) {
-
perror(“fork”);
-
return -1;
-
}
-
else if (pid == 0) {//子进程执行
-
printf(“child process : my pid is %d\n”, getpid());
-
}
-
else {//父进程执行
-
printf(“parent process : my pid is %d\n”, getpid());
-
}
-
-
return 0;
-
}
此时,子进程fork()函数之后,而不是程序的开始处进行。由于子进程继承了父进程的所有内容,所以两个进程的执行没有差分。
exit/_exit结束进程
void exit(int status);
void _exit(int status);
- 结束当前的进程并将status返回
exit与_exit的区别是,exit结束进程时会刷新(流)缓冲区。
示例
-
#include <stdio.h>
-
#include <stdlib.h>
-
-
int main(void)
-
{
-
printf("this process will exit");//
-
exit(0);
-
printf("never be displayed");
-
}
由于shell终端是行缓冲输出,printf输出的字符串又没有换行,所以 "this process will exit" 会驻留在缓冲区。exit()操作会刷新缓冲区,终端显示 "this process will exit"。
exec函数族让进程执行指定程序
#include <unistd.h>
int execl(const char *path, const char *arg, …);
int execlp(const char *file, const char *arg, …);
- 成功时执行指定的程序;失败时返回EOF
- path 执行的程序名称,包含路径
- arg… 传递给执行的程序的参数列表
- file 执行的程序的名称,在PATH中查找
进程调用exec函数族执行某个程序,进程当前内容被指定的程序替换。从而实现让父子进程执行不同的程序。
示例
-
#include <stdio.h>
-
#include <unistd.h>
-
-
int main()
-
{
-
if (execl(“/bin/ls”, “ls”, “-a”, “-l”, “/etc”, NULL) < 0)
-
{
-
perror(“execl”);
-
}
-
-
return 0;
-
}
问:如何让父子进程执行不同程序?
答:fork()创建一个子进程,然后exec()让进程执行其他程序,这样就达到了父子进程执行不同程序目的。
wait/waitpid回收一个已经结束了的进程
如果子进程结束了但是没有被回收的话,此时进程进入死亡态,叫做僵尸进程。僵尸进程的进程控制块依然存在(PCB),会占用资源,务必对其进行回收。通过父进程对其回收。
#include <unistd.h>
pid_t wait(int *status);
- 成功时返回回收的子进程的进程号;失败时返回EOF
- 若子进程没有结束,父进程一直阻塞
- 若有多个子进程,哪个先结束就先回收
- status 指定保存子进程返回值和结束方式的地址
- status为NULL表示直接释放子进程PCB,不接收返回值
示例
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <sys/types.h>
-
#include <sys/wait.h>
-
-
int main()
-
{
-
int status;
-
pid_t pid;
-
-
if ((pid = fork()) < 0) {
-
perror(“fork”);
-
exit(-1);
-
}
-
else if (pid == 0) {
-
sleep(1);
-
exit(2);
-
}
-
else {
-
wait(&status);
-
printf(“%x\n”, status);
-
}
-
-
return 0;
-
}
wait()函数被父进程执行,父进程便进入阻塞态(一直等待),直到子进程结束。status保存子进程的返回值和结束方式地址。
如何创建一个守护进程
守护进程通常在系统启动时运行,系统关闭时结束。Linux很多服务程序以守护进程形式运行。其特点
- 始终在后台运行
- 独立于任何终端
- 周期性的执行某种任务或等待处理特定事件
Linux以会话(session)、进程组的方式管理进程。例如,我在打开shell(一个会话建立),运行一个脚本程序,然后关闭sell终端(会话关闭),程序也随之结束。而守护进程是不会随着终端关闭而结束的!!!
示例
-
#include <stdio.h>
-
#include <unistd.h>
-
#include <stdlib.h>
-
#include <time.h>
-
-
int main() {
-
pid_t pid;
-
FILE *fp;
-
time_t t;
-
int i;
-
-
if ((pid = fork()) < 0)
-
{
-
perror(“fork”);
-
exit(-1);
-
}
-
else if (pid > 0)
-
{
-
exit(0);
-
}
-
setsid();
-
umask(0);
-
chdir(“/tmp”);
-
for (i=0; i< getdtablesize(); i++)
-
{
-
close(i);
-
}
-
if ((fp = fopen(“time.log”, “a”)) == NULL)
-
{
-
perror(“fopen”);
-
exit(-1);
-
}
-
while ( 1 )
-
{
-
time(&t);
-
fprintf(fp, “%s”, ctime(&t));
-
fflush(fp);
-
sleep(1);
-
}
-
-
return 0;
-
}
过程分析
1.创建子进程,让子进程变成孤儿进程(杀死父进程)
-
if ((pid = fork()) < 0)
-
{
-
perror(¡°fork¡±);
-
exit(-1);
-
}
-
else if (pid > 0)
-
{
-
exit(0);
-
}
2.使子进程脱离愿终端(原来子进程是建立在终端上的),切换终端操作
setsid();
3.重设文件掩码(推测是给进程创建文件的权限)
umask(0);
4.设定工作目录(让进程创建的文件一直存在)
chdir()
5.关闭子进程从 父进程那边 `继承`过来的文件
-
for (i=0; i< getdtablesize(); i++)
-
{
-
close(i);
-
}
文章来源: blog.csdn.net,作者:hinzer,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/feit2417/article/details/81275493
- 点赞
- 收藏
- 关注作者
评论(0)