`

【C/C++】Linux下使用system()函数一定要谨慎

 
阅读更多

 

曾经的曾经,被system()函数折磨过,之所以这样,是因为对system()函数了解不够深入。只是简单的知道用这个函数执行一个系统命令,这远远不够,它的返回值、它所执行命令的返回值以及命令执行失败原因如何定位,这才是重点。当初因为这个函数风险较多,故抛弃不用,改用其他的方法。这里先不说我用了什么方法,这里必须要搞懂system()函数,因为还是有很多人用了system()函数,有时你不得不面对它。

 

 

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

 system() executes a command specified in command by calling /bin/sh -c command, and returns after the command has been completed. During execution of the command, SIGCHLD will be blocked, and SIGINT and SIGQUIT will be ignored.

 

system()函数调用/bin/sh来执行参数指定的命令,/bin/sh 一般是一个软连接,指向某个具体的shell,比如bash,-c选项是告诉shell从字符串command中读取命令;
在该command执行期间,SIGCHLD是被阻塞的,好比在说:hi,内核,这会不要给我送SIGCHLD信号,等我忙完再说;
在该command执行期间,SIGINT和SIGQUIT是被忽略的,意思是进程收到这两个信号后没有任何动作。

再来看一下system()函数返回值:
The value returned is -1 on error (e.g. fork(2) failed), and the return status of the command otherwise. This latter return status is in the format specified in wait(2). Thus, the exit code of the command will be WEXITSTATUS(status). In case /bin/sh could not be executed, the exit status will be that of a command that does exit(127).
If the value of command is NULL, system() returns nonzero if the shell is available, and zero if not.
为了更好的理解system()函数返回值,需要了解其执行过程,实际上system()函数执行了三步操作:
1.fork一个子进程;
2.在子进程中调用exec函数去执行command;
3.在父进程中调用wait去等待子进程结束。
对于fork失败,system()函数返回-1。
如果exec执行成功,也即command顺利执行完毕,则返回command通过exit或return返回的值。
(注意,command顺利执行不代表执行成功,比如command:"rm debuglog.txt",不管文件存不存在该command都顺利执行了)
如果exec执行失败,也即command没有顺利执行,比如被信号中断,或者command命令根本不存在,system()函数返回127.
如果command为NULL,则system()函数返回非0值,一般为1.

看一下system()函数的源码
看完这些,我想肯定有人对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成功,则返回子进程的返回状态
}
 

仔细看完这个system()函数的简单实现,那么该函数的返回值就清晰了吧,那么什么时候system()函数返回0呢?只在command命令返回0时。

看一下该怎么监控system()函数执行状态
这里给我出的做法:
int status;
if(NULL == cmdstring) //如果cmdstring为空趁早闪退吧,尽管system()函数也能处理空指针
{
    return XXX;
}
status = system(cmdstring);
if(status < 0)
{
    printf("cmd: %s\t error: %s", cmdstring, strerror(errno)); // 这里务必要把errno信息输出或记入Log
    return XXX;
}

if(WIFEXITED(status))
{
    printf("normal termination, exit status = %d\n", WEXITSTATUS(status)); //取得cmdstring执行结果
}
else if(WIFSIGNALED(status))
{
    printf("abnormal termination,signal number =%d\n", WTERMSIG(status)); //如果cmdstring被信号中断,取得信号值
}
else if(WIFSTOPPED(status))
{
    printf("process stopped, signal number =%d\n", WSTOPSIG(status)); //如果cmdstring被信号暂停执行,取得信号值
}
 

到于取得子进程返回值的相关介绍可以参考另一篇文章:http://my.oschina.net/renhc/blog/35116

 

system()函数用起来很容易出错,返回值太多,而且返回值很容易跟command的返回值混淆。这里推荐使用popen()函数替代,关于popen()函数的简单使用也可以通过上面的链接查看。

popen()函数较于system()函数的优势在于使用简单,popen()函数只返回两个值:
成功返回子进程的status,使用WIFEXITED相关宏就可以取得command的返回结果;
失败返回-1,我们可以使用perro()函数或strerror()函数得到有用的错误信息。

这篇文章只涉及了system()函数的简单使用,还没有谈及SIGCHLD、SIGINT和SIGQUIT对system()函数的影响,事实上,之所以今天写这篇文章,是因为项目中因有人使用了system()函数而造成了很严重的事故。现像是system()函数执行时会产生一个错误:“No child processes”。

关于这个错误的分析,感兴趣的朋友可以看一下:http://my.oschina.net/renhc/blog/54582






 

分享到:
评论

相关推荐

    unix/linux下c/c++ 函数速查手册

    这个"UNIX/Linux下C/C++函数速查手册"提供了全面的C和C++函数参考,帮助开发者快速找到所需的函数信息,提升开发效率。以下是一些关键的知识点: 1. **C语言函数**: - **标准库函数**:C语言的标准库包括、、等...

    linux C/C++超有用4

    在Linux环境下进行C/C++编程是一项重要的技能,尤其对于系统级开发者和软件工程师而言。Linux提供了丰富的工具和库,使得C/C++程序员可以充分利用操作系统的能力。以下是一些关于"Linux C/C++超有用"的知识点: 1. ...

    使用C/C++实现Java的Native方法接口(JNI)/ JNI编程(C/C++) 代码实例

    这个方法不会在Java源代码中实现,而是要在C/C++代码中实现。例如: ```java public class JNICBridge { static { System.loadLibrary("jnibridge"); } public native void callFromJava(); } ``` 这里,`...

    java调用C/C++过程

    5. **编译C/C++代码**:使用C/C++编译器(如GCC)将源代码编译为动态链接库(Windows上是.dll,Linux上是.so,Mac上是.dylib)。确保链接时包含JVM的库。 6. **运行Java程序**:最后,你可以在Java程序中调用`...

    Linux-system-functions.rar_linux_linux c++_system 函数

    "Linux-system-functions.rar"这个压缩包文件显然包含了关于Linux系统函数的详细信息,这对于理解和使用Linux系统服务进行C++编程至关重要。以下是根据标题、描述以及可能包含的文件内容提炼出的一些关键知识点: 1...

    使用JNI进行混合编程:在Java中调用C/C++本地库

    JNI提供了一套接口,让Java虚拟机(JVM)能够调用本地方法,这些方法由C或C++编写,并编译成动态链接库(如Windows下的.dll或Linux下的.so文件)。JNI框架包括了Java端的本地方法声明和本地方法实现,以及C/C++端的...

    java写的一个使用jni调用c/c++的dll

    标题中的"java写的一个使用jni调用c/c++的dll"意味着我们要创建一个Java项目,该项目包含一个或多个`native`方法,这些方法的实现将在C/C++中完成。接着,我们需要使用`javah`工具生成JNI头文件,这个头文件定义了...

    JAVA如何调用dll:用JNI调用C或C++动态联接库原来如此简单

    这里的`callCppFunction`是需要在C/C++中实现的函数,`System.loadLibrary("myDll")`加载名为"myDll"的动态链接库。 3. **生成JNI头文件** 使用`javah`工具(在JDK的bin目录下)生成C/C++的头文件。例如: ``` ...

    java调用C/C++全过程

    将C/C++源代码编译为动态链接库(Windows下为`.dll`,Unix/Linux下为`.so`)。 5. **加载库文件**: 在Java程序中加载动态链接库,可以通过`System.loadLibrary()`方法指定库文件名。 ```java public class ...

    用C/C++实现Linux文件操作命令ls

    本项目旨在使用C/C++编程语言重新实现`ls`命令的核心功能,不依赖于`system`函数或`exec`系列函数。下面将详细阐述如何通过C/C++来实现这些功能。 首先,我们需要理解`ls`命令的基本用法和各个选项: 1. **-l**:...

    linux C程序中获取shell脚本输出(如获取system命令输出)

    通过以上介绍和示例代码分析,我们可以看到在Linux C程序中使用`popen()`函数来获取shell命令的输出是一种非常实用且高效的方法。它不仅可以简化代码逻辑,还能提高程序的可维护性和扩展性。对于那些需要频繁执行...

    c++获取system权限代码

    在Linux中,可以使用`setuid`和`setgid`函数来改变进程的身份,或者使用`capabilites`机制来赋予进程特定的能力。例如,以下代码将使进程具有CAP_SYS_ADMIN能力,这相当于root权限的大部分功能: ```cpp #include ...

    JNA调用c/c++库示例

    你需要创建一个类实现这个接口,并声明你想要调用的C/C++函数。例如,如果你有一个名为`mylib`的C库,其中包含一个名为`addNumbers`的函数,你可以这样定义: ```java import com.sun.jna.Library; import ...

    eclipse 开发c/c++

    C 和 C++ 语言都是世界上最流行且使用最普遍的编程语言, 因此 Eclipse 平台(Eclipse Platform)提供对 C/C++ 开发的支持一点都不足为奇。 因为 Eclipse 平台只是用于开发者工具的一个框架,它不直接支持 C/C++;它...

    linux c++ 守护线程,判断程序是否运行,不存在就启动

    在Linux中,可以使用`system()`函数或者`popen()`来执行命令行操作,例如`pgrep`命令来查找指定的进程。 ```cpp #include #include #include bool isProgramRunning(const std::string& programName) { std::...

    C++里面的清屏函数

    在探讨C++中的清屏函数之前,我们先回顾一下C语言中的清屏操作。在C语言中,`clrscr()` 函数通常用于清除屏幕上的输出,该函数来源于非标准库 `conio.h`。不过,在C++环境中,情况有所不同。由于 `conio.h` 并不是...

    JNI学习指南(java 调用C/C++接口)

    3. **编译并链接**:编译C/C++源代码,并将其链接成动态链接库(Windows下为.dll,Linux/Unix下为.so)。 4. **复制库文件**:将生成的动态链接库复制到Java程序的运行路径下。 5. **调用本地方法**:在Java程序中...

    java调用C语言编写的so库中的函数,java调用C语言编写的dll库中的函数

    本文将深入探讨这两种方法,以及如何在Java中调用C语言编写的SO(Linux下的动态链接库)和DLL(Windows下的动态链接库)中的函数。 首先,JNI是Java官方提供的原生接口,允许Java代码直接与本地代码交互。在Java中...

    c++ linux下编译方法

    在Linux环境下进行C++编程,编译是必不可少的步骤。这里我们将详细讲解如何在Linux系统中编译C++源代码,以及涉及的相关知识点。 首先,你需要一个C++编译器,通常选择的是GNU Compiler Collection (GCC) 的C++版本...

    入门C/C++小游戏--迷宫

    2. **刷新屏幕**:使用C库中的`system("cls")`(Windows)或`system("clear")`(Linux)清屏,然后重新打印迷宫地图。 3. **动画效果**:通过延时函数(如C++的`std::this_thread::sleep_for`)实现简单的动画效果...

Global site tag (gtag.js) - Google Analytics