<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Soul Of Free Loop &#187; 继承</title>
	<atom:link href="https://zohead.com/archives/tag/inherit/feed/" rel="self" type="application/rss+xml" />
	<link>https://zohead.com</link>
	<description>Uranus Zhou&#039;s Blog</description>
	<lastBuildDate>Sat, 19 Jul 2025 15:42:46 +0000</lastBuildDate>
	<language>zh-CN</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.8</generator>
	<item>
		<title>解决socket在execl之后被自动继承的问题</title>
		<link>https://zohead.com/archives/close-socket-after-execl/</link>
		<comments>https://zohead.com/archives/close-socket-after-execl/#comments</comments>
		<pubDate>Thu, 03 May 2012 17:25:12 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[execl]]></category>
		<category><![CDATA[socket]]></category>
		<category><![CDATA[描述符]]></category>
		<category><![CDATA[继承]]></category>

		<guid isPermaLink="false">http://zohead.com/?p=99</guid>
		<description><![CDATA[本文同步自：https://zohead.com/archives/close-socket-after-execl/ Linux下的进程在创建子进程（fork）后，子进程会自动继承父进程的文件描述符，这种机制在没有 execl 运行时一般都没有问题，而且也比较方便在子进程中直接使用父进程中打开的文件描述符，但如果子进程通过 execl 类函数运行新的程序时，这些继承的文件描述符却没有被关闭，导致 execl 之后的程序中也有父进程中文件描述符的拷贝，有些情况就会出现错误。 由于 socket 也在这些文件描述符范围内，本文简单说下解决 socket 被自动继承的几种方法。 首先是测试程序，头 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>本文同步自：<a href="https://zohead.com/archives/close-socket-after-execl/" target="_blank">https://zohead.com/archives/close-socket-after-execl/</a></p>
<p>Linux下的进程在创建子进程（fork）后，子进程会自动继承父进程的文件描述符，这种机制在没有 execl 运行时一般都没有问题，而且也比较方便在子进程中直接使用父进程中打开的文件描述符，但如果子进程通过 execl 类函数运行新的程序时，这些继承的文件描述符却没有被关闭，导致 execl 之后的程序中也有父进程中文件描述符的拷贝，有些情况就会出现错误。</p>
<p>由于 socket 也在这些文件描述符范围内，本文简单说下解决 socket 被自动继承的几种方法。</p>
<p>首先是测试程序，头文件就没列出来了。</p>
<pre class="brush: cpp; highlight: [11,35]; title: cat &gt; test.c; notranslate">
# ... 包含的头文件... #

int main(int argc, char ** argv)
{
	int ret = 0, sock = 0;
	struct sockaddr_in in_svr = {0};
	pid_t p_ret = 0;

	signal(SIGCHLD, SIG_IGN);

	sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (sock &lt; 0) {
		perror(&quot;create socket failed&quot;);
		return 1;
	}

	in_svr.sin_family = AF_INET;
	in_svr.sin_addr.s_addr = htonl(INADDR_ANY);
	in_svr.sin_port = htons(5555);

	ret = bind(sock, (struct sockaddr *)&amp;in_svr, sizeof(in_svr));
	if (ret != 0) {
		perror(&quot;bind failed&quot;);
		close(sock);
		return 2;
	}

	p_ret = fork();

	if (p_ret &lt; 0) {
		perror(&quot;fork failed&quot;);
		close(sock);
		return 3;
	} else if (p_ret == 0) {
		execl(&quot;/bin/sleep&quot;, &quot;sleep&quot;, &quot;10&quot;, NULL);
		exit(0);
	}

	sleep(10);
	close(sock);

	return 0;
}
</pre>
<p>父进程和子进程分别 sleep 10秒钟，方便外部查看打开的文件描述符，编译运行程序，使用 lsof 命令就可以看到子进程在 execl 之后的 sleep 进程中也打开了父进程的 socket 描述符（UDP连接），这不是我们想要的效果滴：</p>
<pre class="brush: bash; title: ; notranslate">
[root@localhost ~]# lsof -c sleep
    COMMAND     PID   USER   FD     TYPE  DEVICE SIZE/OFF   NODE      NAME
    sleep       5750  root   cwd    DIR   8,1    4096       8145      /root
    sleep       5750  root   rtd    DIR   8,1    4096       2         /
    sleep       5750  root   txt    REG   8,1    27880      456104    /bin/sleep
    sleep       5750  root   mem    REG   8,1    155696     301801    /lib64/ld-2.12.so
    sleep       5750  root   mem    REG   8,1    1912928    301802    /lib64/libc-2.12.so
    sleep       5750  root   mem    REG   8,1    99158752   391655    /usr/lib/locale/locale-archive
    sleep       5750  root   0u     CHR   136,1  0t0        4         /dev/pts/1
    sleep       5750  root   1u     CHR   136,1  0t0        4         /dev/pts/1
    sleep       5750  root   2u     CHR   136,1  0t0        4         /dev/pts/1
    sleep       5750  root   3u     IPv4  15022  0t0        UDP       *:5555
</pre>
<p>解决办法有如下几种：</p>
<ol>
<li>
<p>比较死板的方法，在子进程 fork 之后关闭所有的 socket 描述符，在 execl 之前加上：</p>
<pre class="brush: cpp; title: ; notranslate">
int fd = 0, fdtablesize = 0;

for (fd = 0, fdtablesize = getdtablesize(); fd &amp;lt; fdtablesize; fd++) {
  // close all socket handle inherited from parent process
  if (isfdtype(fd, S_IFSOCK)) {
      close(fd);
  }
}
</pre>
<p>这种处理比较彻底，即使子进程没有 execl 也会关闭继承的 socket 句柄，如果要关闭从父进程继承的所有句柄，去掉 isfdtype 判断即可。</p>
</li>
<li>
<p>使用 fcntl 为父进程 socket 设置 O_CLOEXEC 标志，表示此 socket 在 execl 之前自动关闭（注意并非在 fork 之后），在 socket 创建完成之后加上：</p>
<pre class="brush: cpp; title: ; notranslate">
ret = fcntl(sock, F_GETFD);
ret = fcntl(sock, F_SETFD, ret | FD_CLOEXEC);
</pre>
</li>
<li>
<p>Linux 2.6.27 之后创建 socket 时开始支持 SOCK_CLOEXEC 参数，与上面的效果相似，只要改创建 socket 的地方：</p>
<pre class="brush: cpp; title: ; notranslate">
sock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
</pre>
<p>另外 Linux 2.6.27 之后创建 socket 另外支持 SOCK_NONBLOCK 参数，如果设置此参数，socket 自动创建为非阻塞式。</p>
</li>
</ol>
<p>以上三种办法均在 RHEL 6.1 64位系统上编译测试通过，有任何问题欢迎交流，玩的开心 ^_^</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/close-socket-after-execl/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
