exec系列函数和system函数

文章目录

  • 一、exec替换进程映象
  • 二、exec关联函数组
  • 三、执行exec函数,下面属性是不发生变化的
  • 四、执行exec函数,将会发生变化的属性
  • 五、system函数

一、exec替换进程映象

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

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

二、exec关联函数组

  1. 包含头文件

  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系列函数和system函数_第1张图片

三、执行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 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    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 
    #include 
    #include 
    #include 
    
    int main(void)
    {
        system("/usr/bin/echo \"hahaha, I'm from system function!\"");
    
        return 0;
    }
    
    

    我们编译之:

     g++ testSystem.cpp
    

    运行之:

     ./a.out
    

    得到结果:

    exec系列函数和system函数_第2张图片

    我们利用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函数实现的。

你可能感兴趣的:(Linux)