• Linux下的系统编程——进程的执行与回收(八)


    前言:

    前面我们对进程已经有了一个初步的了解与认识,现在让我们学习一下进程中一些函数的具体使用,比如exec可以执行一些指定的程序,wait / waitpid可以回收子进程,什么是孤儿进程,什么是僵尸进程,下面让我们一起对这些进行中的操作进行学习吧

    目录

    一、exec函数族

    1.execlp:

      2.execl:

    3.execvp

    4.exec函数族的一般规律:

    二、回收子进程

    1.孤儿进程:

    2 .僵尸进程:

    ​编辑

    3.wait:

    4.waitpid

     5.waitpid回收多个子进程


    一、exec函数族

    将当前进程的.text、.data替换为所要加载的程序的.text、.data,然后让进程从新的.text第一条指令开始执行,但进程ID不变换核丕换壳
     

    1.execlp:

        int execlp(const char *file, const char *arg, ...);        借助 PATH 环境变量找寻待执行程序

            参1: 程序名

            参2: argv0

            参3: argv1

            ...: argvN

            哨兵:NULL

    该函数通常用来调用系统程序。如: ls、date、cp、cat等命令。
     

    1. #include
    2. #include
    3. #include
    4. int main(int argc,char *argv[])
    5. {
    6. int i;
    7. pid_t pid; //创建子进程
    8. if(pid == -1){
    9. perror("fork error");
    10. exit(1);
    11. }else if(pid == 0){ //子进程
    12. //execlp("ls","-l","-d","-h",NULL);//错误写法
    13. /************************************/
    14. execlp("ls","ls","-l","-h",NULL);
    15. /************************************/
    16. perror("exec error");
    17. exit(1);
    18. }
    19. else if(pid > 0){ //父进程
    20. sleep(1);
    21. printf("I'm parent : %d\n",getpid());
    22. }
    23. return 0;
    24. }

     

     date命令的实现:

    execlp("date","date",NULL);

     

      2.execl:

     int execl(const char *path, const char *arg, ...);    自己指定待执行程序路径。(路径+程序名)

    1. #include
    2. int main(int argc, char **argv)
    3. {
    4. printf("Hello, %s!\n", argv[1]);
    5. printf("Hello, world!\n");
    6. return 0;
    7. }

    1. #include
    2. #include
    3. #include
    4. int main(int argc,char *argv[])
    5. {
    6. int i;
    7. pid_t pid; //创建子进程
    8. if(pid == -1){
    9. perror("fork error");
    10. exit(1);
    11. }else if(pid == 0){ //子进程
    12. //execlp("ls","-l","-d","-h",NULL);
    13. //execlp("date","date",NULL);
    14. /************************************/
    15. execl("./a.out","./a.out","linux",NULL);
    16. /************************************/
    17. perror("exec error");
    18. exit(1);
    19. }
    20. else if(pid > 0){ //父进程
    21. sleep(1);
    22. printf("I'm parent : %d\n",getpid());
    23. }
    24. return 0;
    25. }

    3.execvp

    加载一个进程,使用自定义环境变量env


    int execvp(const char*file, const char *argv[]);

    1. #include
    2. #include
    3. #include
    4. int main(int argc,char *argv[])
    5. {
    6. int i;
    7. pid_t pid; 创建子进程
    8. if(pid == -1){
    9. perror("fork error");
    10. exit(1);
    11. }else if(pid == 0){ //子进程
    12. //execlp("ls","-l","-d","-h",NULL);
    13. //execlp("date","date",NULL);
    14. //execl("./a.out","./a.out","linux",NULL);
    15. /************************************/
    16. char *argv[] = {"date",NULL};
    17. execvp("date",argv);
    18. /************************************/
    19. perror("exec error");
    20. exit(1);
    21. }
    22. else if(pid > 0){ //父进程
    23. sleep(1);
    24. printf("I'm parent : %d\n",getpid());
    25. }
    26. return 0;
    27. }

    4.exec函数族的一般规律:

            l:命令行参数列表

            p:使用PATH环境变量

            v:使用命令行参数数组

            exec函数一旦调试成功即执行新的程序,不返回。只要失败才返回,错误值-1。所以通常我们直接在exec函数调用后调用 perror()和exit()。无需if判断。· 

    二、回收子进程

    1.孤儿进程:

    父进程死亡子进程进孤儿院

            孤儿进程:父进程先于子进程结束,则子进程成为孤儿进程,子进程的父进程成为init进程,称为init进程领养孤儿进程。
    模拟孤儿进程:

    1. #include
    2. #include
    3. #include
    4. int main(void)
    5. {
    6. pid_t pid;
    7. pid = fork();
    8. if (pid == 0) {
    9. while (1) {
    10. printf("I am child, my parent pid = %d\n", getppid());
    11. sleep(1);
    12. }
    13. } else if (pid > 0) {
    14. printf("I am parent, my pid is = %d\n", getpid());
    15. sleep(9);
    16. printf("------------parent going to die------------\n");
    17. } else {
    18. perror("fork");
    19. return 1;
    20. }
    21. return 0;
    22. }

     查看进程状态:ps ajx

     进程孤儿院:

      1   2035   2035   2035 ?            -1 Ss    1001   0:00 /lib/systemd/systemd --user
    

     解决方法:

                     杀死子进程:     kill -9 4871

    2 .僵尸进程:

    子进程死亡,父进程一直不管 

            僵尸进程:进程终止,父进程尚未回收,子进程残留资源(PCB)存放于内核中,变成僵尸(zombie)进程。(死亡以后没有回收)

            特别注意,僵尸进程是不能使用kill命令清除掉的。因为kill命令只是用来终止进程的,而僵尸进程已经终止。

    模拟僵尸进程:

    1. #include
    2. #include
    3. #include
    4. #include
    5. int main(void)
    6. {
    7. pid_t pid;
    8. pid = fork();
    9. if (pid == 0) {
    10. printf("---child, my parent= %d, going to sleep 10s\n", getppid());
    11. sleep(10);
    12. printf("-------------child die--------------\n");
    13. } else if (pid > 0) {
    14. while (1) {
    15. printf("I am parent, pid = %d, myson = %d\n", getpid(), pid);
    16. sleep(1);
    17. }
    18. } else {
    19. perror("fork");
    20. return 1;
    21. }
    22. return 0;
    23. }

      查看进程状态:ps ajx

     解决方法:

            杀死父进程:   kill -9 4770

    *3.wait:

        wait函数:    回收子进程退出资源, 阻塞回收任意一个。

        pid_t wait(int *status)

        

        参数:(传出) 回收进程的状态。

        返回值:成功: 回收进程的pid

                       失败: -1, errno

        函数作用1:    阻塞等待子进程退出

        函数作用2:    清理子进程残留在内核的 pcb 资源

        函数作用3:    通过传出参数,得到子进程结束状态

        
        获取子进程正常终止值

            WIFEXITED(status) --》 为真 --》调用 WEXITSTATUS(status) --》 得到 子进程 退出值。

        获取导致子进程异常终止信号

            WIFSIGNALED(status) --》 为真 --》调用 WTERMSIG(status) --》 得到 导致子进程异常终止的信号编号。

    1. #include
    2. #include
    3. #include
    4. #include
    5. int main(void)
    6. {
    7. pid_t pid, wpid;
    8. int status;
    9. pid = fork();
    10. if (pid == 0) {
    11. printf("---child, my id= %d, going to sleep 10s\n", getpid());
    12. sleep(10);
    13. printf("-------------child die--------------\n");
    14. return 73;
    15. } else if (pid > 0) {
    16. //wpid = wait(NULL); // 不关心子进程结束原因
    17. wpid = wait(&status); // 如果子进程未终止,父进程阻塞在这个函数上
    18. if (wpid == -1) {
    19. perror("wait error");
    20. exit(1);
    21. }
    22. if (WIFEXITED(status)) { //为真,说明子进程正常终止.
    23. printf("child exit with %d\n", WEXITSTATUS(status));
    24. }
    25. if (WIFSIGNALED(status)) { //为真,说明子进程是被信号终止.
    26. printf("child kill with signal %d\n", WTERMSIG(status));
    27. }
    28. printf("------------parent wait finish: %d\n", wpid);
    29. } else {
    30. perror("fork");
    31. return 1;
    32. }
    33. return 0;
    34. }

    正常终止:

    被信号终止:

    *4.waitpid

    waitpid函数:    指定某一个进程进行回收。可以设置非阻塞。          

        waitpid(-1, &status, 0) == wait(&status);

        pid_t waitpid(pid_t pid, int *status, int options)

        

    参数:
            pid:指定回收某一个子进程pid

                > 0: 待回收的子进程pid

                -1:任意子进程

                0:同组的子进程。

            status:(传出) 回收进程的状态。

            options:WNOHANG 指定回收方式为,非阻塞。

      

     返回值:

            > 0 : 表成功回收的子进程 pid

            0 : 函数调用时, 参3 指定了WNOHANG, 并且,没有子进程结束。

            -1: 失败。errno

    回收任意子进程:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. int main(int argc,char *argv[])
    8. {
    9. int i;
    10. pid_t pid,wpid;
    11. for(i = 0;i < 5;i++){
    12. if(fork()==0) //循环期间,子进程不fork
    13. break;
    14. }
    15. if(i == 5){ //父进程
    16. //wait(NULL);//一次wait/waitpid函数调用,只能回收一个子进程
    17. /*****************************************/
    18. wpid = waitpid(-1,NULL,WNOHANG);//回收任意子进程,没有结束的子进程,父进程直接返回0
    19. /****************************************/
    20. if(wpid == -1)
    21. {
    22. perror("waitpid error");
    23. exit(1);
    24. }
    25. printf("I'm parent ,wait a child finish :%d\n",wpid);
    26. }else{ //子进程,从break跳出
    27. sleep(i);
    28. printf("I'm %dth child\n",i+1);
    29. }
    30. return 0;
    31. }


    回收指定进程:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. int main(int argc,char *argv[])
    8. {
    9. int i;
    10. pid_t pid,wpid,tmpid;
    11. for(i = 0;i < 5;i++){
    12. pid = fork();
    13. if(pid == 0){ //循环期间,子进程不fork
    14. break;
    15. }
    16. if(i == 2){
    17. tmpid = pid;
    18. printf("*************pid= %d***************\n",pid);
    19. }
    20. }
    21. if(i == 5){ //父进程,从表达式2跳出
    22. sleep(5); //设置睡眠,等所有子进程结束后再回收
    23. //wait(NULL); //一次wait/waitpid函数调用,只能回收一个子进程
    24. //wpid = waitpid(-1,NULL,WNOHANG); //回收任意子进程,没有结束的子进程,父进程直接返回0
    25. printf("I am parent , before waitpid , pid = %d\n",tmpid);
    26. /********将前面sleep(5)屏蔽***************/
    27. //wpid = waitpid(tmpid,NULL,0); //指定一个进程回收,阻塞回收
    28. /****************************************/
    29. /*****************************************/
    30. wpid = waitpid(tmpid,NULL,WNOHANG); //指定一个进程回收,不阻塞
    31. /****************************************/
    32. if(wpid == -1)
    33. {
    34. perror("waitpid error");
    35. exit(1);
    36. }
    37. printf("I'm parent ,wait a child finish :%d\n",wpid); //wpid回收的是真正的子进程id
    38. }else{ //子进程,从break跳出
    39. sleep(i);
    40. printf("I'm %dth child,pid = %d\n",i+1,getpid());
    41. }
    42. return 0;
    43. }

    注意:

            一次wait/waitpid调用只能回收一个子进程,无法回收他孙子辈的进程,多次清理需要while

     5.waitpid回收多个子进程

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. int main(int argc,char *argv[])
    8. {
    9. int i;
    10. pid_t pid,wpid;
    11. for(i = 0;i < 5;i++){
    12. pid = fork();
    13. if(pid == 0){ //循环期间,子进程不fork
    14. break;
    15. }
    16. }
    17. if(i == 5){ //父进程
    18. /**********使用阻塞回收子进程********/
    19. while((wpid = waitpid(-1,NULL,0))){
    20. printf("wait child %d\n",wpid);
    21. }
    22. /***********************************/
    23. }else{ //子进程
    24. sleep(i);
    25. printf("I'm %dth child ,pid =%d\n",i+1,getpid());
    26. }
    27. return 0;
    28. }

    结束一个回收一个

    之后返回-1,表示没有失败了

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. int main(int argc,char *argv[])
    8. {
    9. int i;
    10. pid_t pid,wpid;
    11. for(i = 0;i < 5;i++){
    12. pid = fork();
    13. if(pid == 0){ //循环期间,子进程不fork
    14. break;
    15. }
    16. }
    17. if(i == 5){
    18. /*********使用阻塞回收子进程***********/
    19. /*
    20. while((wpid = waitpid(-1,NULL,0))){
    21. printf("wait child %d\n",wpid);
    22. }
    23. */
    24. /***********************************/
    25. /*******使用非阻塞方式回收子进程******/
    26. while((wpid = waitpid(-1,NULL,WNOHANG)) != -1){
    27. if(wpid > 0){
    28. printf("wait child %d\n",wpid);
    29. }else if(wpid == 0){
    30. sleep(1);
    31. continue;
    32. }
    33. /************************************/
    34. }
    35. }else{
    36. sleep(i);
    37. printf("I'm %dth child ,pid =%d\n",i+1,getpid());
    38. }
    39. return 0;
    40. }

  • 相关阅读:
    FFmpeg采坑记录——aformat过滤器输出的linesize大小和音频编码后stream_index
    Java Applet基础
    Spring Cloud Alibaba —— 服务注册与配置中心
    飞机机翼机身对接结构数值计算分析(ANSYS)
    面试题:Web View如何做测试?
    国产化之银河麒麟安装达梦数据库DM8
    【数据结构】树与二叉树(三):二叉树的定义、特点、性质及相关证明
    力扣84 双周赛 t4 6144 和力扣305周赛t4 6138
    【媒体邀约】媒体宣传——企业成长的催化剂
    Netty基础概念
  • 原文地址:https://blog.csdn.net/m0_63168877/article/details/132640093