linux system()函数完全解密

“内事不决问百度,外事不决问谷歌”,遇到问题上网找答案,是每个技术人的习惯。
但网上资料多如牛毛,良莠不齐,有的文章可能自己都没验证过,要找到理想的能

解决自己疑惑的文章还是需要精力、技巧和运气的。最近想温习一下linux相关知识

,运气还不错,看到一篇知乎文章,文章条理清晰,排版舒适,通俗易懂,最重要

的是文章是经过作者实践验证得出的结论。

干货难得,尊重原创,本文经过作者授权转载,未经授权,建议改为收藏。

知乎原文链接:https://zhuanlan.zhihu.com/p/457019360

目录

一、基本功能和测试

1.头文件包含

2.函数原型

3.函数流程

4.测试代码

5.测试结果

二、注意事项

1.阻塞进程

2.返回值多

3.消耗内存

4.SIGINT和SIGQUIT

5.SIGCHLD

三、相关文章


一、基本功能和测试

1.头文件包含

#include <stdlib.h>

2.函数原型

/*
*功能:在C程序中执行字符串指定的系统命令或程序
*返回值:
*  1:命令为NULL
* -1:创建子进程失败
*  0x7f00:命令无法执行(子进程返回127)*  
*  其他值:命令的返回值或退出码。
*命令的返回值或退出码格式说明:
*  Bits 15-8 = 进程返回值或退出码。
*  Bit     7 = 是否产生了core dump。
*  Bits  6-0 = 导致进程被杀死的信号值。
*  常用异常码解析宏:
*  WIFEXITED(status)   正常退出返回非零值
*  WEXITSTATUS(status) 取得返回值或退出码
*
*  WIFSIGNALED(status) 被信号杀死返回非零
*  WTERMSIG(status)    取得杀死它的信号值
*
*  WIFSTOPPED(status)  被信号暂停返回非零
*  WSTOPSIG(status)    取得暂停它的信号值
*/
int system(const char *__command);

 3.函数流程

|-cmd==NULL? -------------YES---->return 1
|-创建子进程--------------失败---->return -1
|-执行cmd-----------------失败---->return 0x7f00(32512)
|-等待cmd执行完毕---等待cmd失败---->return -1
|-------------------------------->return cmd返回值或退出码

4.测试代码

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>

/*
*演示system返回值使用,空命令处理和信号处理跟system()不一样。
*/
int mysystem(const char * __command)
{
        pid_t pid;
        int retval=0;
        if(NULL==__command){
                return 1;
        }
        if((pid=fork())<0){//fork error
                return -1;
        }
        else if(pid==0){
                //use __command to replace current process
                execl("/bin/sh", "sh", "-c", __command, (char *)0);
                return 127;
        }
        else{
                while(waitpid(pid,&retval,0)<0){
                        if(EINTR!=errno){
                                retval = -1;
                                break;
                        }
                }
        }
        return retval;
}

int main(int argc,const char **argv)
{
        int ret=0;

        ret=mysystem(argv[1]);
        printf("mysystem ret=%d\n",ret);

        ret=system(argv[1]);
        printf("system ret  =%d\n",ret);
        return 1;
}

5.测试结果

(1)cmd为空

(2)未知命令

(3)执行成功

返回1的是第二个./mysys打印,表示cmd为空。

返回256的是第一个./mysys打印,表示“./mysys”执行成功,并返回了1(256=0x100)

二、注意事项

1.阻塞进程

system会阻塞当前进程,但是当前进程的其他线程不会被阻塞。

可对上面的代码稍加改动,然后测试:

#include <pthread.h>
void *threadloop(void *arg)
{
        while(1){
                printf("this is thread\n");
                usleep(100000);
        }
}

int main(int argc,const char **argv)
{
        int ret=0;
        pthread_t tid;
        pthread_create(&tid,NULL,threadloop,NULL);
        ret=mysystem(argv[1]);
        printf("mysystem ret=%d\n",ret);
        ret=system(argv[1]);
        printf("system ret  =%d\n",ret);
        pthread_join(tid,NULL);
        return 1;
}

 测试结果:system会阻塞当前进程,但是当前进程的其他线程不会被阻塞。

2.返回值多

system()返回值多而复杂,咋一看五花八门,所以必须妥善处理,下面是一个返回值处理的范本。

int main(int argc,const char **argv)
{
        int ret=0;
        const char *cmd=argv[1];

        ret=system(cmd);
        printf("system ret  =%d\n",ret);
        if(1==ret){
                printf("程序命令为空\n");
        }
        else if(-1==ret){
                printf("创建命令子进程失败\n");
        }
        else if(0x7f00==ret){
                printf("命令错误,无法执行\n");
        }
        else{
                if(WIFEXITED(ret)){
                        printf("程序正常结束,返回值:%d\n",WEXITSTATUS(ret));
                }
                else if(WIFSIGNALED(ret)){
                        printf("程序被信号杀死,信号值:%d\n",WTERMSIG(ret));
                }
                else if(WSTOPSIG(ret)){
                        printf("程序被信号暂停,信号值:%d\n",WSTOPSIG(ret));
                }
        }
        return 1;
}

3.消耗内存

子进程完全拷贝父进程的内存数据,所以会消耗跟父进程一样的物理内存。

4.SIGINT和SIGQUIT

system执行命令的期间主进程收到的SIGINT和SIGQUIT会被忽略,这个要求是posix.1规定的,原因是posix不希望主进程响应system子进程的SIGINT和SIGQUIT信号。

下面实验可见,进入sleep 60的子进程后,再给主进程./mysys发送-2和-3(SIGINT和SIGQUIT)信号,无法结束主进程。

5.SIGCHLD

system执行命令的期间主进程收到的SIGCHLD会被阻塞,直到system返回才能收到。这也是posix.1要求的,因为system里面会wait自己创建的子进程,不希望主进程外面再响应system的子进程的SIGCHLD信号。

下面的实验可见,主进程./mysys在调用signal(SIGCHLD,SIG_IGN);后,再调用system()执行ls,会出现返回值为-1,但ls实际执行成功的问题。

 需要注意的是system执行前,主进程必须保证SIGCHLD没有设置为SIG_IGN,否则将导致system无法获取子进程的状态,system()返回-1,但命令可能执行成功的问题。

三、相关文章

linux C语言程序中执行外部命令,除了system()还有一个popen接口,预计在下一篇文章《宏伟精讲-linux-api-popen()研究及与system()比较》中详细研究,欢迎关注和讨论。

 干货难得,尊重原创,本文经过作者授权转载,未经授权,建议改为收藏。

《linux popen()函数完全解密》

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值