fcntl()函数

来源:互联网 发布:org.apache.poi 编辑:程序博客网 时间:2024/04/29 03:10
 

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()同时控制端口,管道等资源在子进程中的使用。