浅聊一下system()函数与popen()函数

这篇具有很好参考价值的文章主要介绍了浅聊一下system()函数与popen()函数。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

浅聊一下system()函数与popen()函数

1.system()函数

system()函数先fork一个子进程,在这个子进程中调用/bin/sh -c来执行command指定的命令。/bin/sh在系统中一般是个软链接,指向dash或者bash等常用的shell,-c选项是告诉shell从字符串command中读取要执行的命令。父进程则调用waitpid()函数来为变成僵尸的子进程收尸,获得其结束状态,然后将这个结束状态返回给system()函数的调用者。

system()源码:

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) {
        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成功,则返回子进程的返回状态
}

(1)当参数command是NULL的时候
返回1,表明系统的命令处理程序,即/bin/sh是可用的。
返回0,命令处理程序不可用。
(2)当参数command不是NULL的时候
1)如果fork等系统调用失败,或者waitpid函数发生除EINTR外的错误时,system返回-1
2)一切致使execl失败的情况下,system返回127
3)除此之外,system返回/bin/sh的终止状态

2.popen()函数

popen总是和pclose一起出现被使用的。popen() 创建一个匿名管道,通过fork或者invoke一个子进程,然后执行command。返回值在标准IO流中,由于是在管道之中,因此数据流是单向的,command只能产生stdout或者读取stdin,因此type只有两个值:‘w’或‘r’。r表示command从管道中读取数据流,而w表示command的stdout输出到管道中。command无法同时读取和输出。popen返回该FIFO数据流的指针。并行执行,可以在程序中读取命令的标准输出,也可在程序中写入命令的标准输入。
pclose函数关闭标准IO流,等待命令的终止,然后返回shell的终止状态。如果你没有在调用popen后调用pclose那么这个子进程就可能变成”僵尸”。
popen()函数源码:

static pid_t    *childpid = NULL;  
                        /* ptr to array allocated at run-time */  
static int      maxfd;  /* from our open_max(), {Prog openmax} */  
 
#define SHELL   "/bin/sh"  
 
FILE* popen(const char *cmdstring, const char *type)  
{  
    int     i, pfd[2];  
    pid_t   pid;  
    FILE    *fp;  
 
    /* only allow "r" or "w" */  
    if ((type[0] != 'r' && type[0] != 'w') || type[1] != 0) {  
        errno = EINVAL;     /* required by POSIX.2 */  
        return(NULL);  
    }  
 
    if (childpid == NULL) {     /* first time through */  
        /* allocate zeroed out array for child pids */  
        maxfd = open_max();  
        if ( (childpid = calloc(maxfd, sizeof(pid_t))) == NULL)  
            return(NULL);  
    }  
 
    if (pipe(pfd) < 0)  
        return(NULL);   /* errno set by pipe() */  
 
    if ( (pid = fork()) < 0)  
        return(NULL);   /* errno set by fork() */  
    
    /* child */ 
    else if (pid == 0) {                             
        if (*type == 'r') {  
            close(pfd[0]);  
            if (pfd[1] != STDOUT_FILENO) {  
                dup2(pfd[1], STDOUT_FILENO);  // 子进程的标准输出写到pfd[1]
                close(pfd[1]);  
            }  
        } else {  
            close(pfd[1]);  
            if (pfd[0] != STDIN_FILENO) {  
                dup2(pfd[0], STDIN_FILENO);  // 子进程从的标准输入从pfd[0]读入
                close(pfd[0]);  
            }  
        }  
          /* close all descriptors in childpid[] */  
        for (i = 0; i < maxfd; i++)  
            if (childpid[i] > 0)  
                close(i);  
 
        execl(SHELL, "sh", "-c", cmdstring, (char *) 0);  
        _exit(127);  
    }  
 
    /* parent */  
    if (*type == 'r') {  
        close(pfd[1]);  
        if ((fp = fdopen(pfd[0], type)) == NULL)  
            return(NULL);  
    } else {  
        close(pfd[0]);  
        if ((fp = fdopen(pfd[1], type)) == NULL)  
            return(NULL);  
    }  
    childpid[fileno(fp)] = pid; /* remember child pid for this fd */  
    return(fp);  
}  

若成功则返回文件指针,否则返回NULL,错误原因存于errno中。

3.区别

1.功能:

  • popen()函数:popen()函数可以用来创建一个管道,通过管道与子进程进行双向通信。它可以执行一个外部命令,并返回一个文件指针,可以用于读取子进程的输出或向子进程发送输入。
  • system()函数:system()函数用于执行一个外部命令,但它只能执行命令,无法直接获取子进程的输入或输出。

2.使用方式:

  • popen()函数:popen()函数需要传入两个参数,第一个参数是要执行的命令字符串,第二个参数是打开模式("r"用于读取子进程输出,"w"用于向子进程发送输入)。它返回一个标准I/O文件指针,可以用于读取子进程的输出或向子进程发送输入。使用完毕后,需要通过pclose()函数关闭文件指针。
  • system()函数:system()函数只需要传入要执行的命令字符串作为参数即可。它会在执行命令完成后返回,并返回命令的退出状态。

3.安全性:

  • popen()函数:popen()函数对于执行外部命令的参数需要进行适当的过滤和验证,以防止命令注入等安全漏洞。
  • system()函数:system()函数对于参数的安全性验证较弱,如果参数字符串来自用户输入等不可信源,存在命令注入的风险。

4.信号处理

