“内事不决问百度,外事不决问谷歌”,遇到问题上网找答案,是每个技术人的习惯。
但网上资料多如牛毛,良莠不齐,有的文章可能自己都没验证过,要找到理想的能解决自己疑惑的文章还是需要精力、技巧和运气的。最近想温习一下linux相关知识
,运气还不错,看到一篇知乎文章,文章条理清晰,排版舒适,通俗易懂,最重要
的是文章是经过作者实践验证得出的结论。
干货难得,尊重原创,本文经过作者授权转载,未经授权,建议改为收藏。
知乎原文链接:https://zhuanlan.zhihu.com/p/457019360
目录
一、基本功能和测试
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()比较》中详细研究,欢迎关注和讨论。
干货难得,尊重原创,本文经过作者授权转载,未经授权,建议改为收藏。