fcntl
表头文件 #include <fcntl.h>
函数定义
int fcntl(int fd , int cmd);
int fcntl(int fd,int cmd,long arg);
int fcntl(int fd,int cmd,struct flock * lock);
功能:
fcntl()用来操作文件描述符的一些特性。参数fd代表欲设置的文件描述词,参数cmd代表欲操作的指令。其中,arg是操作命令cmd的参数,fd是cmd操作的对象。
fcntl函数有5种功能:
1.复制一个现有的描述符(cmd=F_DUPFD).
2.获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD).
3.获得/设置文件状态标记(cmd=F_GETFL或F_SETFL).
4.获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN).
5.获得/设置记录锁(cmd=F_GETLK,F_SETLK或F_SETLKW).
主要介绍fcntl的第二个功能。即获取,设置close-on-exec标志位。
close-on-exec:子进程中执行exec类函数(execl,execle,execv,execve,execvp等)时,根据该标志位判断是否关闭自由句柄。
方法
获取标志位
val=fcntl(i,F_GETFD);
把标志位置为1
val |=FD_CLOEXEC;
设置标志位为1
fcntl(i,F_SETFD,val)
标志位为1时,exec中关闭资源句柄,为0时不关闭。
用处:
进程获取文件描述符最常见的方法是通过本机子例程open或create获取或者通过从父进程继承。后一种方法允许子进程同样能够访问由父进程使用的文件。文件描述符对于每个进程一般是唯一的。当用fork子例程创建某个子进程时,该子进程会获得其父进程所有文件描述符的副本,这些文件描述符在执行fork时打开。在由fcntl、dup和dup2子例程复制或拷贝某个进程时,会发生同样的复制过程。可以通过fcntl()函数来控制子进程中资源的继承。
fcntl()函数放在主进程中,system,exec等调用之前。
例如下面的函数,关闭子进程中的端口句柄。
/*
* Function Name: CloseSockFd()
*
* Description: close the handle of native socket with its port on exec
*
* Argument(s):
*
* Return(s): success:true
* failed:false
*
*Note(s):null
*/
int closeSockFd(int port)
{
struct rlimit lim;
unsigned int i,val,exit=0;
int pock;
string spock;
struct sockaddr_in serv,guest;
socklen_t serv_len = sizeof(serv);
struct stat buf;
if (getrlimit(RLIMIT_NOFILE, &lim) < 0)//获取进程的文件句柄信息
{
printf("get the fd failed");
return -1;
}
if (lim.rlim_cur == RLIM_INFINITY)
{
lim.rlim_cur = 1024;
}
for (i = 3; i < lim.rlim_cur; i++)
{
fstat(i,&buf);
if(S_ISSOCK(buf.st_mode))//判断句柄是否为socket类型
{
getsockname(i,(struct sockaddr *)&serv, &serv_len);
//获取socket信息
pock = ntohs(serv.sin_port);//获取端口号
spock = boost::lexical_cast<string> (pock);
if(pock==port)//判读是否为指定端口
{
g_log.dcLog(CommonLog::INFO, spock.c_str());
//设置此句柄的close-on-exec标志位成1
val=fcntl(i,F_GETFD);
val |=FD_CLOEXEC;
if (fcntl(i,F_SETFD,val)==-1 && errno != EBADF)
{
return false;
}
exit=1;
}
serv.sin_port=0;
}
}
if(exit==0)//判读端口是否存在
{
g_log.dcLog(CommonLog::INFO, "the port is not exit../n");
return false;
}
return true;
}
例如下面的例子:
#include <fcntl.h>
#include <stdlib.h>
int main()
{
fcntl(1,F_SETFD,FD_CLOEXEC);//关闭标准输出文件句柄
system("ls");//执行shell命令ls
exit(0);
}
运行结果
ls: write error: Bad file descriptor
即成功关闭句柄1成功。
注意:文件描述符,不仅用在文件上,也用在端口(socket()创建成功返回),管道(pipe()创建成功返回),所以,fcntl()同时控制端口,管道等资源在子进程中的使用。