首先先回顾 apue 中对它的描述:

① 表示描述符在通过一个 exec 时仍保持有效(书P63,3.14节 fcntl 函数,在讲 F_DUPFD 时顺便提到)

② 对打开文件的处理与每个描述符的执行时关闭(close-on-exec)标志值有关。

见图 3-1 节中对 FD_CLOEXEC 的说明,进程中每个打开描述符都有一个执行时关闭标志。若此标志设置,

则在执行 exec 时关闭该描述符,否则该描述符仍打开。除非特地用 fcntl 设置了该标志,否则系统的默认

操作是在执行 exec 后仍保持这种描述符打开。(书P190,8.10节 exec 函数)

概括为:

① FD_CLOEXEC 是“文件描述符”的标志

② 此标志用来控制在执行 exec 后,是否关闭对应的文件描述符

(关闭文件描述符即不能对文件描述符指向的文件进行任何操作)

下面以一个例子进行说明,包含两个独立程序,一个用来表示父进程,另一个表示它的子进程

父进程 parent.c:

// parent.c
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h> int main()
{
int fd = open("test.txt",O_RDWR|O_APPEND); if (fd == -)
{
printf("The file test.txt open failed ! The fd = %d\n",fd);
execl( "/bin/touch", "touch", "test.txt", (char*)NULL );
return ;
}
else
{
printf("The file test.txt open success ! The fd = %d\n", fd);
} printf("fork!\n"); // 什么也不写,相当于系统默认 fcntl(fd, F_SETFD, 0) ,即用 execl 执行子进程时,
// 不打开“执行时关闭”标识位 FD_CLOEXEC,此时子进程可以向 test.txt 写入字符串
char *s="The Parent Process Writed !\n"; pid_t pid = fork();
if(pid == ) /* Child Process */
{
printf("***** exec child *****\n");
execl("child", "./child", &fd, NULL);
printf("**********************\n");
} // 等待子进程执行完毕
wait(NULL);
ssize_t writebytes = write(fd,s,strlen(s));
if ( writebytes == - )
{
printf("The Parent Process Write To fd : %d Failed !\n", fd);
} close(fd);
return ;
}
 

子进程 child.c

//child.c
#include <stdio.h>
#include <unistd.h>
#include <string.h> int main(int argc, char *argv[])
{
printf("argc = %d\n",argc); if ( argv[] == NULL )
{
printf("There is no Parameter !\n");
return ;
} int fd = *argv[];
printf("child fd = %d\n",fd); char *s = "The Child Process Writed !\n";
ssize_t writebytes = write(fd, (void *)s, strlen(s));
if ( writebytes == - )
{
printf("The Child Process Write To fd : %d Failed !\n", fd);
} close(fd);
return ;
}

此时观察 test.txt ,得到结果

The Child Process Writed !
The Parent Process Writed !

因为代码中没做任何操作,系统默认是不设置“执行时关闭标识位”的。

现在在代码中进行设置这个标志:

…………前面代码省略

printf("fork!\n");
 
fcntl(fd, F_SETFD, 1);
 
char *s="The Parent Process Writed !\n";

…………后面代码省略

此时再观察 test.txt,发现只能看到父进程的输出了:

The Parent Process Writed !

更标准的写法是:

…………前面代码省略
printf("fork!\n");
 
// 和 fcntl(fd, F_SETFD, 1) 等效,但这是标准写法,即用 FD_CLOEXEC 取代直接写1
int tFlags = fcntl(fd, F_GETFD);
fcntl(fd, F_SETFD, tFlags | FD_CLOEXEC ); char *s="The Parent Process Writed !\n";
…………后面代码省略

推荐后面一种写法。

如果在后面重新进行设置 fcntl(fd, F_SETFD, 0) ,即可重新看到子进程的输出(读者可以自己尝试)。

那么问题来了,如果子进程不使用 exec 函数执行的这种方式呢?

那么理论上设置这个标志是无效的。

