从C中的系统命令开始的进程继承父fd的

时间:2011-08-04 17:27:01

标签: c linux system netstat pppd

我有一个SIP服务器的示例应用程序,它同时监听tcp和udp端口5060。 在代码中的某个时刻,我做了一个系统(“pppd file / etc / ppp / myoptions&”);

在此之后,如果我执行netstat -apn,它会告诉我端口5060也为pppd打开了! 有没有办法避免这种情况?这是Linux中系统功能的标准行为吗?

谢谢, Elison

5 个答案:

答案 0 :(得分:14)

是的,默认情况下,每当您分叉一个进程(system执行)时,子进程都会继承所有父进程的文件描述符。如果孩子不需要那些描述符,它应该关闭它们。使用system(或执行fork + exec的任何其他方法)执行此操作的方法是在所有文件描述符上设置FD_CLOEXEC标志,该标志不应由您的子进程使用。这将导致他们在任何孩子执行其他程序时自动关闭。

一般情况下,任何时候你的程序会打开任何一段长时间存在的文件描述符(例如你的例子中的一个监听套接字),哪些不应该与孩子共享,你应该这样做

fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);

在文件描述符上。


截至2016年?在修改POSIX.1时,您可以使用SOCK_CLOEXEC标志或插入套接字类型来在创建套接字时自动获得此行为:

listenfd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC, 0);
bind(listenfd, ...
listen(listemfd, ...

即使其他一些同时运行的线程进行了systemfork + exec调用,它也会被正确关闭。幸运的是,这个标志在Linux和BSD unix上已经支持了一段时间(但不幸的是OSX,但不幸)。

答案 1 :(得分:3)

您应该完全避免使用system()功能。它本质上是危险的,因为它调用shell,即使在Unicies之间也可以被篡改而且非便携。

你应该做的是fork()/exec()舞蹈。它就是这样的

if(!fork()){
     //close file descriptors
     ...

    execlp("pppd", "pppd", "file", "/etc/ppp/myoptions", NULL);
    perror("exec");
    exit(-1);
}

答案 2 :(得分:1)

是的,这是Linux中fork()的标准行为,system()已实现。

socket()调用返回的标识符是有效的文件描述符。此值可用于面向文件的函数,例如read()write()ioctl()close()

相反,每个文件描述符都是套接字,不是真的。无法使用open()打开常规文件,并将该描述符传递给bind()listen()

当您调用system()时,子进程继承与父进程相同的文件描述符。这就是子进程继承stdout(0),stdin(1)和stderr(2)的方式。如果安排打开文件描述符为0,1或2的套接字,子进程将继承该套接字作为标准I / O文件描述符之一。

您的子进程从父进程继承每个打开的文件描述符,包括您打开的套接字。

答案 3 :(得分:1)

正如其他人所说,这是程序所依赖的标准行为。

说到防止它你有几个选择。正如Dave建议的那样,首先关闭fork()之后的所有文件描述符。其次,POSIX支持使用fcntlFD_CLOEXEC在每个fd基础上设置'关闭exec'位。

但是,最后,既然你提到你在Linux上运行,那么有一系列的改变可以让你在打开东西时设置正确的位置。当然,这取决于平台。可以在http://udrepper.livejournal.com/20407.html

找到概述

这意味着您可以在套接字创建调用中使用按位或“类型”来设置SOCK_CLOEXEC标志。如果你正在运行内核2.6.27或更高版本,那就是。

答案 4 :(得分:-1)

system()复制当前进程,然后在其上启动一个子进程。 (当前进程不再存在。这可能是pppd使用5060的原因。您可以尝试fork()/exec()创建子进程并保持父进程。