system中对SIGCHLD、SIGINT、SIGQUIT都做了处理,但是在popen中没有对信号做任何的处理。
SIGCHLD是子进程退出的时候发给父进程的一个信号,总结为了system()调用能够及时的退出并且能够正确的获取子进程的退出状态(成功回收子进程)。
popen没有屏蔽SIGCHLD,主要的原因就是popen是”并行”的。如果我们在调用popen的时候屏蔽了SIGCHLD,那么如果在调用popen和pclose之间调用进程又创建了其它的子进程并且调用进程注册了SIGCHLD信号处理句柄来处理子进程的回收工作(waitpid)那么这个回收工作会一直阻塞到pclose调用。这也意味着如果调用进程在pclose之前执行了一个wait()操作的话就可能获取到popen创建的子进程的状态,这样在调用pclose的时候就会回收(waitpid)子进程失败,返回-1,同时设置errno为ECHLD,标示pclose无法获取子进程状态。
popen()函数中没有屏蔽SIGINT、SIGQUIT的原因也还是因为popen是”并行的”,不能影响其它”并行”进程。文章来源地址https://www.toymoban.com/news/detail-495732.html

总结:

  • popen()函数适合在需要与子进程进行双向通信的情况下使用,可以方便地读取子进程的输出或向子进程发送输入。
  • system()函数适合简单的命令执行,不需要与子进程进行交互的情况下使用。
  • 在使用这些函数时,需要注意对命令参数的验证和过滤,以避免安全漏洞。

到了这里,关于浅聊一下system()函数与popen()函数的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C语言system()函数

    头文件: 作用:暂停程序进程。 示例: 会得到这样的结果: 这就是因为system(“pause”)暂停了进程,当我们按下任意键后程序继续. 作用:改变控制台背景色(由num1控制)和前景色(由num2控制),注意这里的num1和num2均为十六进制数。 注:整个程序中,只有最后一个system

    2024年02月07日
    浏览(33)
  • Linux--system函数

    system函数相当于封装后的exec system()函数的返回值如下: 成功:则返回进程状态值;当sh不执行时,返回127; 失败:返回-1; linux system函数详解 https://www.cnblogs.com/leijiangtao/p/4051387.html

    2024年02月09日
    浏览(29)
  • c++system函数

    希望各位给个赞,来个关注,100%回关 前言 system是一个比较常用的函数,说白了就是dos指令,下面说几个常用的 所需头文件:windows.h cls的作用是 清屏 ,会把控制台都清空 例如上面这段代码运行后,先输出hello,等待一秒后变成world. 是因为system(\\\"cls\\\")把hello给清除了,在输出的

    2023年04月09日
    浏览(21)
  • C/C++ system()函数的常用参数详解

    前言 一、常用推荐 二、文件操作 三、系统操作 总结 system()函数适用于C/C++程序调用操作系统命令,在我们的C++编程中会经常被使用。这里详细记录一下C++编程中system()函数的常用参数。 pause:暂停批处理文件的处理并显示消息 color:  设置默认控制台前景和背景颜色 title:

    2024年02月09日
    浏览(30)
  • gseaplot3修改一下clusterProfiler默认绘图函数

    直接使用 clusterProfiler::gseaplot2 绘图会出现下边的结果,导致四周显示不全,线的粗细也没办法调整,因为返回的是一个aplot包中的gglist对象,没太多研究。 自定义gseaplot3函数增加了size参数调整线的粗细,也调整了margin四周边距,可以在下边gseaplot3函数的基础上继续调整,如

    2024年01月20日
    浏览(30)
  • V3.0_用exec族函数替代system()

    注意点:exec族函数的使用 以execl为例: (1)  (2)exec族函数中的函数调用失败时会设置error并返回-1,然后从源程序调用点接着往下执行。 执行成功后不会返回,也不会从源程序调用点接着往下执行。 exec族函数一般配合fork使用; 创建子进程后,在子进程中调用exec组函数

    2024年02月06日
    浏览(35)
  • linux的exec和system函数介绍及选择

    在应用程序中有时候需要调用第三方的应用,这是常见的需求。此时可以使用linux下的exec命令或system命令达到目的。但是这两个该选择哪个呢?有什么区别?下面总结介绍下。 在Linux中,`exec`命令用于在当前进程中执行一个新的程序。它会取代当前进程的内容,并用新的程序

    2024年02月15日
    浏览(34)
  • C++ system()函数的常用用法 (史上最详细)

    目录 一.推荐:     1. system(\\\"pause\\\")     2. system(\\\"color *\\\")     3.system(\\\"title *\\\")     4. system(\\\"cls\\\") 二.文件操作:     1. system(\\\"start *\\\")     2. system(\\\"del *\\\")     3. system(\\\"copy A B\\\")     4. system(\\\"move A B\\\") 三.系统操作     1. 关机,重启,睡眠     2. 日期/时间     3. task     1. system(\\\"pause\\\"

    2024年02月12日
    浏览(43)
  • 与ChatGPT浅聊Pulsar

    我 : 艾米丽,谈一谈你对Pulsar的理解? ChatGPT : 当然可以!Apache Pulsar是一款分布式消息中间件,它支持多种消息模式,包括发布/订阅模式、队列模式和流模式。在发布/订阅模式下,消息发布者将消息发布到一个主题中,订阅者可以订阅该主题,并接收到所有发布到该主题

    2023年04月15日
    浏览(39)
  • 浅聊JVM--基础版

    jvm共有三种 Sun公司: HotSpot使用最多 BEA:JRockit IBM:J9VM ​ 今天我们主要了解的是Sun公司的HotSpot(关于HotSpot的爱恨情仇这里就不做过多解释了。)我们以前测试jdk是否安装成功,java的环境变量是否配置成功会使用java -version命令来检查。有一个细节大家可以看一下,cmd输入

    2024年02月03日
    浏览(12)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包