从 C 中的系统命令启动的进程继承父 fd

发布于 2024-11-27 20:32:48 字数 222 浏览 4 评论 0原文

我有一个在 tcp 和 udp 端口​​ 5060 上侦听的 SIP 服务器的示例应用程序。 在代码中的某个时刻,我做了一个 system("pppd file /etc/ppp/myoptions &");

之后,如果我执行 netstat -apn,它会显示端口 5060 也为 pppd 打开! 有什么方法可以避免这种情况吗?这是 Linux 中系统功能的标准行为吗?

谢谢, 埃里森

I have a sample application of a SIP server listening on both tcp and udp ports 5060.
At some point in the code, I do a system("pppd file /etc/ppp/myoptions &");

After this if I do a netstat -apn, It shows me that ports 5060 are also opened for pppd!
Is there any method to avoid this? Is this standard behaviour of the system function in Linux?

Thanks,
Elison

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

扶醉桌前 2024-12-04 20:32:48

是的,默认情况下,每当您分叉一个进程(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 不支持)。

Yes, by default whenever you fork a process (which system does), the child inherits all the parent's file descriptors. If the child doesn't need those descriptors, it SHOULD close them. The way to do this with system (or any other method that does a fork+exec) is to set the FD_CLOEXEC flag on all file descriptors that shouldn't be used by the children of you process. This will cause them to be closed automatically whenever any child execs some other program.

In general, ANY TIME your program opens ANY KIND of file descriptor that will live for an extended period of time (such as a listen socket in your example), and which should not be shared with children, you should do

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

on the file descriptor.


As of the 2016? revision of POSIX.1, you can use the SOCK_CLOEXEC flag or'd into the type of the socket to get this behavior automatically when you create the socket:

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

which guarentees it will be closed properly even if some other simultaneously running thread does a system or fork+exec call. Fortunately, this flag has been supported for awhile on Linux and BSD unixes (but not OSX, unfortunately).

薄凉少年不暖心 2024-12-04 20:32:48

您可能应该完全避免使用 system() 函数。它本质上是危险的,因为它会调用 shell,而 shell 可以被篡改并且不可移植,即使在 Unicies 之间也是如此。

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

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

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

You should probably avoid the system() function altogether. It's inherently dangerous, in that it invokes the shell, which can be tampered with and rather non-portable, even between Unicies.

What you should do is the fork()/exec() dance. It goes something like this

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

    execlp("pppd", "pppd", "file", "/etc/ppp/myoptions", NULL);
    perror("exec");
    exit(-1);
}
公布 2024-12-04 20:32:48

是的,这是 Linux 中 fork() 的标准行为,system() 就是通过它实现的。

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

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

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

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

Yes, this is standard behavior of fork() in Linux, from which system() is implemented.

The identifier returned from the socket() call is a valid file descriptor. This value is usable with file-oriented functions such as read(), write(), ioctl(), and close().

The converse, that every file descriptor is a socket, is not true. One cannot open a regular file with open() and pass that descriptor to, e.g., bind() or listen().

When you call system() the child process inherits the same file descriptors as the parent. This is how stdout (0), stdin (1), and stderr (2) are inherited by child processes. If you arrange to open a socket with a file descriptor of 0, 1 or 2, the child process will inherit that socket as one of the standard I/O file descriptors.

Your child process is inheriting every open file descriptor from the parent, including the socket you opened.

放我走吧 2024-12-04 20:32:48

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

当谈到预防它时,您有几种选择。首先,按照 Dave 的建议,关闭 fork() 之后的所有文件描述符。其次,POSIX 支持使用 fcntlFD_CLOEXEC 在每个 fd 的基础上设置“执行时关闭”位。

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

。意味着您可以在套接字创建调用中使用按位或与“类型”来设置 SOCK_CLOEXEC 标志。前提是您运行的是 2.6.27 或更高版本的内核。

As others have stated, this is standard behavior that programs depend on.

When it comes to preventing it you have a few options. Firstly is closing all file descriptors after the fork(), as Dave suggests. Second, there is the POSIX support for using fcntl with FD_CLOEXEC to set a 'close on exec' bit on a per-fd basis.

Finally, though, since you mention you are running on Linux, there are a set of changes designed to let you set the bit right at the point of opening things. Naturally, this is platform dependent. An overview can be found at http://udrepper.livejournal.com/20407.html

What this means is that you can use a bitwise or with the 'type' in your socket creation call to set the SOCK_CLOEXEC flag. Provided you're running kernel 2.6.27 or later, that is.

z祗昰~ 2024-12-04 20:32:48

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

system() copies current process and then launch a child on top of it. (current process is no more there. that is probably why pppd uses 5060. You can try fork()/exec() to create a child process and keep parent alive.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文