【问题标题】:Can I execute a shell or system call without a fork?我可以在没有 fork 的情况下执行 shell 或系统调用吗?
【发布时间】:2016-02-22 22:23:02
【问题描述】:

我有一个 TCP 服务器应用程序,它有时需要重新配置绑定的端口,方法是关闭它们,然后再打开它们。

应用程序还需要执行与它通信的外部二进制文件。目前这是使用 popen() 调用完成的。外部二进制运行时间可以跨越需要​​重新配置网络端口的时间段。

问题是,当主应用程序关闭一个端口时,它会被 popen 创建的用于运行二进制文件的“分叉”进程占用。

这是有道理的(已在 What happens when a tcp server binds and forks before doing an accept ? Which process would handle the client requests? 讨论过),但不可取,因为主应用程序随后无法重新打开端口。

这是可以使用 popen(3) 中的 FD_CLOEXEC O_CLOEXEC 的地方吗?应用程序需要 popen(3) 提供的管道作为执行二进制文件的标准输入,当 CLOEXEC 关闭其他文件句柄时该文件句柄保持打开状态。

有没有更好的方法来运行二进制文件,这不会导致分叉的进程保留一个关闭的端口?

How to fork process without inheriting handles? 上还有另一个可能相关的问题

【问题讨论】:

  • 什么是外部二进制文件,它在做什么?

标签: c linux multithreading tcp fork


【解决方案1】:

不,如果没有fork(2) 后跟一些execve(2) 代码(这是popenposix_spawnsystem 正在做的事情),您将无法启动另一个程序并从中返回。你最好避免使用popensystem 并自己明确编码pipe+fork+execve(因为你知道应该使用哪些文件描述符保留到close(2),一般在子进程中fork之后和execve之前...),见this

(每个进程和程序,除了/sbin/init和一些热插拔的东西,都是以fork + execve开始的;你的shell一直在使用fork + @ 987654356@ 对于大多数命令,除了像 cd 这样的内置命令)

fork(2) 调用可以由clone(2) 实现。

阅读一些好书,如高级 Linux 编程 书,可在线免费获得 here。另见syscalls(2)。使用(至少用于调试和理解事物)strace(1) 和您的调试器 (gdb)。研究free software libc(GNU libcmusl-libc)中popensystem的源代码,以及你的shell的源代码。

您可以几乎通过mmap(2)(和相关的munmap)调用的棘手序列来模仿execve(2),但不是完全(特别是wrt close-在执行等...)。您可能还需要调用过时的setcontext(3)(或编写等效的汇编代码)。

您可能会考虑与执行forkexecve 的专门的类似shell 的类似服务器的程序进行通信(例如,请参阅我的execicar.c 和灵感)。也许你会发现daemon(3) 很有用。

更好的选择可能是在您的应用程序中嵌入一些解​​释器(如luaguile)和/或嵌入dlopen(3) 一些plugin。缺点是错误(在解释脚本或插件中)会影响您的整个服务器。

【讨论】:

  • 谢谢,但是是否可以以其他方式执行二进制文件,或者至少在父进程关闭时阻止分叉进程接管网络端口?
  • 自己编码pipe+fork+execve,并适当地拨打close
  • 我从来没有深入思考过它,但对我来说,无法“从磁盘”启动可执行文件似乎违反直觉(因为这就是例如在 shell 中发出命令的外观)。当然,一个新进程可能必须从某个地方获取环境和其他设置,并且有父进程会很方便(强制?)。但是,所有这些都可以通过复制信息轻松完成。现代 MS Windows 是否遵循相同的“执行即分叉”范式?
  • 我不了解 Windows,但是创建进程(使用 fork)和启动可执行文件(使用 execve)之间的分离是 Unix 和 Posix 的中心思想,它是非常好的主意(因为可以在两者之间编写一些东西!)
  • @PeterA.Schneider,Windows 是不同的。它根本没有任何关于 fork() 的想法。 Nix 都是 fork,Win 不是 fork - 相反他们有 CreateProcessEx。我非常喜欢 fork。
【解决方案2】:

您绝对可以使用 close-on-exec 标志来避免新启动的进程继承打开的文件或套接字(我知道没有其他方法)。如果您使用open(2) 打开一个文件,您可以通过将O_CLOEXEC 添加到文件创建标志来设置该标志。如果文件已经打开,您可以使用fcntl() 设置它(如果您使用fopen(3) 打开文件,您可以通过fileno(3) 获取所需的文件描述符)。对于套接字,您还可以通过设置SOCK_CLOEXEC 在使用socket(2) 打开它时设置标志。设置此标志的文件不会被您的程序生成的进程继承,无论是直接通过fork + exec 还是该组合的任何其他“伪装”,例如system(3)popen(3)

【讨论】:

    猜你喜欢
    • 2019-12-26
    • 1970-01-01
    • 2014-03-23
    • 1970-01-01
    • 1970-01-01
    • 2018-11-20
    • 1970-01-01
    • 2020-08-23
    • 2018-04-29
    相关资源
    最近更新 更多