execv() 贵吗?

Is execv() expensive?

我有一个要求。我的进程必须在其代码路径之一期间 fork->exec 另一个进程。子进程运行一些检查,当某些条件为真时,它必须重新执行自身。当我在高端机器上测试时,它没有引起任何性能问题。

但是在同一个进程中再次调用 execv() 会很昂贵吗?特别是当它自己执行时?

注意:第二次没有涉及到fork()。该进程将第二次执行 execv() 本身,以在其虚拟地址 space.

中重新映射某些内容

第二次 execv() 电话并不比第一次贵。它甚至可能更便宜,因为系统可能不需要从磁盘读取程序映像,并且不需要加载任何新的动态库。

另一方面,execv() 只是在同一个程序中进行分支,成本要高得多。我无法想象我想编写一个程序来重新执行自身(不分叉)而不是仅仅调用一个函数的情况。

另一方面,"cheap"和"expensive"是相对的。除非您经常这样做,否则您实际上可能不会注意到任何差异。

execve 系统调用有点贵; 运行 它每秒超过几十次 - 或者可能是几百次 - 是不合理的(即使它可能持续几毫秒,并且在大多数情况下可能持续几分之一毫秒)。

它可能比您将用来 模仿它的对 mmap(2) (& munmap & mprotect(2)) and setcontext(3) 的十几个等效调用更快(更干净)(然后,有在执行 execve 的线程之外杀死 运行ning 线程的问题,以及附加到进程的其他资源,例如 FD_CLOEXEC-ed 文件描述符)。

(您将无法用 mmapmunmapsetcontextclose 复制 execve正在做,但你可能已经足够接近了……但这太荒谬了)

此外,execve 实用 成本还应考虑共享库的动态加载量(应在 运行ning 之前加载main,但技术上 execve 系统调用之后...)及其启动。

这个问题可能意义不大,它在很大程度上取决于机器的实际状态和 execveed executabe。我猜想 execve 一个巨大的 ELF 二进制文件(一些可执行文件可能有一个千兆字节的代码段,例如,传言说神秘的 Google 爬虫可能是一个具有十亿个 C++ 源代码行的整体程序,并且在有些时候它是静态链接的),例如有数百个共享库比 execve 长得多-在通常的 /bin/sh.

我还猜想 execve 来自具有 TB 大小地址的进程 space 比通常的 execve my zsh shell 长得多正在我的桌面上做。

execve 它自己的程序(实际上是它的一些更新版本)的一个典型原因是,在一个持久的服务器中,当服务器的二进制可执行文件被更新时。

另一个 execve 它自己的程序的原因是有一个或多或少的 "stateless" 服务器(一些静态内容的网络服务器)重新启动并重新加载它的配置文件。

更一般地说,这是一个完整的研究课题:阅读有关 dynamic software updating, application checkpointing, persistence, etc... See also the references here

在RAM中转储core(5) file: in my life, I never saw a core dump lasting more that a fraction of a second, but I did hear than on early 1990-s Cray computers, a core dump could (pathologically) last half an hour.... So I imagine that some pathological execve could last quite a long time (e.g. bringing a terabyte of code segment, using C-O-W技术是一样的;这不算 execve 时间,但它是启动程序成本的一部分;并且您还可能对许多共享库进行多次重定位。)。

附录

对于小型可执行文件(小于几兆字节),您可能负担得起每秒数百 execve,因此这在实践中并不是什么大问题。请注意,带有 lsmv 等常用命令的 shell 脚本... execve-ing 相当多(通常在一些 fork 之后)它几乎对每个命令都这样做)。如果您怀疑某些问题,您可以进行基准测试(例如 strace(1) using strace -tt -T -f....). On my desktop Debian/x86-64/Sid i7 3770K an execve of /bin/ls (by strace --T -f -tt zsh-static -c ls) takes about 250 µs (for an ELF binary executable /bin/ls of 118Kbytes which is probably already in the page cache), and for ocamlc (a binary of 1.8Mbyte) about 1.3ms ; a malloc usually takes half or a few µs ; a call to time(2) takes about 3ns (avoiding the overhead of a syscall thru vdso(7)...)