Linux开发中操控进程(fork exit/_exit exec wait/wait_pid...)

举报
王建峰 发表于 2021/11/19 01:58:31 2021/11/19
【摘要】 拥有梦想是一种智力,实现梦想是一种能力。   概述 并发程序是应用开发中非常重要的一部分内容,如何实现程序的并发?包括多进程编程、进程间通信机制、多线程编程、线程间同步和异步机制等等。本次介绍多进程编程: fork创建进程exit/_exit结束进程exec函数族让进程执行指定程序wait/waitpid回收...

拥有梦想是一种智力,实现梦想是一种能力。

 

概述

并发程序是应用开发中非常重要的一部分内容,如何实现程序的并发?包括多进程编程、进程间通信机制、多线程编程、线程间同步和异步机制等等。本次介绍多进程编程:

  • fork创建进程
  • exit/_exit结束进程
  • exec函数族让进程执行指定程序
  • wait/waitpid回收一个已经结束了的进程
  • 如何创建一个守护进程

 

系统调用fork允许一个进程(父进程)创建一个新进程(子进程)。通过fork,子进程几乎是父进程的复制版本,子进程获得父进程的栈、数据段、堆和执行文本段的拷贝。通常,调用fork产生子进程后,子进程随便会调用execve函数簇执行新的任务,随后执行exit相关函数退出。而父进程则通常会调用wait函数等待子进程终止。此外,我会通过程序的方向来介绍,如何让一个子进程脱离它原有的终端成为一个守护进程运行。

 

fork()创建一个子进程

 pid_t  fork(void);

  • 创建新的进程,失败时返回-1
  • 成功时父进程返回子进程的进程号,子进程返回0
  • 通过fork的返回值区分父进程和子进程

示例


  
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <sys/types.h>
  4. int main()
  5. {
  6. pid_t pid;
  7. if ((pid = fork()) < 0) {
  8. perror(“fork”);
  9. return -1;
  10. }
  11. else if (pid == 0) {//子进程执行
  12. printf(“child process : my pid is %d\n”, getpid());
  13. }
  14. else {//父进程执行
  15. printf(“parent process : my pid is %d\n”, getpid());
  16. }
  17. return 0;
  18. }

此时,子进程fork()函数之后,而不是程序的开始处进行。由于子进程继承了父进程的所有内容,所以两个进程的执行没有差分。

 

exit/_exit结束进程

 void  exit(int  status);
 void  _exit(int  status);

  • 结束当前的进程并将status返回

exit与_exit的区别是,exit结束进程时会刷新(流)缓冲区。

 

示例


  
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. int main(void)
  4. {
  5. printf("this process will exit");//
  6. exit(0);
  7. printf("never be displayed");
  8. }

由于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函数族执行某个程序,进程当前内容被指定的程序替换。从而实现让父子进程执行不同的程序。

 

示例


  
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. int main()
  4. {
  5. if (execl(“/bin/ls”, “ls”, “-a”, “-l”, “/etc”, NULL) < 0)
  6. {
  7. perror(“execl”);
  8. }
  9. return 0;
  10. }

 

问:如何让父子进程执行不同程序?

答:fork()创建一个子进程,然后exec()让进程执行其他程序,这样就达到了父子进程执行不同程序目的。

 

wait/waitpid回收一个已经结束了的进程

如果子进程结束了但是没有被回收的话,此时进程进入死亡态,叫做僵尸进程。僵尸进程的进程控制块依然存在(PCB),会占用资源,务必对其进行回收。通过父进程对其回收。

  #include  <unistd.h>
  pid_t wait(int *status);
   

  •  成功时返回回收的子进程的进程号;失败时返回EOF
  •  若子进程没有结束,父进程一直阻塞
  •  若有多个子进程,哪个先结束就先回收
  •  status 指定保存子进程返回值和结束方式的地址
  •  status为NULL表示直接释放子进程PCB,不接收返回值

示例


  
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sys/types.h>
  4. #include <sys/wait.h>
  5. int main()
  6. {
  7. int status;
  8. pid_t pid;
  9. if ((pid = fork()) < 0) {
  10. perror(“fork”);
  11. exit(-1);
  12. }
  13. else if (pid == 0) {
  14. sleep(1);
  15. exit(2);
  16. }
  17. else {
  18. wait(&status);
  19. printf(“%x\n”, status);
  20. }
  21. return 0;
  22. }

wait()函数被父进程执行,父进程便进入阻塞态(一直等待),直到子进程结束。status保存子进程的返回值和结束方式地址。

 

 

如何创建一个守护进程

守护进程通常在系统启动时运行,系统关闭时结束。Linux很多服务程序以守护进程形式运行。其特点

  • 始终在后台运行
  • 独立于任何终端
  • 周期性的执行某种任务或等待处理特定事件

Linux以会话(session)、进程组的方式管理进程。例如,我在打开shell(一个会话建立),运行一个脚本程序,然后关闭sell终端(会话关闭),程序也随之结束。而守护进程是不会随着终端关闭而结束的!!!

 

示例


  
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <stdlib.h>
  4. #include <time.h>
  5. int main() {
  6. pid_t pid;
  7. FILE *fp;
  8. time_t t;
  9. int i;
  10. if ((pid = fork()) < 0)
  11. {
  12. perror(“fork”);
  13. exit(-1);
  14. }
  15. else if (pid > 0)
  16. {
  17. exit(0);
  18. }
  19. setsid();
  20. umask(0);
  21. chdir(“/tmp”);
  22. for (i=0; i< getdtablesize(); i++)
  23. {
  24. close(i);
  25. }
  26. if ((fp = fopen(“time.log”, “a”)) == NULL)
  27. {
  28. perror(“fopen”);
  29. exit(-1);
  30. }
  31. while ( 1 )
  32. {
  33. time(&t);
  34. fprintf(fp, “%s”, ctime(&t));
  35. fflush(fp);
  36. sleep(1);
  37. }
  38. return 0;
  39. }

 

过程分析

1.创建子进程,让子进程变成孤儿进程(杀死父进程)


  
  1.     if ((pid = fork()) < 0
  2.     {
  3.       perror(¡°fork¡±);  
  4.       exit(-1);
  5.     }
  6.     else if (pid > 0
  7.     {
  8.        exit(0);
  9.     }

2.使子进程脱离愿终端(原来子进程是建立在终端上的),切换终端操作

setsid();
 

3.重设文件掩码(推测是给进程创建文件的权限)

umask(0);
 

4.设定工作目录(让进程创建的文件一直存在)

chdir()
 

5.关闭子进程从 父进程那边 `继承`过来的文件


  
  1.     for (i=0; i< getdtablesize(); i++) 
  2.     {
  3.        close(i);
  4.     }

 

文章来源: blog.csdn.net,作者:hinzer,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/feit2417/article/details/81275493

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。