// parent.c
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h> int main()
{
int fd = open("test.txt",O_RDWR|O_APPEND); if (fd == -)
{
printf("The file test.txt open failed ! The fd = %d\n",fd);
execl( "/bin/touch", "touch", "test.txt", (char*)NULL );
return ;
}
else
{
printf("The file test.txt open success ! The fd = %d\n", fd);
} printf("fork!\n"); // 系统默认 fcntl(fd, F_SETFD, 0) ,即用 execl 执行子进程时,
// 不打开“执行时关闭”标识位 FD_CLOEXEC
//fcntl(fd, F_SETFD, 1);
//fcntl(fd, F_SETFD, 0); // 和 fcntl(fd, F_SETFD, 1) 等效,但这是标准写法,即用 FD_CLOEXEC 取代直接写1
int tFlags = fcntl(fd, F_GETFD);
fcntl(fd, F_SETFD, tFlags | FD_CLOEXEC ); char *s="The Parent Process Writed !\n"; pid_t pid = fork();
if(pid == ) /* Child Process */
{
printf("***** exec child *****\n"); // execl("child", "./child", &fd, NULL);
// 注意下面,子进程不用 exec 函数,而是改成直接写入处理
// 此时文件描述符标识位 FD_CLOEXEC 不再起作用
// 即使设置这个标识位,子进程一样可以写入
char *s = "The Child Process Writed !\n";
ssize_t writebytes = write(fd, (void *)s, strlen(s));
if ( writebytes == - )
{
printf("Child Process Write To fd : %d Failed !\n", fd);
} printf("**********************\n");
// 注意这里结束子进程,但不要关闭文件描述符,否则父进程无法写入
exit();
} // 等待子进程执行完毕
wait(NULL);
ssize_t writebytes = write(fd,s,strlen(s));
if ( writebytes == - )
{
printf("The Parent Process Write To fd : %d Failed !\n", fd);
} close(fd);
return ;
}

注意修改后的地方:

if(pid == )                                        /* Child Process */
{
printf("***** exec child *****\n"); // execl("child", "./child", &fd, NULL);
// 注意下面,子进程不用 exec 函数,而是改成直接写入处理
// 此时文件描述符标识位 FD_CLOEXEC 不再起作用
// 即使设置这个标识位,子进程一样可以写入
char *s = "The Child Process Writed !\n";
ssize_t writebytes = write(fd, (void *)s, strlen(s));
if ( writebytes == - )
{
printf("The Child Process Write To fd : %d Failed !\n", fd);
} printf("**********************\n");
// 注意这里结束子进程,但不要关闭文件描述符,否则父进程无法写入
exit();
}

在前面仍然要设置标志:

int tFlags = fcntl(fd, F_GETFD);
fcntl(fd, F_SETFD, tFlags | FD_CLOEXEC );

重新编译,观察结果,发现子进程又可以重新写文件了:

The Child Process Writed !
The Parent Process Writed !

证明设置这个标志,对不用 exec 的子进程是没有影响的。

