• 第6章 进程控制(3)_wait、exec和system函数


    5. 等待函数

    (1)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);

    返回值

    成功返回子进程ID,出错返回-1

    功能

    等待子进程退出并回收,防止僵尸进程的产生

    参数

    (1)status参数:

      ①为空时,代表任意状态结束的子进程;

      ②不为空时,则等待指定状态结束的子进程

    (2)waitpid的pid参数

      ①pid == -1 :等待任一子进程,功能与wait等效。

      ②pid > 0 :等待其进程ID与pid相等的子进程

      ③pid == 0:等待其组ID等于调用进程的组ID的任一子进程

      ④pid < -1:等待其组ID等于pid的绝对值的任一子进程。

    (3)options参数

      ①WNOHANG:如果pid子进程未结束则立即返回,不会阻塞,此时返回值为0。如果pid进程己退出,则返回这个进程的pid。

      ②WUNTRACED:使waitpid报告那些己经被停止的未报告子进程的状态。

    备注

    wait的waitpid的区别:

      ①在一个子进程终止前,wait使用调用者阻塞

      ②waitpid的options可使调用者不阻塞

      ③waitpid等待一个指定的子进程,而wait等待所有的子进程,返回任一终止子进程的状态。

    (2)检查wait和waitpid函数返回的终止状态的宏

    判断终止状态

    获取终止状态值

    说明

    WIFEXITED(status)

    WEXITSTATUS(status)

    判断子进程是否正常终止及获取退出码

    WIFSIGNAL(status)

    WTERMSIG(status)

    判断子进程是否异常终止及获取异常终止的信号编码

    WIFSTOPED(status)

    WSTOPSIG(status)

    判断子进程是否被暂停及获取暂停的信号编码

    【编程实验】判断进程的终止状态

    //process_wait.c

    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/wait.h>
    
    void out_status(int status)
    {
        if(WIFEXITED(status)){
            printf("normal exit: %d
    ", WEXITSTATUS(status));
        }else if(WIFSIGNALED(status)){
            printf("abnormal terminate: %d
    ", WTERMSIG(status));
        }else if(WIFSTOPPED(status)){
            printf("Stopped signal: %d
    ", WSTOPSIG(status));
        }else{
            printf("unknow signal
    ");
        }
    }
    
    int main(void)
    {
        int status;
        pid_t pid;
       
       //第1个子进程
        if((pid = fork()) < 0){
            perror("fork error");
            exit(1);
        }else if(pid == 0){ //child process
            printf("pid: %d, ppid: %d
    ", getpid(), getppid());
            exit(0);  //正常终止
        }
    
        //等待子进程结束(也可防止僵尸进程)
        if(wait(&status) != pid)
              perror("wait error");
        out_status(status);
        printf("----------------------------------------
    ");
        
        //第2个子进程
        if((pid = fork()) < 0){
            perror("fork error");
            exit(1);
        }else if(pid == 0){ //child process
            printf("pid: %d, ppid: %d
    ", getpid(), getppid());
            abort();  //异常终止
        }
    
        //等待子进程结束(也可防止僵尸进程)
        if(wait(&status) != pid)
              perror("wait error");
        out_status(status);
        printf("----------------------------------------
    ");
    
        //第3个子进程
        if((pid = fork()) < 0){
            perror("fork error");
            exit(1);
        }else if(pid == 0){ //child process
            printf("pid: %d, ppid: %d
    ", getpid(), getppid());
            int i = 3, j = 0;
            int k =  i /j;  //除0
            printf("k: %d
    ", k);
        }
    
        if(wait(&status) != pid)
              perror("wait error");
        out_status(status);
        printf("----------------------------------------
    ");
     
        //第4个子进程
        if((pid = fork()) < 0){
            perror("fork error");
            exit(1);
        }else if(pid == 0){ //child process
            printf("pid: %d, ppid: %d
    ", getpid(), getppid());
            pause();  //暂停,需要用带WUNTRACED的waipid来等待。
                      //同时,该子进程需发kill -SIGSTOP 来结束
        }
        //阻塞方式
        waitpid(pid, &status, WUNTRACED);
    
    //    //非阻塞方式
    //   do
    //    {
    //        //当waitpid返回0,表示子进程未结束
    //        pid = waitpid(pid, &status, WNOHANG |  WUNTRACED);
    //        
    //        if (pid == 0) sleep(1);
    //    }while(pid == 0);
    
        out_status(status);
    
        return 0;
    }
    /*输出结果:
    pid: 1686, ppid: 1685
    normal exit: 0
    ----------------------------------------
    pid: 1687, ppid: 1685
    abnormal terminate: 6
    ----------------------------------------
    pid: 1688, ppid: 1685
    abnormal terminate: 8
    ----------------------------------------
    pid: 1689, ppid: 1685
    Stopped signal: 19
    */

    6. exec函数

    (1)exec函数的主要作用

      ①在fork函数创建子进程后,子进程往往要调exec函数来执行另一个程序。

      ②当进程调用exec函数时,该进程完全由新程序代换,替换原有进程的正文,而新程序则从其main函数开始执行。因为exec并不创建新进程,所以前后的进程ID并不改变。

      ③exec只是用另一个新程序替换了当前进程的正文、数据、堆和栈段

    (2)函数原型

    头文件

    #include <unistd.h>

    函数

    int execl(const char* pathname, const char* arg0,…/*(char*)0*/);

    int execv(const char* pathname, char* const argv[]);

    int execle(const char* pathname, const char* arg0,…/*(char*)0,char* const envp[]*/);

    int execve(const char* pathname, char* const argv[],char* const envp[]);

    int execlp(const char* pathname, const char* arg0,…/*(char*)0*/);

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

    返回值

    出错返回-1,成功则不返回

    功能

    执行程序

    参数

    (1)argv参数为新程序执行main函数中传递的argv参数,最后一个元素为NULL。

    (2)envp为进程环境表。

    (3)execlp和execvp函数中的pathname,相对和绝对路径均可使用其它四个函数中的pathname只能使用绝对路径。相对路径一定要在进程环境表对应的PATHT中。

    备注

    (1execve函数为系统调用,其余为库函数。当成功执行execve后就不返回 execve后面的代码去执行,进程进入新的程序执行,最后从新程序中退出。但执行execve出错时,仍会返回原程序。

    (2)函数名后带字母“l”,表示第2个参数为列表(List),列表中的第1个元素为要执行的程序名,最后一个参数必须为NULL。

    (3)带“p”的函数,表示第1个参数可以是相对或绝对路径。如果是相对路径,则会在环境变量PATH指定的路径中搜索。p表示PATH

    (4)带字母“v”表示参数列为通过一个字符串数组来传递,相当于main函数的argv参数,数组中的第1个元素必须是程序名,最后一个参数也必须为NULL。

    (5)带“e”的函数,用户可以自己设置程序接收一个设置环境变量的数组。

    (3)6个函数的关系

     

    (4)exec执行后新进程保留原进程的一些特性

      ①进程ID、父进程ID、实际用户和组ID、进程组ID

      ②会话ID、控制终端;

      ③当前工作目录、根目录

      ④文件锁,进程信号屏蔽、未处理信号、资源限制

      ⑤闹钟尚余留的时间、tms_utime、tms_stime、tms_cutime以及tms_cstime的值。

      ⑥对打开文件的处理与每个描述符的(close-on-exec)标志有关。如果此标志设置,则在执行exec时关闭该描述符,否则该描述符仍打开。(一般系统默认这个标志位是关闭,也可以用fcntl来设置该标志位)。

      ⑦在执行exec时会自动关闭打开的目录流(调用opendir函数打开的目录),也可以用fcntl函数为打开的目录流的描述符设置close-on-exec标志。

    【编程实验】打开新程序

    //process_exec.c

    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    char* cmd1 = "cat";      //相对路径
    char* cmd2 = "/bin/cat"; //绝对路径
    char* argv1 ="/etc/passwd";
    char* argv2 = "/etc/group";
    
    int main(void)
    {
        pid_t pid;
        
        //第1个子进程
        if((pid = fork()) < 0){
            perror("fork error");
            exit(1);
        }else if(pid == 0){  //child process
            //调用exec函数执行新的程序
            //if(execl(cmd1, cmd1, argv1, argv2, NULL) < 0){ //错误,execl只能用绝对路径
            if(execl(cmd2, cmd1, argv1, argv2, NULL) < 0){
                perror("execl error");
                exit(1);
            }else{
                printf("execl %s success
    ", cmd1); //不返回这里
            }
    
            //当exec成功调用后,执行流进入新的程序,不返回到这里。
            printf("after exec...
    ");
        }
        //只有父进程会执行到这里,子进程因exec进入了新的程序
        wait(NULL); 
        printf("--------------------------------
    ");
    
        //第2个子进程
        if((pid = fork()) < 0){
            perror("fork error");
            exit(1);
        }else if(pid == 0){  //child process
            char* argv[4]={cmd1, argv1, argv2, NULL};
            if(execvp(cmd1, argv) < 0){
                perror("execvp error");
                exit(1);
            }else{
                printf("execvp %s success
    ", cmd1); //不返回这里
            }
        }
    
        wait(NULL);
    
        return 0;
    }

    7. system函数

    头文件

    #include <stdlib.h>

    函数

    int system(const char* command);

    返回值

    成功返回执行命令的状态,出错返回-1

    功能

    简化exec函数的使用

    备注

    ①system函数内部构建一个子进程由子进程调用exec函数

    ②/bin/bash -c "cmd"或者exec("bash","-c","cmd");

    【编程实验】自定义system函数

    //process_system.c

    #include <unistd.h>
    #include <stdlib.h>
    
    const char* cmd1 = "date > s1.txt";
    const char* cmd2 = "date > s2.txt";
    //模仿system函数
    void mysystem(const char* cmd)
    {
        pid_t pid;
    
        //1.创建子进程
        if((pid = fork()) < 0){
            perror("fork error");
            exit(1);
        }else if(pid == 0){//子进程
            //2. 通过exec函数创/bin/bash进程来执行命令
            if(execlp("/bin/bash", "bin/bash","-c", cmd, NULL) < 0){
                perror("execlp error");
                exit(1);
            }
        }else{  //父进程
            wait(0);
        }
    }
    
    int main(void)
    {  
        system("clear"); //清屏
        system(cmd1);
        mysystem(cmd2); //自定义的mysystem
    
        return 0;
    }
    
    /*输出结果
    root@bogon 6.process]# cat s1.txt              
    2017年 01月 26日 星期四 19:26:23 CST
    [root@bogon 6.process]# cat s2.txt              
    2017年 01月 26日 星期四 19:26:23 CST
    [root@bogon 6.process]# 
    */
  • 相关阅读:
    [LeetCode] 157. Read N Characters Given Read4 用Read4来读取N个字符
    [LeetCode] 158. Read N Characters Given Read4 II
    AndroidManifest.xml文件详解(activity)(一)
    Android中Bitmap、Drawable、byte[]转换
    图片和byte[]数组互转
    EditText 属性
    adb server didn't ack failed to start daemon
    PhoneGap 获得设备属性Demo
    PhoneGap 第一个程序
    android手机常用分辨率
  • 原文地址:https://www.cnblogs.com/5iedu/p/6357946.html
Copyright © 2020-2023  润新知