在C中从系统命令开始的进程inheritance父级fd's

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

在这之后,如果我做了一个netstat-apn,它显示了端口5060也打开了pppd! 有什么方法可以避免吗? 这是在Linux系统function的标准行为?

谢谢,Elison

是的,默认情况下,当你分叉一个进程(哪个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 unixes上已经被支持了一段时间(不幸的是,不是OSX)。

你应该完全避免system()函数。 这本身就是危险的,因为它调用了可以被篡改的外壳,而不是可移植的外壳,甚至在Unicies之间。

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

 if(!fork()){ //close file descriptors ... execlp("pppd", "pppd", "file", "/etc/ppp/myoptions", NULL); perror("exec"); exit(-1); } 

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

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

反过来说,每个文件描述符都是一个套接字,是不正确的。 不能用open()打开一个常规的文件,并把这个描述符传给例如bind()listen()

当你调用system() ,子进程继承与父进程相同的文件描述符。 这是stdout (0), stdin (1)和stderr (2)如何由子进程继承。 如果打算使用文件描述符0,1或2打开一个套接字,子进程将继承该套接字作为标准I / O文件描述符之一。

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

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

当涉及到防止它有几个选项。 首先是在fork()之后关闭所有的文件描述符,就像Dave所说的那样。 其次,POSIX支持使用fcntlFD_CLOEXEC来设置每个fd的'close on exec'位。

最后,由于您提到您正在Linux上运行,因此有一系列更改旨在让您在打开某些东西的时候正确设置位置。 当然,这是平台的依赖。 可以在http://udrepper.livejournal.com/20407.html找到一个概述

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

system()复制当前进程,然后在其上启动一个子进程。 (当前进程不在那里,这可能是为什么pppd使用5060.你可以尝试fork()/exec()来创建一个子进程并保持父进程。