执行时关闭标识位 FD_CLOEXEC 的作用的更多相关文章

  1. 理清PHP在Linxu下执行时的文件权限

    首先推荐一个linux权限的视频:Linux权限管理之基本权限,讲的非常好,看完后就基本明白了. 一.文件权限及所属 1.文件有三种类型的权限,为了方便期间,可以用数字来代替,这样可以通过数字的加减, ...

  2. IOS -执行时 (消息传递 )

    一 函数调用概述      Objective-C不支持多重继承(同Java和Smalltalk),而C++语言支持多重继承. Objective-C是动态绑定,它的类库比C++要easy操作. Ob ...

  3. ZT fcntl设置FD_CLOEXEC标志作用

    fcntl设置FD_CLOEXEC标志作用 分类: C/C++ linux 2011-11-02 22:11 3217人阅读 评论(0) 收藏 举报 bufferexegccnullfile 通过fc ...

  4. java内存结构(执行时数据区域)

    java虚拟机规范规定的java虚拟机内存事实上就是java虚拟机执行时数据区,其架构例如以下: 当中方法区和堆是由全部线程共享的数据区. Java虚拟机栈.本地方法栈和程序计数器是线程隔离的数据区. ...

  5. SQLSERVER:大容量导入数据时保留标识值 (SQL Server)

    从MSDN上看到实现大容量导入数据时保留标识值得方法包含三种: MSDN链接地址为:https://msdn.microsoft.com/zh-cn/library/ms178129.aspx 感觉M ...

  6. iOS执行时工具-cycript

    cycript是大神saurik开发的一个很强大的工具,能够让开发人员在命令行下和应用交互,在执行时查看和改动应用.它确实能够帮助你破解一些应用,但我认为这个工具主要还是用来学习其它应用的设计(主要是 ...

  7. CentOS安装redis-audit 但执行时出错未解决 记录一下安装过程

    网上很多安装过程都太老了,测试很多方法终于成功了,但执行时还是出错,哪位熟悉的可以告知一下. yum install -y ruby rubygems ruby-devel git gcc gem s ...

  8. VC++ MFC单文档应用程序SDI下调用glGenBuffersARB(1, &pbo)方法编译通过但执行时出错原因分析及解决办法:glewInit()初始化的错误

    1.问题症状 在VC++环境下,利用MFC单文档应用程序SDI下开发OpenGL程序,当调用glGenBuffersARB(1, &pbo)方法编译通过但执行时出错,出错代码如下: OpenG ...

  9. VS2015 调试中断点突然失效的解决办法、VS调试时关闭调试让浏览器继续保留页面

    VS2010 调试中断点突然失效的解决办法 问题描述:在调试前加了断点,但debug时红色的断点变成透明的圆圈加一个感叹号,执行到该处时也不会停止. 这个问题遇到过几次了,前几次都没怎么注意,有时候是 ...

随机推荐

  1. ios framework 分离与合并多种CPU架构,分离与合并模拟器与真机

    ios  framework 分离与合并多种CPU架构,分离与合并模拟器与真机 如果你所用的framework支持真机和模拟器多种CPU架构,而你需要的是其中的一种或几种,那么可以可以从framewo ...

  2. YYYY-mm-dd HH:MM:SS

    备忘:YYYY-mm-dd HH:MM:SS部分解释 d               月中的某一天.一位数的日期没有前导零.    dd             月中的某一天.一位数的日期有一个前导零 ...

  3. Security » Authorization » 基于视图的授权

    View Based Authorization¶ 基于视图的授权 44 of 46 people found this helpful Often a developer will want to ...

  4. linux centos 安装mysql

    安装步骤 http://www.cnblogs.com/gaojupeng/p/5727069.html 下面这个报错 主要还是在 题啊加软连接的   命令出了问题 1.启动  报错 mysqld_s ...

  5. 网页边框样式与style样式部分总结

    1).border边框样式:border-style:solid 边框样式值如下: none : 无边框.与任何指定的border-width值无关 hidden : 隐藏边框.IE不支持 dotte ...

  6. javascript进阶系列专题:作用域与作用域链

    字面意思,作用域是指变量和函数的作用范围,换言之,作用域决定了变量和函数的可见性和有效时间.javascript作用域是用函数来区分,与其他语言的大括号不同. for (var i=0; i<5 ...

  7. ios基础篇(二十六)—— UITableViewCell的分组索引与标记

    一.表视图的索引目录 首先要创建一个TableView,之前有说过,这里就不详细说了(参考前面第十四篇). 直接贴代码吧, #import "ViewController.h" @ ...

  8. 动态获取R.drawable.xx资源

    String imageName = "index_fragmen"+getColor();final int resId = context.getResources().get ...

  9. CSS实现各种形状

    CSS3的一个非常酷的特性是允许我们创建各种规则和不规则形状的图形,从而可以减少图片的使用.以前只能在Photoshop等图像编辑软件中制作的复杂图形现在使用CSS3就可以完成了.通过使用新的CSS属 ...

  10. CDN 实现原理

    传统未加缓存访问过程: 用户提交域名——浏览器对域名进行解释——访问目的主机IP地址——根据IP地址发送请求——得到请求数据并回复 由此我们可以得到未加CDN缓存网站的过程为 (1) 用户向浏览器提供 ...