Lia*_*m M 4 c c++ linux posix file
我想使用posix_spawn(...)(或非常类似的东西)生成一系列进程.此函数接受posix_spawn_file_actions_t类型的参数,该参数允许我指定应如何处理打开的文件句柄.根据我从文档中可以确定的内容,所有文件都从调用进程继承,并根据posix_spawn_file_actions_t结构中的信息进行修改.
我希望所有文件都不被生成的进程打开(stdin,stdout和stderr除外).有谁知道如何做到这一点?显然,这可以通过'POSIX_SPAWN_CLOEXEC_DEFAULT'spawn属性标志在某些实现上完成,但这在我的平台上不可用.每当我打开文件时,我也可以使用fcntl(...)指定'close on exec',但我觉得这个问题的更局部解决方案更可取.
使用文件租约和/或锁(记录锁)在多线程应用程序上fork()和exec*()在多线程应用程序中打开文件描述符fcntl()是很冒险的.
通常,O_CLOEXEC/ fcntl(fd, F_SETFD, FD_CLOEXEC)option优于显式关闭描述符,因为显式关闭描述符会产生一些不良副作用.特别是,如果您对描述符有租约,则关闭子进程中的描述符将释放租约.
请注意,在Linux中,fcntl()锁不会跨越fork(); 参见man 2 fork中的描述.
posix_spawn()在C库中被实现,并且该文件的行为可以由被管理posix_spawn_file_actions_init(),posix_spawn_file_actions_addclose()等等; 请查看手册页中的" 另请参见"列表.就个人而言,我不会使用这个界面,因为在之前关闭子进程中的描述符exec*()至少是这么简单.
由于上述所有原因,我个人更喜欢使用O_CLOEXEC和/或使用打开文件,fcntl(fd,F_SETFD,FD_CLOEXEC)以便默认情况下所有描述符都是close-on-exec.就像是
#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);
}
Run Code Online (Sandbox Code Playgroud)
是一种非常便携的方法,可以快速将所有打开的描述符(标准除外)设置为close-on-exec; 库有时会在内部使用描述符,并且可能无法设置O_CLOEXEC.在我的系统上,set_all_close_on_exec()需要0.25ms才能运行; 最大值分别为4096和1024,因此最终尝试设置4093个文件描述符.
(注意,fcntl(fd,F_SETFD,FD_CLOEXEC)对于所有有效描述符应该成功,errno==EBADF对于其他(无效/未使用)描述符则失败.)
请注意,简单地尝试在所有可能的描述符上设置标志比尝试找出实际打开的描述符要快得多.(后者可以在Linux中通过例如/proc/self/fd/.)
其次,我更喜欢使用辅助函数来创建子进程的控制管道,将文件描述符移动到适当的位置(这并不总是微不足道的),并分叉子进程.签名通常类似于
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);
Run Code Online (Sandbox Code Playgroud)
我的do_exec()函数创建一个close-on-exec控件管道,以区分执行子二进制文件的失败和子二进制退出状态.(如果子进程失败exec(),则将其errno作为签名字符写入控制管道.父进程尝试从控制管道的另一端读取单个签名字符.如果成功,则exec失败;父进程失败子进程使用eg waitpid(),并返回errno错误.否则,管道由于exec()而关闭,因此父进程知道子进程已经开始,并且可以关闭(最后一个开放的)控制管道.)
最后,如果您有一个多线程服务器类型的进程需要以最小的延迟和资源使用生成新的子进程,请使用Unix域套接字启动连接到原始进程的单个子进程(因为您可以使用辅助消息来传输凭据和使用这些的文件描述符,并让该子进程启动实际的子进程.这正是Apache mod_cgid和大多数FastCGI实现所做的.
| 归档时间: |
|
| 查看次数: |
2404 次 |
| 最近记录: |