我知道fork和exec系列在执行后会保留文件描述符 . 这种行为对我的需求是可取的 . 同样来自exec手册:
exec()系列函数用新的过程映像替换当前过程映像 .
因此,如果我做对了,在处理分支然后执行后,程序需要额外的方法来了解哪些fds是开放的 .
我寻求的是简单的方法,将fds(将需要关闭的fds)传递给新流程 .
作为旁注 . 在Windows中,您只能看到进程正在使用的列表fds . 你需要低水平才能做到这一点 .
没有系统调用只提供打开文件描述符列表 . 但是,您正在解决的更大问题可以通过几种不同的方式解决 . 最典型的两种方式是:
如果孩子只处理一个客户端,那么只需从stdin(fd 0)读取并写入其stdout(fd 1);在分叉之后但在执行之前,你使用 dup2 将相应的文件描述符重新分配给数字0和1,单独留下fd 2(stderr),然后关闭所有其他(或者你事先使用 O_CLOEXEC / FD_CLOEXEC ,这样你就不必去做) . 有些Unix有一个名为 closefrom 的方便函数,它关闭所有数据值大于或等于其参数的文件描述符,但其他Unix拒绝采用它,原因我认为完全是假的 .
dup2
O_CLOEXEC
FD_CLOEXEC
closefrom
如果孩子正在与一个文件描述符中包含的套接字或其他全双工通信通道进行通信,则仍应将其指定为fds 0和1,因为这样可以正常使用 stdin 和 stdout FILE对象(最多无论如何你需要使用 recvfrom , shutdown 等,并且因为如果fds 0,1和2中的任何一个未打开,许多库会感到困惑 .
stdin
stdout
recvfrom
shutdown
该策略以最小的努力提供了良好的并行度,并允许子程序与 inetd 以及类似的这种多路复用器一起使用 .
inetd
如果孩子需要处理多个客户端,请不要将任何客户端分配为fds 0或1;而是将逗号分隔的客户端套接字列表作为命令行参数进行处理 . 孩子应该只关注那些文件描述符,并忽略所有其他文件描述符,即使它们是开放的 . (你应该尽力在分叉之后但在执行之前关闭所有不相关的fds . )
对孩子中的多个客户端进行多路复用I / O需要更多的编程;如果你需要走这条路线,我强烈建议使用某人已为你编写的异步I / O库,例如 libevent 或 libuv .
libevent
libuv
在你真的需要知道哪些fds是开放的情况而你的父母没有给你任何线索的情况下,你有一个糟糕且不可移植的选项,以及一个可怕但便携的选项 .
错误和不可移植的选项是打开并扫描目录 /proc/self/fd . 如果此目录存在,则其条目对应于进程中的打开文件描述符,其名称是十进制的描述符编号 . 这是一个糟糕的选择,因为你必须循环调用 readdir 并注意避免fd支持 DIR 句柄你're using, and it'不可移植,因为据我所知,只有Linux实现了这个特殊的目录,它可能不适用于你即使在那里 .
/proc/self/fd
readdir
DIR
可怕但可移植的选项是用 getrlimit(RLIMIT_NOFILE) 查询最大文件描述符号,然后从0循环到调用 fcntl(i, F_GETFD) 的那个号码,如果fd打开则返回非负号码,如果关闭则返回负号码(并设置errno)到EBADF) . 这适用于所有地方,但它可能非常慢,特别是如果最大文件描述符数量很大 .
getrlimit(RLIMIT_NOFILE)
fcntl(i, F_GETFD)
1 回答
没有系统调用只提供打开文件描述符列表 . 但是,您正在解决的更大问题可以通过几种不同的方式解决 . 最典型的两种方式是:
如果孩子只处理一个客户端,那么只需从stdin(fd 0)读取并写入其stdout(fd 1);在分叉之后但在执行之前,你使用
dup2
将相应的文件描述符重新分配给数字0和1,单独留下fd 2(stderr),然后关闭所有其他(或者你事先使用O_CLOEXEC
/FD_CLOEXEC
,这样你就不必去做) . 有些Unix有一个名为closefrom
的方便函数,它关闭所有数据值大于或等于其参数的文件描述符,但其他Unix拒绝采用它,原因我认为完全是假的 .如果孩子正在与一个文件描述符中包含的套接字或其他全双工通信通道进行通信,则仍应将其指定为fds 0和1,因为这样可以正常使用
stdin
和stdout
FILE对象(最多无论如何你需要使用recvfrom
,shutdown
等,并且因为如果fds 0,1和2中的任何一个未打开,许多库会感到困惑 .该策略以最小的努力提供了良好的并行度,并允许子程序与
inetd
以及类似的这种多路复用器一起使用 .如果孩子需要处理多个客户端,请不要将任何客户端分配为fds 0或1;而是将逗号分隔的客户端套接字列表作为命令行参数进行处理 . 孩子应该只关注那些文件描述符,并忽略所有其他文件描述符,即使它们是开放的 . (你应该尽力在分叉之后但在执行之前关闭所有不相关的fds . )
对孩子中的多个客户端进行多路复用I / O需要更多的编程;如果你需要走这条路线,我强烈建议使用某人已为你编写的异步I / O库,例如
libevent
或libuv
.在你真的需要知道哪些fds是开放的情况而你的父母没有给你任何线索的情况下,你有一个糟糕且不可移植的选项,以及一个可怕但便携的选项 .
错误和不可移植的选项是打开并扫描目录
/proc/self/fd
. 如果此目录存在,则其条目对应于进程中的打开文件描述符,其名称是十进制的描述符编号 . 这是一个糟糕的选择,因为你必须循环调用readdir
并注意避免fd支持DIR
句柄你're using, and it'不可移植,因为据我所知,只有Linux实现了这个特殊的目录,它可能不适用于你即使在那里 .可怕但可移植的选项是用
getrlimit(RLIMIT_NOFILE)
查询最大文件描述符号,然后从0循环到调用fcntl(i, F_GETFD)
的那个号码,如果fd打开则返回非负号码,如果关闭则返回负号码(并设置errno)到EBADF) . 这适用于所有地方,但它可能非常慢,特别是如果最大文件描述符数量很大 .