调用的posix_spawn时关闭所有文件句柄 [英] Close all File Handles when Calling posix_spawn

查看:911
本文介绍了调用的posix_spawn时关闭所有文件句柄的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想产卵使用的posix_spawn(...)(或非常类似的东西)进程的集合。这个函数接受类型posix_spawn_file_actions_t,这让我指定打开的文件句柄应如何处理的参数。从我可以从文档,所有文件都从调用进程继承和改良根据在posix_spawn_file_actions_t结构的信息。

I'd like to spawn a collection of processes using posix_spawn(...) (or something very similar). This function accept an argument of type posix_spawn_file_actions_t, which allows me specify how the open file handles should be treated. From what I can ascertain from the documentation, all files are inherited from the calling processes and modified according the information in the posix_spawn_file_actions_t structure.

我想所有文件由衍生的进程(除了标准输入,标准输出,和stderr)未开封。有谁知道如何做到这一点?显然,这可以在一些实施usinf的'POSIX_SPAWN_CLOEXEC_DEFAULT'产卵特征标志来完成,但是这是无法使用我的平台上。我还可以使用的fcntl(...)指定'收盘EXEC每当我打开一个文件,但我觉得更本地化的解决这个问题将是preferable。

I want all files to be unopened by the spawned process (except for stdin, stdout, and stderr). Does anyone know how to accomplish this? Apparently this can be done on some implementations usinf the 'POSIX_SPAWN_CLOEXEC_DEFAULT' spawn attribute flag, but this isn't available on my platform. I could also use fcntl(...) to specify 'close on exec' whenever I open a file, but I feel that a more localised solution to this problem would be preferable.

推荐答案

打开文件描述符处理超过的 叉() 和的 EXEC *() 使用文件的租约在多线程应用程序和/或的fcntl()锁(记录锁)是冒险的。

Open file descriptor handling over fork() and exec*() in a multithreaded application using file leases and/or fcntl() locks (record locks) is dicey.

在一般情况下, O_CLOEXEC / 的fcntl( FD,F_SETFD,FD_CLOEXEC) 选择是preferable在显式关闭的描述,为显式关闭一个描述符有一定的不良副作用。特别是,如果你对描述租约,在子进程关闭描述符将释放租约。

In general, the O_CLOEXEC/fcntl(fd, F_SETFD, FD_CLOEXEC) option is preferable over explicitly closing the descriptors, as explicitly closing a descriptor has some undesirable side effects. In particular, if you have a lease on the descriptor, closing the descriptor in the child process will release the lease.

请注意,在Linux中,的fcntl()锁不会跨叉继承();看到人2叉说明。

Note that in Linux, fcntl() locks are not inherited across a fork(); see Description in man 2 fork.

的posix_spawn() 在C库中实现,并且该文件的操作可以通过 管理​​posix_spawn_file_actions_init( ) posix_spawn_file_actions_addclose() 等等;看看手册页中的另请参见列表中。就个人而言,我不会使用这个接口,在子进程之前EXEC *关闭描述符()至少是一样简单。

posix_spawn() is implemented in the C library, and the file actions can be managed by posix_spawn_file_actions_init(), posix_spawn_file_actions_addclose() et cetera; look at the See also list in the man pages. Personally, I would not use this interface, as closing the descriptors in the child process prior to exec*() is at least as simple.

由于上述所有的,我个人preFER与 O_CLOEXEC 和/或使用的fcntl打开文件(FD,F_SETFD, FD_CLOEXEC),使所有描述符是近距离上EXEC默认。类似

Because of all of the above, I personally prefer to open files with O_CLOEXEC and/or use fcntl(fd,F_SETFD,FD_CLOEXEC) so that all descriptors are close-on-exec by default. Something like

#define _GNU_SOURCE
#define _POSIX_C_SOURCE 200809L
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/resource.h>

