linux下使用system函数产生新进程

在之前的linux学习和实践中,对于一个进程的产生一般都是以下几个函数:

  1. fork()
  2. vfork()
  3. exec()族函数以及轻量级进程(线程)clone()等的使用

参考链接:
http://www.man7.org/linux/man-pages/man2/fork.2.html
https://blog.csdn.net/kunkliu/article/details/86621131

最近在看公司源码时看到一个函数system(),当时不是很懂就搜了一下,发现也是产生新的进程的一种方法,
接下来对该函数进行了简单的使用和跟踪:

#include<stdlib.h>
int system(const char *command)

使用:

  1 #include<stdio.h>                                                                                 
  2 #include<stdlib.h>
  3 int main()
  4 {
  5     int n = system("ls -al");
  6     printf("sys_ret = %d\n",n);
  7     return 0;
  8 }

我们可以用strace进行跟踪一下:


实际上我们启动一个新进程必定会调用很多的系统调用:
包括open一些文件如动态库等
brk() mmap() munmap()等申请开辟内存,我在会在下面打印结果中删除一些与研究system函数关系不大的一些系统调用

txp@txp-TM1801:~/mydir/study_note/0717$ strace ./2
//
execve("./2", ["./2"], [/* 85 vars */]) = 0
brk(NULL)                               = 0x1ce6000

//删除open access stat等系统调用
mmap(NULL, 140814, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fca717ec000
close(3)                                = 0
//...
open("/lib/x86_64-linux-gnu/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\0P\t\2\0\0\0\0\0"..., 832) = 832
//...
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fca717eb000
//...
munmap(0x7fca717ec000, 140814)          = 0

//这里应该是对SIGINT SIGQUIT等信号函数做处理
rt_sigaction(SIGINT, {SIG_IGN, [], SA_RESTORER, 0x7fca712554b0}, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGQUIT, {SIG_IGN, [], SA_RESTORER, 0x7fca712554b0}, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0

//产生子进程,父进程中wait()
clone(child_stack=0, flags=CLONE_PARENT_SETTID|SIGCHLD, parent_tidptr=0x7ffd790627ac) = 26385
wait4(26385, total 40

//开始执行程序中给定system的命令
drwxrwxr-x 2 txp txp 4096 7月  18 09:59 .
drwxrwxr-x 3 txp txp 4096 7月  17 16:58 ..
-rwxrwxr-x 1 txp txp 9312 7月  17 17:05 1
-rw-rw-r-- 1 txp txp  191 7月  17 17:09 1.c
-rwxrwxr-x 1 txp txp 8656 7月  18 09:59 2
-rw-rw-r-- 1 txp txp  119 7月  18 09:59 2.c

[{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 26385
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7fca712554b0}, NULL, 8) = 0
rt_sigaction(SIGQUIT, {SIG_DFL, [], SA_RESTORER, 0x7fca712554b0}, NULL, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=26385, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 6), ...}) = 0
brk(NULL)                               = 0x1ce6000
brk(0x1d07000)                          = 0x1d07000

//在标准输出上打印sys_ret = 0这样的字符串
write(1, "sys_ret = 0\n", 12sys_ret = 0
)           = 12
exit_group(0)                           = ?
+++ exited with 0 +++

看到这里有点不明白为什么调用那么多关于信号的系统调用,然后查看帮助手册
在博客上也看到大家发出来关系这个函数的内核的实现,基本搞明白了.

先看帮助手册怎么说:man system

system()库函数使用fork(2)来创建执行shell的子进程,然后使用execl(3)在命令中指定的命令如下:
 execl(“/ bin / sh”,“sh”,“ -  c”,command,(char *)0);命令完成后,system()返回。

在该command执行期间,SIGCHLD是被阻塞的,告诉内核此时不要给我送SIGCHLD信号,等到命令执行完毕再说; 
在该command执行期间,SIGINT和SIGQUIT是被忽略的,意思是进程收到这两个信号后没有任何动作。
如果command为NULL,则system()返回指示shell是否可用的状态
(实际上,这就是我们看到在执行命令前,将对这些信号做了其他的处理,
如rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7fca712554b0}, NULL, 8) = 0)




最后,看一下大家网上粘出来的一部分源码,还是比较好理解的代码:
int system(const char * cmdstring)
{
    pid_t pid;
    int status;
    if(cmdstring == NULL)
    {
        return (1); //如果cmdstring为空,返回非零值,一般为1
    }
    if((pid = fork())<0)
    {
        status = -1; //fork失败,返回-1
    }
    else if(pid == 0)//用/bin/sh替换产生的子进程,并且执行命令cmdstring
    {
        execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
        _exit(127); 
     // exec执行失败返回127,注意exec只在失败时才返回现在的进程,成功的话现在的进程就不存在
    }
    else //父进程等待子进程的结束,否则产生僵尸进程
    {
        while(waitpid(pid, &status, 0) < 0)
        {
            if(errno != EINTR)
            {
                status = -1; //如果waitpid被信号中断,则返回-1
                break;
            }
        }
    }
    return status; //如果waitpid成功,则返回子进程的返回状态
}


那也就是说system()实际上也是对fork和exec类函数进行了封装,再加上waitpid()的调用
不同的是很固定的用shell来替换掉子进程,然后给shell传递命令字符串形式执行命令.
然后对于函数返回值也是明了,具体如果用到可以查看帮助手册和说明文档.

万变不离其宗.

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用waitpid()函数来检测子进程的退出状态。waitpid()函数挂起当前进程,直到指定的子进程退出为止。 具体步骤如下: 1. 在启动子进程时,使用fork()函数创建一个进程。 2. 在子进程使用exec()函数执行要启动的程序。 3. 在父进程使用waitpid()函数等待子进程退出。 4. 如果waitpid()函数返回的状态值为0,则表示子进程还在运行。 5. 如果waitpid()函数返回的状态值大于0,则表示子进程已经退出,并且可以通过WEXITSTATUS()宏获取子进程的退出状态。 6. 如果waitpid()函数返回的状态值小于0,则表示出现了错误。 下面是一个示例代码: ``` #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> int main() { pid_t pid; int status; pid = fork(); if(pid < 0) { perror("fork failed"); exit(EXIT_FAILURE); } else if(pid == 0) { // 子进程 execl("/bin/ls", "ls", "-l", NULL); exit(EXIT_SUCCESS); } else { // 父进程 while(waitpid(pid, &status, WNOHANG) == 0) { printf("子进程还在运行...\n"); sleep(1); } if(WIFEXITED(status)) { printf("子进程正常退出,退出状态为:%d\n", WEXITSTATUS(status)); } else if(WIFSIGNALED(status)) { printf("子进程异常退出,退出信号为:%d\n", WTERMSIG(status)); } } return 0; } ``` 在上面的示例代码中,我们使用了WNOHANG标志,表示waitpid()函数在子进程退出之前不挂起当前进程。这样可以避免当前进程一直阻塞在waitpid()函数中,导致无法响应其他事件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值