By default, the new file descriptor is set to remain open across
an execve(2) (i.e., the FD_CLOEXEC file descriptor flag described
in fcntl(2) is initially disabled); the O_CLOEXEC flag, described
below, can be used to change this default.
O_CLOEXEC (since Linux 2.6.23)
Enable the close-on-exec flag for the new file descriptor.
Specifying this flag permits a program to avoid additional
fcntl(2) F_SETFD operations to set the FD_CLOEXEC flag.
Note that the use of this flag is essential in some
multithreaded programs, because using a separate fcntl(2)
F_SETFD operation to set the FD_CLOEXEC flag does not
suffice to avoid race conditions where one thread opens a
file descriptor and attempts to set its close-on-exec flag
using fcntl(2) at the same time as another thread does a
fork(2) plus execve(2). Depending on the order of
execution, the race may lead to the file descriptor
returned by open() being unintentionally leaked to the
program executed by the child process created by fork(2).
(This kind of race is in principle possible for any system
call that creates a file descriptor whose close-on-exec
flag should be set, and various other Linux system calls
provide an equivalent of the O_CLOEXEC flag to deal with
this problem.)
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
using namespace std;
#define BUFFER "test"
int main(int argc, char **argv)
{
int tty_fd = -1 ;
int rv = -1 ;
struct termios options;
printf("pid: %d\n", getpid());
tty_fd = open("/dev/ttyXRUSB0",O_RDWR|O_NOCTTY|O_NDELAY | O_CLOEXEC) ; //打开串口设备
#if 1
pid_t fpid; //fpid表示fork函数返回的值
int count = 0;
fpid = fork();
if (fpid < 0){
printf("error in fork!");
}else if (fpid == 0) {
printf("i am the child process, my process id is %d\n", getpid());
char *argv[] = {"ls", "-l", NULL};
execv("/bin/ls", argv);
}
else {
printf("i am the parent process, my process id is %d\n", getpid());
count++;
}
printf("统计结果是: %d\n", count);
#endif
if(tty_fd < 0)
{
printf("open tty failed:%s\n", strerror(errno)) ;
goto cleanup ;
}
printf("open devices sucessful!\n") ;
memset(&options, 0, sizeof(options)) ;
rv = tcgetattr(tty_fd, &options); //获取原有的串口属性的配置
if(rv != 0)
{
printf("tcgetattr() failed:%s\n",strerror(errno)) ;
goto cleanup ;
}
options.c_cflag|=(CLOCAL|CREAD ); // CREAD 开启串行数据接收,CLOCAL并打开本地连接模式
options.c_cflag &=~CSIZE;// 先使用CSIZE做位屏蔽
options.c_cflag |= CS8; //设置8位数据位
options.c_cflag &= ~PARENB; //无校验位
/* 设置115200波特率 */
cfsetispeed(&options, B115200);
cfsetospeed(&options, B115200);
options.c_cflag &= ~CSTOPB;/* 设置一位停止位; */
options.c_cc[VTIME] = 0;/* 非规范模式读取时的超时时间;*/
options.c_cc[VMIN] = 0; /* 非规范模式读取时的最小字符数*/
tcflush(tty_fd ,TCIFLUSH);/* tcflush清空终端未完成的输入/输出请求及数据;TCIFLUSH表示清空正收到的数据,且不读取出来 */
if((tcsetattr(tty_fd, TCSANOW,&options))!=0)
{
printf("tcsetattr failed:%s\n", strerror(errno));
goto cleanup ;
}
while(1)
{
rv = write(tty_fd, BUFFER,strlen(BUFFER)) ;
if(rv < 0)
{
printf("Write() error:%s\n",strerror(errno)) ;
goto cleanup ;
}
sleep(3) ;
}
cleanup:
close(tty_fd) ;
return 0 ;
}
O_CLOEXEC 的意思是 close-on-exec,就是执行 (exec函数簇)的时候,关闭父进程继承的 file descriptor.。
如上述代码,不加O_CLOEXEC,执行以下命令:
fuser /dev/ttyXRUSB0
结果是父子两个进程(都打开着文件描述符)。
加O_CLOEXEC。就只有父进程有。子进程在执行execv函数的时候关闭了file descriptor.。