void set_all_close_on_exec(void)
{
    struct rlimit  rlim;
    long           max;
    int            fd;

    /* Resource limit? */
#if defined(RLIMIT_NOFILE)
    if (getrlimit(RLIMIT_NOFILE, &rlim) != 0)
        rlim.rlim_max = 0;
#elif defined(RLIMIT_OFILE)
    if (getrlimit(RLIMIT_OFILE, &rlim) != 0)
        rlim.rlim_max = 0;
#else
    /* POSIX: 8 message queues, 20 files, 8 streams */
    rlim.rlim_max = 36;
#endif

    /* Configured limit? */
#if defined(_SC_OPEN_MAX)
    max = sysconf(_SC_OPEN_MAX);
#else
    max = 36L;
#endif

    /* Use the bigger of the two. */
    if ((int)max > (int)rlim.rlim_max)
        fd = max;
    else
        fd = rlim.rlim_max;

    while (fd-->0)
        if (fd != STDIN_FILENO  &&
            fd != STDOUT_FILENO &&
            fd != STDERR_FILENO)
            fcntl(fd, F_SETFD, FD_CLOEXEC);
}

是快速设置所有打开的描述符(除了标准的)一个pretty可移植的方法来关闭-ON-EXEC;图书馆有时使用描述内部,并且不得设置 O_CLOEXEC 。在我的系统, set_all_close_on_exec() 0.25ms的需要运行;的最大值分别为4096和1024,因此它最终试图设置4093文件描述符。

is a pretty portable way to quickly set all open descriptors (except standard ones) to close-on-exec; libraries sometimes use descriptors internally, and may not set O_CLOEXEC. On my system, set_all_close_on_exec() takes 0.25ms to run; the maximums are 4096 and 1024 respectively, so it ends up trying to set 4093 file descriptors.

(注意:的fcntl(FD,F_SETFD,FD_CLOEXEC)对所有有效的描述应该会成功,而失败,的errno == EBADF 其他(无效/未用)的描述。)

(Note that fcntl(fd,F_SETFD,FD_CLOEXEC) should succeed for all valid descriptors, and fail with errno==EBADF for other (invalid/unused) descriptors.)

请注意,这是快得多简单地尝试各种标志上的所有可能的描述,而不是试图找出哪些描述符实际上是开放的。 (后者可能在Linux中通过例如的/ proc /自/ FD /

Note that it is much faster to simply try setting the flag on all possible descriptors, than to try and find out which descriptors are actually open. (The latter is possible in Linux via e.g. /proc/self/fd/.)

第二,我preFER使用一个辅助函数来创建一个控制管道的子进程,将文件描述符他们适当的地方(这并不总是简单),和叉子进程。签名通常是类似于

Second, I prefer to use a helper function to create a control pipe to the child process, move the file descriptors to their proper places (which is not always trivial), and fork the child process. The signature is usually similar to

int do_exec(pid_t *const childptr,
            const char *const cmd,
            const char *const args[],
            const int stdin_fd,
            const int stdout_fd,
            const int stderr_fd);

我的 do_exec()函数创建一个近距离上EXEC控制管道,未能执行的二进制孩子,和儿童二进制退出状态来区分。 (如果子进程无法执行exec(),把它写入错误号作为一个符号字符的控制管道。该父进程尝试读取从控制管道的另一端一个符号字符如果成功,则执行失败;母公司例如使用的 waitpid函数() ,并返回错误号错误,否则,管道被关闭,由于给exec(),所以父进程知道孩子的执行已经开始,可以关闭控制管(的最后一个开口端)。)

My do_exec() function creates a close-on-exec control pipe, to differentiate between failure to execute the child binary, and child binary exit statuses. (If the child process fails to exec(), it writes errno as a signed char to the control pipe. The parent process tries to read a single signed char from the other end of the control pipe. If that succeeds, then the exec failed; the parent reaps the child using e.g. waitpid(), and returns the errno error. Otherwise, the pipe was closed due to the exec(), so the parent process knows the child execution has started, and can close the (last open end of the) control pipe.)

最后,如果你有一个多线程的服务器类型的过程,需要产卵用最小的延迟和资源利用的新的子进程,启动连接到原有的工艺与Unix域套接字(因为你可以使用辅助消息的单一子进程转让凭证,并使用这些文件的描述),并具有子进程开始实际的孩子。这正是如阿帕奇mod_cgid最FastCGI的实现做。

Finally, if you have a multithreaded server-type process that needs to spawn new child processes with minimum latency and resource use, start a single child process connected to the original process with an Unix domain socket (because you can use ancillary messages to transfer credentials and file descriptors using those), and have that child process start the actual children. This is exactly what e.g. Apache mod_cgid and most FastCGI implementations do.

这篇关于调用的posix_spawn时关闭所有文件句柄的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