困扰了许久的一个问题:父子进程共享文件描述符或者说父子进程传递文件描述符问题 今天终于解决了!!!因此,记录一下。
一、问题描述:
在linux系统,我有一个A程序,使用socket函数打开并绑定了本地端口假设是5555,然后A程序执行一个sh脚本文件,这个脚本文件会将B程序运行起来。结果,查询端口占用情况时,A,B两个程序都在监听5555端口。即A程序启动B程序时,端口发生了传递。我的问题是,怎样才能防止端口传递?
在Windows系统,我有一个A程序,使用socket函数打开并绑定了本地端口假设是5555,然后A程序用CreateProcess启动一个B程序。然后用任务管理器将A程序强杀,再次启动A程序,发现A程序无法正常启动了,提示端口已经被占用,无法打开。将B程序退出运行后,再启动A程序,此时A程序可以正常启动。我的问题是,怎样才能防止端口传递?
二、 解决办法:
1)创建socket时,如果是linux系统,设置FD_CLOEXEC标志位,核心代码如下:
int m_sock = socket(AF_INET, SOCK_STREAM, 0);
if (m_sock == -1)
{
printf("create socket failed:%s\n",strerror(errno));
}
::fcntl(m_sock, F_SETFD, FD_CLOEXEC);
2)创建socket时,如果是Windows系统,使用SetHandleInformation设置HANDLE_FLAG_INHERIT标志位,核心代码如下:
SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0);
3)我写了一个Windows和linux设置属性的函数,如下:
//Windows和linux
void mg_set_close_on_exec(sock_t sock) {
#if defined(_WIN32) && !defined(WINCE)
(void) SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0);
#elif defined(__unix__)
fcntl(sock, F_SETFD, FD_CLOEXEC);
#else
(void) sock;
#endif
}
三、问题扩展
注:libevent库没有处理句柄传递和文件描述符传递的问题。
SetHandleInformation函数介绍来自百度百科:
函数
SetHandleInformation函数:
[函数功能]
控制哪些子进程能继承内核对象句柄,可调用SetHandleInformation函数改变内核对象句柄的继承标志。
[函数原型声明]
BOOL WINAPI SetHandleInformation(
_In_ HANDLE hObject,
_In_ DWORD dwMask,
_In_ DWORD dwFlags
);
参数说明
第一个参数hObject标识了一个有效句柄。
第二个参数dwMask告诉函数我们想更改哪个或者哪些标志:
1\ HANDLE_FLAG_INHERIT 用CreateProcess(bInheritHandle设为TRUE)创建出来的子进程可以继承对象句柄
2\HANDLE_FLAG_PROTECT_FROM_CLOSE 无法调用CloseHandle关闭对象句柄
第三个参数dwFlags指出希望把标志设成什么。
实例
例如,要打开一个内核对象句柄的继承标志,可以像下面这样写:
SetHandleInformation( hObj, HANDLE_FLAG_INHERIT ,HANDLE_FLAG_INHERIT );
要关闭这个标志,可以像下面这样写:
SetHandleInformation( hObj , HANDLE_FLAG_INHERIT , 0)