Linux C fork exec介绍用法

原文:http://blog.csdn.NET/nvd11/article/details/8856278

假如我们在编写1个c程序时想调用1个shell脚本或者执行1段 bash shell命令, 应该如何实现呢?

      其实在<stdlib.h> 这个头文件中包含了1个调用shell命令或者脚本的函数 system();直接把 shell命令作为参数传入 system函数就可以了, 的确很方便. 关于system 有一段这样的介绍:   system 执行时内部会自动启用fork() 新建1个进程,  效率没有直接使用fork() 和 exec函数高.

       那么这篇文章其实就是介绍一下fork() 和 exec函数的用法, 以及如何使用它们来替代system函数.

1. fork() 函数

1.1 fork() 函数的作用

       一般来讲, 我们编写1个普通的c程序, 运行这个程序直到程序结束, 系统只会分配1个pid给这个程序, 也就就说, 系统里只会有一条关于这个程序的进程.

 

        但是执行了fork() 这个函数就不同了. 

        fork 这个英文单词在英文里是"分叉"意思,  fork() 这个函数作用也很符合这个意思.  它的作用是复制当前进程(包括进程在内存里的堆栈数据)为1个新的镜像. 然后这个新的镜像和旧的进程同时执行下去. 相当于本来1个进程, 遇到fork() 函数后就分叉成两个进程同时执行了. 而且这两个进程是互不影响

  fork函数的特点概括起来就是“调用一次,返回两次”

1.2 区别分主程序和子程序.

        实际应用中, 单纯让程序分叉意义不大, 我们新增一个子程序, 很可能是为了让子进程单独执行一段代码. 实现与主进程不同的功能.

         要实现上面所说的功能, 实际上就是让子进程和主进程执行不同的代码啊.

         所以fork() 实际上有返回值, 而且在两条进程中的返回值是不同的, 在主进程里 fork()函数会返回子进程的pid,   而在子进程里会返回0!   所以我们可以根据fork() 的返回值来判断进程到底是哪个进程, 就可以利用if 语句来执行不同的代码了!

int fork_test(){  
    int childpid;  
    int ret;  
  
    if (ret =fork() == -1){    
            printf("fork failed\n");  
        }  
    }else if(ret == 0){  
        //child process  
            printf("This is child process\n");  
    }  else{
        printf("This is parent process\n");
    }
}      

1.3 使用wait() 函数主程序等子程序执行完成(退出)后再执行.   

        由上面例子得知,  主程序和子程序的执行次序是随机的,  但是实际情况下, 通常我们希望子进程执行后,  才继续执行主进程. 

        wait()函数就提供了这个功能,    在if 条件内的  主进程呢部分内 加上wait() 函数, 就可以让主进程执行fork()函数时先hold 住, 等子进程退出后再执行, 通常会配合子进程的exit()函数一同使用.

2. exec 函数组

      需要注意的是exec并不是1个函数, 其实它只是一组函数的统称, 它包括下面6个函数:

#include <unistd.h>  
  
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[]);  

可以见到这6个函数名字不同, 而且他们用于接受的参数也不同.

       实际上他们的功能都是差不多的, 因为要用于接受不同的参数所以要用不同的名字区分它们, 毕竟C语言没有函数重载的功能嘛..  

 

       但是实际上它们的命名是有规律的:

       exec[l or v][p][e]

       exec函数里的参数可以分成3个部分,      执行文件部分,     命令参数部分,   环境变量部分.

        例如我要执行1个命令   ls -l /home/gateman  

        执行文件部分就是  "/usr/bin/ls"

        命令参赛部分就是 "ls","-l","/home/gateman",NULL              见到是以ls开头 每1个空格都必须分开成2个部分, 而且以NULL结尾的啊.

        环境变量部分, 这是1个数组,最后的元素必须是NULL 例如  char * env[] = {"PATH=/home/gateman", "USER=lei", "STATUS=testing", NULL};

        

        好了说下命名规则:

        e后续,  参数必须带环境变量部分,   环境变零部分参数会成为执行exec函数期间的环境变量, 比较少用

        l 后续,   命令参数部分必须以"," 相隔, 最后1个命令参数必须是NULL

        v 后续,   命令参数部分必须是1个以NULL结尾的字符串指针数组的头部指针.         例如char * pstr就是1个字符串的指针, char * pstr[] 就是数组了, 分别指向各个字符串.

        p后续,   执行文件部分可以不带路径, exec函数会在$PATH中找

 

          

         还有1个注意的是, exec函数会取代执行它的进程,  也就是说, 一旦exec函数执行成功, 它就不会返回了, 进程结束.   但是如果exec函数执行失败, 它会返回失败的信息,  而且进程继续执行后面的代码!

 

       通常exec会放在fork() 函数的子进程部分, 来替代子进程执行啦, 执行成功后子程序就会消失,  但是执行失败的话, 必须用exit()函数来让子进程退出!

       下面是各个例子:

2.1  execv 函数

int childpid;  
int i;  
  
if (fork() == 0){  
    //child process  
    char * execv_str[] = {"echo", "executed by execv",NULL};  
    if (execv("/usr/bin/echo",execv_str) <0 ){  
        perror("error on exec");  
        exit(0);  
    }  
}else{  
    //parent process  
    wait(&childpid);  
    printf("execv done\n\n");  
}  

注意字符串指针数组的定义和赋值

2.2  execvp 函数

if (fork() == 0){  
    //child process  
    char * execvp_str[] = {"echo", "executed by execvp",">>", "~/abc.txt",NULL};  
    if (execvp("echo",execvp_str) <0 ){  
        perror("error on exec");  
        exit(0);  
    }  
}else{  
    //parent process  
    wait(&childpid);  
    printf("execvp done\n\n");  
}  

2.3 execve 函数

if (fork() == 0){  
    //child process  
    char * execve_str[] = {"env",NULL};  
    char * env[] = {"PATH=/tmp", "USER=lei", "STATUS=testing", NULL};  
    if (execve("/usr/bin/env",execve_str,env) <0 ){  
        perror("error on exec");  
        exit(0);  
    }  
}else{  
    //parent process  
    wait(&childpid);  
    printf("execve done\n\n");  
}  

2.4 execl 函数

if (fork() == 0){  
    //child process  
    if (execl("/usr/bin/echo","echo","executed by execl" ,NULL) <0 ){  
        perror("error on exec");  
        exit(0);  
    }  
}else{  
    //parent process  
    wait(&childpid);  
    printf("execv done\n\n");  
}  

2.5 execlp 函数

if (fork() == 0){  
    //child process  
    if (execlp("echo","echo","executed by execlp" ,NULL) <0 ){  
        perror("error on exec");  
        exit(0);  
    }  
}else{  
    //parent process  
    wait(&childpid);  
    printf("execlp done\n\n");  
} 

2.6 execle 函数

 

if (fork() == 0){  
    //child process  
    char * env[] = {"PATH=/home/gateman", "USER=lei", "STATUS=testing", NULL};  
    if (execle("/usr/bin/env","env",NULL,env) <0){  
        perror("error on exec");  
        exit(0);  
    }  
}else{  
    //parent process  
    wait(&childpid);  
    printf("execle done\n\n");  
}  

 

 
posted @ 2017-08-22 14:12  爱吃土豆的男孩  阅读(4074)  评论(0编辑  收藏  举报