精华内容
参与话题
问答
  • exec系列函数

    2020-04-11 11:02:55
    linux系统编程之进程(五):exec系列函数(execl,execlp,execle,execv,execvp)使用 本节目标: exec替换进程映像 exec关联函数组(execl、execlp、execle、execv、execvp) 一,exec替换进程映像 在进程的...

    linux系统编程之进程(五):exec系列函数(execl,execlp,execle,execv,execvp)使用

     

    本节目标:

    • exec替换进程映像
    • exec关联函数组(execl、execlp、execle、execv、execvp)

    一,exec替换进程映像

    在进程的创建上Unix采用了一个独特的方法,它将进程创建与加载一个新进程映象分离。这样的好处是有更多的余地对两种操作进行管理。

    当我们创建了一个进程之后,通常将子进程替换成新的进程映象,这可以用exec系列的函数来进行。当然,exec系列的函数也可以将当前进程替换掉。

    例如:在shell命令行执行ps命令,实际上是shell进程调用fork复制一个新的子进程,在利用exec系统调用将新产生的子进程完全替换成ps进程。

    二,exec系列函数(execl、execlp、execle、execv、execvp)

    包含头文件<unistd.h>

    功能:

        用exec函数可以把当前进程替换为一个新进程,且新进程与原进程有相同的PID。exec名下是由多个关联函数组成的一个完整系列,

    头文件<unistd.h>

    extern char **environ;

    原型:

    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[]);

    参数:

    path参数表示你要启动程序的名称包括路径名

    arg参数表示启动程序所带的参数,一般第一个参数为要执行命令名,不是带路径且arg必须以NULL结束

    返回值:成功返回0,失败返回-1

    注:上述exec系列函数底层都是通过execve系统调用实现:

           #include <unistd.h>

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

    DESCRIPTION:
           execve() executes the program pointed to by filename.  filename must be
           either a binary executable, or a script starting with  a  line  of  the form

    以上exec系列函数区别:

    1,带l 的exec函数:execl,execlp,execle,表示后边的参数以可变参数的形式给出且都以一个空指针结束。

    示例:

    复制代码
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    int main(void)
    {
        printf("entering main process---\n");
        execl("/bin/ls","ls","-l",NULL);
        printf("exiting main process ----\n");
        return 0;
    }
    复制代码

    QQ截图20130712225614

    利用execl将当前进程main替换掉,所有最后那条打印语句不会输出

    2,带 p 的exec函数:execlp,execvp,表示第一个参数path不用输入完整路径,只有给出命令名即可,它会在环境变量PATH当中查找命令

    示例:

    当不带p但没给出完整路径时:

    复制代码
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    int main(void)
    {
        printf("entering main process---\n");
        execl("/bin/ls","ls","-l",NULL);
        printf("exiting main process ----\n");
        return 0;
    }
    复制代码

    结果:

    QQ截图20130712230253

    结果显示找不到,所有替换不成功,main进程继续执行

    现在带p:

    QQ截图20130712230432

    替换成功

    3,不带 l 的exec函数:execv,execvp表示命令所需的参数以char *arg[]形式给出且arg最后一个元素必须

    是NULL

    示例:

    复制代码
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    int main(void)
    {
        printf("entering main process---\n");
        int ret;
        char *argv[] = {"ls","-l",NULL};
        ret = execvp("ls",argv);
        if(ret == -1)
            perror("execl error");
        printf("exiting main process ----\n");
        return 0;
    }
    复制代码

    结果:

    QQ截图20130712230956

    进程替换成功

    4,带 e 的exec函数:execle表示,将环境变量传递给需要替换的进程

    从上述的函数原型中我们发现:

    extern char **environ;

    此处的environ是一个指针数组,它当中的每一个指针指向的char为“XXX=XXX”

    environ保存环境信息的数据可以env命令查看:

    QQ截图20130712231740

    它由shell进程传递给当前进程,再由当前进程传递给替换的新进程

    示例:execle.c

    复制代码
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    int main(int argc, char *argv[])
    {
        //char * const envp[] = {"AA=11", "BB=22", NULL};
        printf("Entering main ...\n");
        int ret;
        ret =execl("./hello", "hello", NULL);
        //execle("./hello", "hello", NULL, envp);
        if(ret == -1)
            perror("execl error");
        printf("Exiting main ...\n");
        return 0;
    }
    复制代码

    hello.c

     

    复制代码
    #include <unistd.h>
    #include <stdio.h>
    extern char** environ;
    
    int main(void)
    {
        printf("hello pid=%d\n", getpid());
        int i;
        for (i=0; environ[i]!=NULL; ++i)
        {
            printf("%s\n", environ[i]);
        }
        return 0;
    }
    复制代码

     

    结果:

    QQ截图20130712232918

    可知原进程确实将环境变量信息传递给了新进程

    那么现在我们可以利用execle函数自己给的需要传递的环境变量信息:

    示例程序:execle.c

     

    复制代码
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    int main(int argc, char *argv[])
    {
        char * const envp[] = {"AA=11", "BB=22", NULL};
        printf("Entering main ...\n");
        int ret;
        //ret =execl("./hello", "hello", NULL);
        ret =execle("./hello", "hello", NULL, envp);
        if(ret == -1)
            perror("execl error");
        printf("Exiting main ...\n");
        return 0;
    }
    复制代码

     

    hello.c

     

    复制代码
    #include <unistd.h>
    #include <stdio.h>
    extern char** environ;
    
    int main(void)
    {
        printf("hello pid=%d\n", getpid());
        int i;
        for (i=0; environ[i]!=NULL; ++i)
        {
            printf("%s\n", environ[i]);
        }
        return 0;
    }
    复制代码

     

    结果:

    QQ截图20130712233335

    确实将给定的环境变量传递过来了

     

    三,fcntl()函数中的FD_CLOEXEC标识在exec系列函数中的作用

    #include <unistd.h>
    #include <fcntl.h>

    int fcntl(int fd, int cmd, ... /* arg */ );

    File descriptor flags
          The following commands manipulate the  flags  associated  with  a  file
          descriptor.   Currently, only one such flag is defined: FD_CLOEXEC, the
          close-on-exec flag.  If the FD_CLOEXEC bit is 0,  the  file  descriptor
          will remain open across an execve(2), otherwise it will be closed.

         //如果FD_CLOEXEC标识位为0,则通过execve调用后fd依然是打开的,否则为关闭的

          F_GETFD (void)
                 Read the file descriptor flags; arg is ignored.

          F_SETFD (long)
                 Set the file descriptor flags to the value specified by arg.

    如:fcntl(fd, F_SETFD, FD_CLOEXEC);

    测试示例:

     

    复制代码
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <fcntl.h>
    int main(int argc, char *argv[])
    {
    
        printf("Entering main ...\n");
        int ret = fcntl(1, F_SETFD, FD_CLOEXEC);
        if (ret == -1)
            perror("fcntl error");
        int val;
        val =execlp("ls", "ls","-l", NULL);
        if(val == -1)
            perror("execl error");
        printf("Exiting main ...\n");
        return 0;
    }
    复制代码

     

    结果:

    QQ截图20130712235308

    1关闭(标准输出关闭)ls -l无法将结果显示在标准输出

    展开全文
  • C例子:exec系列函数

    2016-04-02 21:06:01
    该程序是我写的博客“一起talk C栗子吧(第一百三十六回:C语言实例--exec系列函数二)”的配套程序,共享给大家使用
  • Linux 之exec系列函数

    千次阅读 2018-08-24 23:26:46
    当进程调用exec系列函数中的任一个时,该进程用户空间资源(正文、数据、堆、栈)完全由新程序替代。因为调用exec并不创建新程序,如果无特殊指示代码,进程内核信息基本不用修改 (1)execl函数声明如下: // ...

    在这里插入图片描述
    当进程调用exec系列函数中的任一个时,该进程用户空间资源(正文、数据、堆、栈)完全由新程序替代。因为调用exec并不创建新程序,如果无特殊指示代码,进程内核信息基本不用修改

    (1)execl函数声明如下:

    // from /usr/include/unistd.h
    int execl(const char* path,const char* arg,...); 
    例子 : execl("/bin/ls","ls","-l","/home",(char*)0);
    

    第一个参数表示execl要执行的程序,第二个及以后的参数代表执行文件时传递的参数列表,最后一个参数必须是空指针。

    (2)execle函数声明如下:

    // from /usr/include/unistd.h
    int execle(const char* path,const char* arg,...); 
    例子:execle("/bin/ls","ls","-l","/home",(char*)0,env);
    

    execle用来执行参数path代表的程序,第二个及以后的参数代表执行文件时传入参数列表,最后一个参数必须指向一个新的环境变量数组,即新执行程序的环境变量。

    (3)execlp函数声明如下:

    // from /usr/include/unistd.h
    int execlp(const char* file,const char* arg,...); 
    例子:execlp("ls","ls","-l","/home",(char*)0);
    

    execlp会在$PATH环境变量所指向的目录中查找文件名为第一个参数的程序,并执行它,第二个及以后的参数代表执行文件时传递的参数列表,最后一个参数必须是空指针。

    (4)execv函数声明如下:

    // from /usr/include/unistd.h
    int execv(const char* path,const char* argv[]);
    例子:char* argv[] = {"ls","-l","/home",(char*)0};
    	 execv("/bin/ls",argv); 
    

    execv函数执行path路径下的程序,第二个参数为数组指针维护的程序参数列表,该数组的最后一个成员必须为NULL.

    (5)execvp函数声明如下:

    // from /usr/include/unistd.h
    int execvp(const char* file,const char* argv[]);
    例子:char* argv[] = {"ls","-l","/home",(char*)0};
    	 execv("ls",argv); 
    

    execvp会在$PATH环境变量所指向的目录中查找文件名为第一个参数的程序,并执行它,第二个及以后的参数代表执行文件时传递的参数列表,最后一个参数必须是空指针。

    (6)system函数声明如下:

    //from /usr/include/stdlib.h
    int system(const char* command);
    

    system函数用来创建新进程,直到程序结束后才继续运行父进程。一般运行脚本文件
    在这里插入图片描述

    展开全文
  • exec 系列函数:execl execv等1. exec函数族1.1 exec 重要说明!!!1.2 使用exec函数族主要有两种情况:2.1 exec 函数族6个函数可以划分为两组:2.2 exec 函数的原型如下:2.3 exec 命名规律2.3.1 exec函数里的参数...

    1. exec函数族

    exec函数族提供了一个在进程中启动另一个程序执行的方法。
    实际上他们的功能都是差不多的, 因为要用于接受不同的参数所以要用不同的名字区分它们, 毕竟c语言没有函数重载的功能

    1.1 exec 重要说明!!!

    exec函数会取代执行它的进程, 也就是说, 一旦exec函数执行成功, 它就不会返回了, 进程结束.
    但是如果exec函数执行失败, 它会返回失败的信息, 而且进程继续执行后面的代码!
    通常exec会放在fork() 函数的子进程部分, 来替代子进程执行啦, 执行成功后子程序就会消失, 但是执行失败的话, 必须用exit()函数来让子进程退出!

    1.2 使用exec函数族主要有两种情况:

    (1)当进程认为自己不能再为系统和用户做出任何贡献时,就可以调用exec函数族中的任意一个函数让自己重生。
    (2)如果一个进程想执行另一个程序,那么它就可以调用fork函数新建一个进程,然后调用exec函数族中的任意一个函数,
    这样看起来就像通过执行应用程序而产生了一个新进程(这种情况非常普遍)。

    2. exec函数族共有6种不同形式的函数

    2.1 exec 函数族6个函数可以划分为两组:

    这两组函数的不同在于exec后的第一个字符,

    (1)execl、execle和execlp。
    

    第一组是l,在此称,为execl系列;这里的l是list(列表)的意思,表示execl系列函数需要将每个命令行参数作为函数的参数进行传递;

    (2)execv、execve和execvp。
    

    第二组是v,在此称为execv系列。而v是vector(矢量)的意思,表示execv系列函数将所有函数包装到一个矢量数组中传递即可。

    2.2 exec 函数的原型如下:

    int execl(const char * path,const char * arg,…);
    int execle(const char * path,const char * arg,char * const envp[]);
    int execlp(const char * file,const char * arg,…);
    int execv(const char * path,char * const argv[]);
    int execve(const char * path,char * const argv[],char * const envp[]);
    int execvp(const char * file,char * const argv[]);
    

    3 exec 命名规律

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

    3.1 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};

    3.2 命名规则:

    e 结尾, 参数必须带环境变量部分, 环境变零部分参数会成为执行exec函数期间的环境变量, 比较少用
    l 结尾, 命令参数部分必须以"," 相隔, 最后1个命令参数必须是NULL
    例如: execl("/bin/ls", “ls”, “-l”, NULL);
    v 结尾, 命令参数部分必须是1个以NULL结尾的字符串指针数组的头部指针.
    例如char * pstr就是1个字符串的指针, char * pstr[] 就是数组了, 分别指向各个字符串.
    execv(szcmd, ps_argv);
    p 结尾, 执行文件部分可以不带路径, exec函数会在$PATH中找

    3.3 参数说明:

    path:要执行的程序路径。可以是绝对路径或者是相对路径。在execv、execve、execl和execle这4个函数中,使用带路径名的文件名作为参数。
    file:要执行的程序名称。如果该参数中包含“/”字符,则视为路径名直接执行;否则视为单独的文件名,系统将根据PATH环境变量指定的路径顺序搜索指定的文件。
    argv:命令行参数的矢量数组。
    envp:带有该参数的exec函数可以在调用时指定一个环境变量数组。其他不带该参数的exec函数则使用调用进程的环境变量。
    arg:程序的第0个参数,即程序名自身。相当于argv[O]。
    …:命令行参数列表。调用相应程序时有多少命令行参数,就需要有多少个输入参数项。注意:在使用此类函数时,在所有命令行参数的最后应该增加一个空的参数项(NULL),表明命令行参数结束。

    例如: execl("/bin/ls", “ls”, “-l”, NULL);

    3.4 exec 返回值

    -1表明调用exec失败,无返回表明调用成功。

    4. execl execv 代码示例

    4.1 execl 代码示例

    4.1.1 execl 执行成功不返回/命令执行失败返回-1

    原因:
    在进程的创建上Unix采用了一个独特的方法,它将进程创建与加载一个新进程映象分离。
    execl将当前进程替换掉,所有最后那条打印语句不会输出

    #include <stdio.h>
    int main(int argc, char* argv[]){
    
    	int a = execl("/bin/ls", "ls", "-l", NULL);
    	printf("%d\n", a);
    
    	printf("exiting...\n");
    	return 0;
    }
    
    root@glusterfs home]# gcc test.c 
    [root@glusterfs home]# ll
    total 16
    -rwxr-xr-x 1 root root 8584 Dec  2 17:10 a.out
    -rw-r--r-- 1 root root  175 Dec  2 17:10 test.c
    [root@glusterfs home]# ./a.out 
    total 16
    -rwxr-xr-x 1 root root 8584 Dec  2 17:10 a.out
    -rw-r--r-- 1 root root  175 Dec  2 17:10 test.c
    [root@glusterfs home]# 
    

    4.1.2 execl + fork

    #include <stdio.h>
    #include <errno.h>
    #include <unistd.h>
    
    int main(int argc, char* argv[]){
    	int childpid;
    	pid_t pid;
    
    	pid = fork();
    	if(pid > 0)
    	{
    		printf("parent execl done\n");
    		wait(&childpid);
    	}else if(0 == pid)
    	{
    		execl("/bin/ls", "ls", "-l", NULL);
    		printf("pid execl done\n");
    	}else{
    		printf("error pid < 0 \n");
    	}
    	printf("exiting...\n");
    	return 0;
    }
    

    结果:

    [root@glusterfs home]# gcc test.c 
    [root@glusterfs home]# ./a.out 
    parent execl done
    total 52
    -rwxr-xr-x 1 root root 8632 Dec  3 14:14 a.out
    -rw-r--r-- 1 root root  444 Dec  3 14:14 test.c
    exiting...
    [root@glusterfs home]#
    

    4.2 execv 代码示例

    #include <stdio.h>
    #include <errno.h>
    #include <unistd.h>
    
    int main(int argc, char* argv[]){
    	int childpid;
    	pid_t pid;
    
    	char szcmd[] = "/bin/ls";
    	char *ps_argv[3];
    	ps_argv[0] = "ls";
    	ps_argv[1] = "-l";
    	ps_argv[2] = NULL;
    
    	pid = fork();
    	if(pid > 0)
    	{
    		printf("parent execv done\n");
    		wait(&childpid);
    	}else if(0 == pid)
    	{
    		execv(szcmd, ps_argv);
    		printf("pid execv done\n");
    	}else{
    		printf("error pid < 0 \n");
    	}
    	printf("exiting...\n");
    	return 0;
    }
    

    结果:

    [root@glusterfs home]# gcc execv.c 
    [root@glusterfs home]# ./a.out 
    parent execv done
    total 48
    -rwxr-xr-x 1 root root 8632 Dec  3 14:19 a.out
    -rw-r--r-- 1 root root  550 Dec  3 14:19 execv.c
    -rwxr-xr-x 1 root root 8648 Dec  2 17:39 sys
    -rw-r--r-- 1 root root  192 Dec  2 17:39 sys.c
    -rwxr-xr-x 1 root root 8632 Dec  3 11:49 test
    -rw-r--r-- 1 root root  444 Dec  3 14:14 test.c
    exiting...
    [root@glusterfs home]#
    

    4.3 execv execle execlp 等不再阐述

    参考:

    https://www.cnblogs.com/dongguolei/p/8098181.html
    https://blog.csdn.net/amoscykl/article/details/80354052

    展开全文
  • 文章目录一、exec替换进程映象二、exec关联函数组三、执行exec函数,下面属性是不发生变化的四、执行exec函数,将会发生变化的属性五...了一个进程之后,通常将子进程替换成新的进程映象,这可以用exec系列函数来...

    一、exec替换进程映象

    在进程的创建上Unix采用了一个独特的方法,它将进程创建与加载一个新进程映象分离。这样的好处是有更多的余地对两种操作进行管理。当我们创建

    了一个进程之后,通常将子进程替换成新的进程映象,这可以用exec系列的函数来进行。当然,exec系列的函数也可以将当前进程替换掉。

    二、exec关联函数组

    1. 包含头文件

      <unistd.h>

    2. 功能

      用exec函数可以把当前进程替换为一个新进程。

    3. 原型

        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 execvpe(const char *file, char *const argv[], char *const envp[]);
      
    4. 参数
      path参数表示你要启动程序的名称包括路径名
      arg参数表示启动程序所带的参数

    5. 返回值

      成功,没有返回值----因为进程替换,他已经去执行别的进程,如果非要说,不妨说是替换进程的返回值,一个正常退出的程序的返回值为 0

      失败,返回-1

    6. 可分为两组

      execlexeclpexecle(都带l,可以将l理解为list)的参数个数是可变的,参数以一个空指针结束。

      execvexecvpexecvpe的第二个参数是一个字符串数组(可以理解为vector)。

      新程序在启动时会把在argv数组中给定的参数传递到main(不管是不是进程替换,main函数都是一个程序的入口函数)

    7. 关于函数的一些解释

      名字含字母“p”的函数会搜索PATH环境变量去查找新程序的可执行文件。如果可执行文件不在PATH定义的路径上,就必须把包括子目录在内的绝对文件名做为一个参数传递给这些函数。

      名字最后一个字母为"e"的函数可以自设环境变量。

    8. 关于这一些列函数的实现

      这些函数通常都是用execve实现的,这是一种约定俗成的做法,并不是非这样不可。

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

      注意,前面6个函数都是C库函数,而execve是一个系统调用。
      六个exec函数之间的关系如下图:
      六个exec函数之间的关系

    三、执行exec函数,下面属性是不发生变化的

    1. 进程ID和父进程IDpid, ppid
    2. 实际用户ID和实际组IDruid, rgid
    3. 附加组IDsgid
    4. 会话ID
    5. 控制终端
    6. 闹钟余留时间
    7. 当前工作目录
    8. 根目录
    9. umask
    10. 文件锁
    11. 进程信号屏蔽
    12. 未处理信号
    13. 资源限制
    14. 进程时间

    四、执行exec函数,将会发生变化的属性

    1. 关于 文件描述符的close-on-exec属性

      文件描述符如果存在close-on-exec标记的话,那么打开的文件描述符会被关闭。

    2. 如果可执行程序文件存在SUIDSGID位的话,那么有效用户ID和组IDeuid, gid)会发生变化

    3. 关于信号处理方式

      程序启动的时候,所有的信号处理方式都是默认的。

      fork来说,因为子进程和父进程的地址空间是一样的,所以信号处理方式保留了下来。

      接下来进行exec,会将所有设置成为捕捉的信号都修改成为默认处理,而原来已经设置成为忽略的信号就不发生改变。

    4. 示例

      我们写一个简单的小程序,演示一下exec函数的用法,我们拿 execle 举例吧

      /**
       * filename instanceOfExec.cpp
       * author   zbq
       * date     2019.05.15
       * */
      #include <iostream>
      #include <cstdio>
      #include <cstdlib>
      #include <cassert>
      #include <unistd.h>
      #include <sys/wait.h>
      
      static const char * env_init[] = {"USER=root", "PATH=/root/study/apue", NULL};
      
      int main(void)
      {
          pid_t pid = 0;
      
          pid = fork();
      
          if(pid < 0)
          {
              std::cerr << "fork() faild " << std::endl;
              exit(1);
          }
          else if(pid == 0)
          {
              int ret_exec = execle("/usr/bin/echo", "echo", "hahaha, I'm from exec!!!", (char*)0);
              if(ret_exec < 0)
              {
                  std::cerr << "execle() faild -_-|" << std::endl;
                  exit(-1);
              }
              exit(0);
          }
          else
          {
              pid_t ret_wait = waitpid(pid, nullptr, 0);
              if(ret_wait < 0 || ret_wait != pid)
              {
                  std::cerr << "waitpid() faild" << std::endl;
                  exit(2);
              }
          }
      
          return 0;
      }
      

      我们编译之:

       g++ instanceOfExec.cpp -std=c++11
      

      运行之:

      ./a.out
      

      exec函数实例
      我们成功打印出了hahaha, I'm from exec!!!,这和在bash上输入 echo "hahaha, I'm from exec!!!"是一样的。

    5. 我们用strace命令来看一下这个过程是怎么样的

      execve("./a.out", ["./a.out"], [/* 24 vars */]) = 0
      brk(NULL)                               = 0x12f2000
      mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f83d45ce000
      access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
      open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
      fstat(3, {st_mode=S_IFREG|0644, st_size=48797, ...}) = 0
      mmap(NULL, 48797, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f83d45c2000
      close(3)                                = 0
      open("/lib64/libstdc++.so.6", O_RDONLY|O_CLOEXEC) = 3
      read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0 \262\5\0\0\0\0\0"..., 832) = 832
      fstat(3, {st_mode=S_IFREG|0755, st_size=991616, ...}) = 0
      mmap(NULL, 3171168, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f83d40a7000
      mprotect(0x7f83d4190000, 2093056, PROT_NONE) = 0
      mmap(0x7f83d438f000, 40960, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xe8000) = 0x7f83d438f000
      mmap(0x7f83d4399000, 82784, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f83d4399000
      close(3)                                = 0
      open("/lib64/libm.so.6", O_RDONLY|O_CLOEXEC) = 3
      read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20S\0\0\0\0\0\0"..., 832) = 832
      fstat(3, {st_mode=S_IFREG|0755, st_size=1137024, ...}) = 0
      mmap(NULL, 3150120, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f83d3da5000
      mprotect(0x7f83d3ea6000, 2093056, PROT_NONE) = 0
      mmap(0x7f83d40a5000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x100000) = 0x7f83d40a5000
      close(3)                                = 0
      open("/lib64/libgcc_s.so.1", O_RDONLY|O_CLOEXEC) = 3
      read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\220*\0\0\0\0\0\0"..., 832) = 832
      fstat(3, {st_mode=S_IFREG|0755, st_size=88776, ...}) = 0
      mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f83d45c1000
      mmap(NULL, 2184192, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f83d3b8f000
      mprotect(0x7f83d3ba4000, 2093056, PROT_NONE) = 0
      mmap(0x7f83d3da3000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x14000) = 0x7f83d3da3000
      close(3)                                = 0
      open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
      read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\340$\2\0\0\0\0\0"..., 832) = 832
      fstat(3, {st_mode=S_IFREG|0755, st_size=2151672, ...}) = 0
      mmap(NULL, 3981792, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f83d37c2000
      mprotect(0x7f83d3984000, 2097152, PROT_NONE) = 0
      mmap(0x7f83d3b84000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c2000) = 0x7f83d3b84000
      mmap(0x7f83d3b8a000, 16864, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f83d3b8a000
      close(3)                                = 0
      mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f83d45c0000
      mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f83d45be000
      arch_prctl(ARCH_SET_FS, 0x7f83d45be740) = 0
      mprotect(0x7f83d3b84000, 16384, PROT_READ) = 0
      mprotect(0x7f83d3da3000, 4096, PROT_READ) = 0
      mprotect(0x7f83d40a5000, 4096, PROT_READ) = 0
      mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f83d45bd000
      mprotect(0x7f83d438f000, 32768, PROT_READ) = 0
      mprotect(0x600000, 4096, PROT_READ)     = 0
      mprotect(0x7f83d45cf000, 4096, PROT_READ) = 0
      munmap(0x7f83d45c2000, 48797)           = 0
      clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f83d45bea10) = 4573
      wait4(4573, hahaha, I'm from exec!!!
      NULL, 0, NULL)              = 4573
      --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=4573, si_uid=0, si_status=0, si_utime=0, si_stime=0} ---
      exit_group(0)                           = ?
      +++ exited with 0 +++
      

      5152行我们看到fork函数是调用clone函数创建4573号子进程的,然后子进程替换,父进程等待4573号进程退出,当子进程退出的时候,操作系统会给父进程发送SIGCHILD信号,父进程检测到该信号,就知道有子进程退出了。

    五、system函数

    1. 功能

      system函数调用/bin/sh -c command执行特定的命令,阻塞当前进程直到command命令执行完毕

    2. 原型

      int system(const char *command);
      
    3. 返回值

      如果无法启动shell运行命令,system将返回127

      出现不能执行system调用的其他错误时,返回-1

      如果system能够顺利执行,返回那个命令的退出码。

    4. system函数执行时,会调用forkexecvewaitpid等函数。

    5. 示例

      /**
       * filename testSystem.cpp
       * author   zbq
       * date     2019.05.15
       * */
      #include <iostream>
      #include <cstdio>
      #include <cstdlib>
      #include <unistd.h>
      
      int main(void)
      {
          system("/usr/bin/echo \"hahaha, I'm from system function!\"");
      
          return 0;
      }
      
      

      我们编译之:

       g++ testSystem.cpp
      

      运行之:

       ./a.out
      

      得到结果:

      system函数实例

      我们利用strace命令来看看system函数如何运作起来的

      execve("./a.out", ["./a.out"], [/* 24 vars */]) = 0
      brk(NULL)                               = 0x1c20000
      mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fdc4a002000
      access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
      open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
      fstat(3, {st_mode=S_IFREG|0644, st_size=48797, ...}) = 0
      mmap(NULL, 48797, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fdc49ff6000
      close(3)                                = 0
      open("/lib64/libstdc++.so.6", O_RDONLY|O_CLOEXEC) = 3
      read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0 \262\5\0\0\0\0\0"..., 832) = 832
      fstat(3, {st_mode=S_IFREG|0755, st_size=991616, ...}) = 0
      mmap(NULL, 3171168, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fdc49adb000
      mprotect(0x7fdc49bc4000, 2093056, PROT_NONE) = 0
      mmap(0x7fdc49dc3000, 40960, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xe8000) = 0x7fdc49dc3000
      mmap(0x7fdc49dcd000, 82784, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fdc49dcd000
      close(3)                                = 0
      open("/lib64/libm.so.6", O_RDONLY|O_CLOEXEC) = 3
      read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20S\0\0\0\0\0\0"..., 832) = 832
      fstat(3, {st_mode=S_IFREG|0755, st_size=1137024, ...}) = 0
      mmap(NULL, 3150120, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fdc497d9000
      mprotect(0x7fdc498da000, 2093056, PROT_NONE) = 0
      mmap(0x7fdc49ad9000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x100000) = 0x7fdc49ad9000
      close(3)                                = 0
      open("/lib64/libgcc_s.so.1", O_RDONLY|O_CLOEXEC) = 3
      read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\220*\0\0\0\0\0\0"..., 832) = 832
      fstat(3, {st_mode=S_IFREG|0755, st_size=88776, ...}) = 0
      mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fdc49ff5000
      mmap(NULL, 2184192, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fdc495c3000
      mprotect(0x7fdc495d8000, 2093056, PROT_NONE) = 0
      mmap(0x7fdc497d7000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x14000) = 0x7fdc497d7000
      close(3)                                = 0
      open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
      read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\340$\2\0\0\0\0\0"..., 832) = 832
      fstat(3, {st_mode=S_IFREG|0755, st_size=2151672, ...}) = 0
      mmap(NULL, 3981792, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fdc491f6000
      mprotect(0x7fdc493b8000, 2097152, PROT_NONE) = 0
      mmap(0x7fdc495b8000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c2000) = 0x7fdc495b8000
      mmap(0x7fdc495be000, 16864, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fdc495be000
      close(3)                                = 0
      mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fdc49ff4000
      mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fdc49ff2000
      arch_prctl(ARCH_SET_FS, 0x7fdc49ff2740) = 0
      mprotect(0x7fdc495b8000, 16384, PROT_READ) = 0
      mprotect(0x7fdc497d7000, 4096, PROT_READ) = 0
      mprotect(0x7fdc49ad9000, 4096, PROT_READ) = 0
      mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fdc49ff1000
      mprotect(0x7fdc49dc3000, 32768, PROT_READ) = 0
      mprotect(0x600000, 4096, PROT_READ)     = 0
      mprotect(0x7fdc4a003000, 4096, PROT_READ) = 0
      munmap(0x7fdc49ff6000, 48797)           = 0
      rt_sigaction(SIGINT, {SIG_IGN, [], SA_RESTORER, 0x7fdc4922c280}, {SIG_DFL, [], 0}, 8) = 0
      rt_sigaction(SIGQUIT, {SIG_IGN, [], SA_RESTORER, 0x7fdc4922c280}, {SIG_DFL, [], 0}, 8) = 0
      rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
      clone(child_stack=0, flags=CLONE_PARENT_SETTID|SIGCHLD, parent_tidptr=0x7fff17e54140) = 4553
      wait4(4553, hahaha, I'm from system function!
      [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 4553
      rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7fdc4922c280}, NULL, 8) = 0
      rt_sigaction(SIGQUIT, {SIG_DFL, [], SA_RESTORER, 0x7fdc4922c280}, NULL, 8) = 0
      rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
      --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=4553, si_uid=0, si_status=0, si_utime=0, si_stime=0} ---
      exit_group(0)                           = ?
      +++ exited with 0 +++
      

      我们注意看一下5455行,先调用 clone 函数,之后调用 wait4 函数等待4553号进程退出,其实fork函数底层也是用clone函数实现的。

    展开全文
  • exec系列函数记忆

    2013-07-31 10:21:00
    最近看到了exec系列函数,由于相似函数有6个,于是总结了每个函数的寓意方便记忆。 6个函数分别如下: int execl(const char *path, const char *arg, ...); int execlp(const char *file, const char *arg, ...
  • 作用 替换主调进程的进程映像,并开始执行指定的新程序或者脚本。 说明 ........exec系列函数包含了6个函数,原型分别如下: 3>.上述6个函数最终会调用系统函数execve,即以上函数...

空空如也

1 2 3 4 5 ... 20
收藏数 522
精华内容 208
关键字:

exec系列函数