初识fcntl文件操作
2018-05-17 15:14:26 阿炯

fcntl是计算机中的一种函数,这里主要是指C语言中的头文件fcntl.h,通过fcntl可以改变已打开的文件性质,fcntl针对描述符提供控制。参数fd是被参数cmd操作的描述符,针对cmd的值,fcntl能够接受第三个参数int arg。fcntl()用来操作文件描述符的一些特性,它不仅可以施加建议性锁,还可以施加强制锁;同时fcntl还能对文件的某一记录进行上锁,也就是记录锁。

fcntl的返回值与命令有关。如果出错,所有命令都返回-1,如果成功则返回某个其他值。

函数类型

定义函数
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操作(如下面的描述)的描述符,针对cmd的值,fcntl能够接受第三个参数int arg。

参数fd
参数fd代表欲设置的文件描述符。

参数cmd
参数cmd代表打算操作的指令,有以下几种情况:
F_DUPFD用来查找大于或等于参数arg的最小且仍未使用的文件描述符,并且复制参数fd的文件描述符。执行成功则返回新复制的文件描述符。新描述符与fd共享同一文件表项,但是新描述符有它自己的一套文件描述符标志,其中FD_CLOEXEC文件描述符标志被清除。请参考dup2()。

F_GETFD取得close-on-exec旗标。若此旗标的FD_CLOEXEC位为0,代表在调用exec()相关函数时文件将不会关闭。

F_SETFD 设置close-on-exec 旗标。该旗标以参数arg 的FD_CLOEXEC位决定。

F_GETFL 取得文件描述符状态旗标,此旗标为open()的参数flags。

F_SETFL 设置文件描述符状态旗标,参数arg为新旗标,但只允许O_APPEND、O_NONBLOCK和O_ASYNC位的改变,其他位的改变将不受影响。

F_GETLK 取得文件锁定的状态。

F_SETLK 设置文件锁定的状态。此时flcok 结构的l_type 值必须是F_RDLCK、F_WRLCK或F_UNLCK。如果无法建立锁定,则返回-1,错误代码为EACCES或EAGAIN。

F_SETLKW F_SETLK 作用相同,但是无法建立锁定时,此调用会一直等到锁定动作成功为止。若在等待锁定的过程中被信号中断时,会立即返回-1,错误代码为EINTR。

上面是简述,下面将具体来说,fcntl函数功能依据cmd的值的不同而不同,参数对应功能如下:
(1)F_DUPFD
与dup函数功能一样,复制由fd指向的文件描述符,调用成功后返回新的文件描述符,与旧的文件描述符共同指向同一个文件。

(2)F_GETFD
读取文件描述符close-on-exec标志

(3)F_SETFD
将文件描述符close-on-exec标志设置为第三个参数arg的最后一位

(4)F_GETFL
获取文件打开方式的标志,标志值含义与open调用一致

(5)F_SETF
设置文件打开方式为arg指定方式,文件记录锁是fcntl函数的主要功能。

记录锁:实现只锁文件的某个部分,并且可以灵活的选择是阻塞方式还是立刻返回方式,当fcntl用于管理文件记录锁的操作时,第三个参数指向一个struct flock *lock的结构体。

参数lock指针为flock 结构指针,定义如下
struct flock{
    short_l_type;    /*锁的类型*/
    short_l_whence;  /*偏移量的起始位置:SEEK_SET,SEEK_CUR,SEEK_END*/
    off_t_l_start;     /*加锁的起始偏移*/
    off_t_l_len;    /*上锁字节*/
    pid_t_l_pid;   /*锁的属主进程ID */
};

l_type 有三种状态:
F_RDLCK 建立一个供读取用的锁定
F_WRLCK 建立一个供写入用的锁定
F_UNLCK 删除之前建立的锁定

l_whence 也有三种方式:
SEEK_SET 以文件开头为锁定的起始位置。
SEEK_CUR 以目前文件读写位置为锁定的起始位置
SEEK_END 以文件结尾为锁定的起始位置。

l_start 表示相对l_whence位置的偏移量,两者一起确定锁定区域的开始位置。l_len表示锁定区域的长度,如果为0表示从起点(由l_whence和l_start决定的开始位置)开始直到最大可能偏移量为止。即不管在后面增加多少数据都在锁的范围内。

short_l_type用来指定设置共享锁(F_RDLCK,读锁)还是互斥锁(F_WDLCK,写锁)。当short_l_type的值为F_UNLCK时,传入函数中将解锁。

每个进程可以在该字节区域上设置不同的读锁。但给定的字节上只能设置一把写锁,并且写锁存在就不能再设其他任何锁,且该写锁只能被一个进程单独使用。

这是多个进程的情况。

单个进程时,文件的一个区域上只能有一把锁,若该区域已经存在一个锁,再在该区域设置锁时,新锁会覆盖掉旧的锁,无论是写锁还时读锁。

l_whence,l_start,l_len三个变量来确定给文件上锁的区域。

l_whence确定文件内部的位置指针从哪开始,l_star确定从l_whence开始的位置的偏移量,两个变量一起确定了文件内的位置指针先所指的位置,即开始上锁的位置,然后l_len的字节数就确定了上锁的区域。

特殊的,当l_len的值为0时,则表示锁的区域从起点开始直至最大的可能位置,就是从l_whence和l_start两个变量确定的开始位置开始上锁,将开始以后的所有区域都上锁。

为了锁整个文件,我们会把l_whence,l_start,l_len都设为0。

返回值 成功返回依赖于cmd的值,若有错误则返回-1,错误原因存于errno。

(6)F_SETLK
此时fcntl函数用来设置或释放锁。当short_l_type为F_RDLCK为读锁,F_WDLCK为写锁,F_UNLCK为解锁。

如果锁被其他进程占用,则返回-1;

这种情况设的锁遇到锁被其他进程占用时,会立刻停止进程。

(7)F_SETLKW
此时也是给文件上锁,不同于F_SETLK的是,该上锁是阻塞方式。当希望设置的锁因为其他锁而被阻止设置时,该命令会等待相冲突的锁被释放。

(8)F_GETLK
第3个参数lock指向一个希望设置的锁的属性结构,如果锁能被设置,该命令并不真的设置锁,而是只修改lock的l_type为F_UNLCK,然后返回该结构体。如果存在一个或多个锁与希望设置的锁相互冲突,则fcntl返回其中的一个锁的flock结构。

再从其功能的视角进行一次总结

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).

1. cmd值的F_DUPFD:
F_DUPFD 返回一个如下描述的(文件)描述符:
·最小的大于或等于arg的一个可用的描述符
·与原始操作符一样的某对象的引用
·如果对象是文件(file)的话,则返回一个新的描述符,这个描述符与arg共享相同的偏移量(offset)
·相同的访问模式(读,写或读/写)
·相同的文件状态标志(如:两个文件描述符共享相同的状态标志)
·与新的文件描述符结合在一起的close-on-exec标志被设置成交叉式访问execve(2)的系统调用

实际上调用dup(oldfd);
等效于
fcntl(oldfd, F_DUPFD, 0);

而调用dup2(oldfd, newfd);
等效于
close(oldfd);
fcntl(oldfd, F_DUPFD, newfd);

2. cmd值的F_GETFD和F_SETFD:      
F_GETFD 取得与文件描述符fd联合的close-on-exec标志,类似FD_CLOEXEC。如果返回值和FD_CLOEXEC进行与运算结果是0的话,文件保持交叉式访问exec(),否则如果通过exec运行的话,文件将被关闭(arg 被忽略)     

F_SETFD 设置close-on-exec标志,该标志以参数arg的FD_CLOEXEC位决定,应当了解很多现存的涉及文件描述符标志的程序并不使用常数 FD_CLOEXEC,而是将此标志设置为0(系统默认,在exec时不关闭)或1(在exec时关闭)

在修改文件描述符标志或文件状态标志时必须谨慎,先要取得现在的标志值,然后按照希望修改它,最后设置新标志值。不能只是执行F_SETFD或F_SETFL命令,这样会关闭以前设置的标志位。

3. cmd值的F_GETFL和F_SETFL:
F_GETFL 取得fd的文件状态标志,如同下面的描述一样(arg被忽略),在说明open函数时,已说明
了文件状态标志。不幸的是,三个存取方式标志 (O_RDONLY , O_WRONLY , 以及O_RDWR)并不各占1位。(这三种标志的值各是0 , 1和2,由于历史原因,这三种值互斥 — 一个文件只能有这三种值之一。)因此首先必须用屏蔽字O_ACCMODE相与取得存取方式位,然后将结果与这三种值相比较。       

F_SETFL 设置给arg描述符状态标志,可以更改的几个标志是:O_APPEND,O_NONBLOCK,O_SYNC 和 O_ASYNC。而fcntl的文件状态标志总共有7个:O_RDONLY , O_WRONLY , O_RDWR , O_APPEND , O_NONBLOCK , O_SYNC和O_ASYNC

可更改的几个标志如下面的描述:
O_NONBLOCK 非阻塞I/O,如果read(2)调用没有可读取的数据,或者如果write(2)操作将阻塞,则read或write调用将返回-1和EAGAIN错误
O_APPEND 强制每次写(write)操作都添加在文件大的末尾,相当于open(2)的O_APPEND标志
O_DIRECT 最小化或去掉reading和writing的缓存影响。系统将企图避免缓存你的读或写的数据。如果不能够避免缓存,那么它将最小化已经被缓存了的数据造成的影响。如果这个标志用的不够好,将大大的降低性能
O_ASYNC 当I/O可用的时候,允许SIGIO信号发送到进程组,例如:当有数据可以读的时候

4. cmd值的F_GETOWN和F_SETOWN:
F_GETOWN 取得当前正在接收SIGIO或者SIGURG信号的进程id或进程组id,进程组id返回的是负值(arg被忽略)     
F_SETOWN 设置将接收SIGIO和SIGURG信号的进程id或进程组id,进程组id通过提供负值的arg来说明(arg绝对值的一个进程组ID),否则arg将被认为是进程id

5. cmd值的F_GETLK, F_SETLK或F_SETLKW: 获得/设置记录锁的功能,成功则返回0,若有错误则返回-1,错误原因存于errno。
F_GETLK 通过第三个参数arg(一个指向flock的结构体)取得第一个阻塞lock description指向的锁。取得的信息将覆盖传到fcntl()的flock结构的信息。如果没有发现能够阻止本次锁(flock)生成的锁,这个结构将不被改变,除非锁的类型被设置成F_UNLCK

F_SETLK 按照指向结构体flock的指针的第三个参数arg所描述的锁的信息设置或者清除一个文件的segment锁。F_SETLK被用来实现共享(或读)锁(F_RDLCK)或独占(写)锁(F_WRLCK),同样可以去掉这两种锁(F_UNLCK)。如果共享锁或独占锁不能被设置,fcntl()将立即返回EAGAIN

F_SETLKW 除了共享锁或独占锁被其他的锁阻塞这种情况外,这个命令和F_SETLK是一样的。如果共享锁或独占锁被其他的锁阻塞,进程将等待直到这个请求能够完成。当fcntl()正在等待文件的某个区域的时候捕捉到一个信号,如果这个信号没有被指定SA_RESTART, fcntl将被中断。

当一个共享锁被set到一个文件的某段的时候,其他的进程可以set共享锁到这个段或这个段的一部分。共享锁阻止任何其他进程set独占锁到这段保护区域的任何部分。如果文件描述符没有以读的访问方式打开的话,共享锁的设置请求会失败。

独占锁阻止任何其他的进程在这段保护区域任何位置设置共享锁或独占锁。如果文件描述符不是以写的访问方式打开的话,独占锁的请求会失败。

fcntl函数返回值

fcntl的返回值与命令有关。如果出错,所有命令都返回-1,如果成功则返回某个其他值。下列四个命令有特定返回值:F_DUPFD、F_GETFD、F_GETFL、F_GETOWN.第一个返回新的文件描述符,接下来的两个返回相应标志,最后一个返回一个正的进程ID或负的进程组ID。

fcntl文件锁有两种类型:建议性锁和强制性锁

建议性锁是这样规定的:每个使用上锁文件的进程都要检查是否有锁存在,当然还得尊重已有的锁。内核和系统总体上都坚持不使用建议性锁,它们依靠程序员遵守这个规定。

强制性锁是由内核执行的:当文件被上锁来进行写入操作时,在锁定该文件的进程释放该锁之前,内核会阻止任何对该文件的读或写访问,每次读或写访问都得检查锁是否存在。

系统默认fcntl都是建议性锁,强制性锁是非POSIX标准的。如果要使用强制性锁,要使整个系统可以使用强制性锁,那么得需要重新挂载文件系统,mount使用参数 -0 mand 打开强制性锁,或者关闭已加锁文件的组执行权限并且打开该文件的set-GID权限位。

建议性锁只在cooperating processes之间才有用。对cooperating process的理解是最重要的,它指的是会影响其它进程的进程或被别的进程所影响的进程,举两个例子:
(1) 我们可以同时在两个窗口中运行同一个命令,对同一个文件进行操作,那么这两个进程就是cooperating  processes
(2) cat file | sort,那么cat和sort产生的进程就是使用了pipe的cooperating processes

使用fcntl文件锁进行I/O操作必须小心:进程在开始任何I/O操作前如何去处理锁,在对文件解锁前如何完成所有的操作,是必须考虑的。如果在设置锁之前打开文件,或者读取该锁之后关闭文件,另一个进程就可能在上锁/解锁操作和打开/关闭操作之间的几分之一秒内访问该文件。当一个进程对文件加锁后,无论它是否释放所加的锁,只要文件关闭,内核都会自动释放加在文件上的建议性锁(这也是建议性锁和强制性锁的最大区别),所以不要想设置建议性锁来达到永久不让别的进程访问文件的目的(强制性锁才可以);强制性锁则对所有进程起作用。

fcntl使用三个参数 F_SETLK/F_SETLKW,F_UNLCK和F_GETLK 来分别要求、释放、测试record locks。record locks是对文件一部分而不是整个文件的锁,这种细致的控制使得进程更好地协作以共享文件资源。fcntl能够用于读取锁和写入锁,read lock也叫shared lock(共享锁),因为多个cooperating process能够在文件的同一部分建立读取锁;write lock被称为exclusive lock(排斥锁),因为任何时刻只能有一个cooperating process在文件的某部分上建立写入锁。如果cooperating processes对文件进行操作,那么它们可以同时对文件加read lock,在一个cooperating process加write lock之前,必须释放别的cooperating process加在该文件的read lock和wrtie lock,也就是说,对于文件只能有一个write lock存在,read lock和wrtie lock不能共存。

Linux的ioctl和fcntl

1. 一切都是文件
在Linux中一切设备都当作文件,也就是说一切设备都抽象成一个文件,能够提取出来一些共同的特征。文件的一些最常用的操作,如打开、读取、写入等,Linux都提供了单独的系统调用(open、read、write)供程序使用。

2. fcntl
NAME
fcntl - manipulate file descriptor

SYNOPSIS
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, … /* arg */ );

DESCRIPTION
fcntl() performs one of the operations described below on the open file descriptor fd. The operation is determined by cmd.

从上面fcntl的man手册来看,fcntl就是对打开的文件描述符进行一系列的控制操作。每一个打开的文件有很多的属性,比如文件描述符的标志(目前只有FD_CLOEXEC一个标志)、文件的状态(O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC)。fcntl主要有如下几个方面的功能:
复制文件描述符,cmd:F_DUPFD、F_DUPFD_CLOEXEC
文件描述符标志位,cmd:F_GETFD、F_SETFD
文件状态标志,cmd:F_GETFL、F_SETFL
文件建议锁,cmd:F_SETLK, F_SETLKW, and F_GETLK

上面提到的fcntl的功能大部分都是对文件描述符的一些通用属性进行操作,因为这些功能相对read、write等常用的功能不那么常用所以把这些功能都放到一个“收纳盒“fcntl中。

3. ioctl
NAME
ioctl - control device

SYNOPSIS
#include <sys/ioctl.h>
int ioctl(int d, unsigned long request, …);

DESCRIPTION
The ioctl() function manipulates the underlying device parameters of special files.

ioctl就是对设备(文件)的控制操作。上面我们知道一切设备都可以看做是文件,我们已经提到了一些对文件(设备)的通用操作,每一个设备肯定有一些不同于其它设备的功能或者属性,比如网络接口设备有IP、mask等属性,终端设备有波特率。

那么对设备的不同属性我们也提供了另外一个“收纳盒“ioctl,因为每一个设备的特性都是不同的所以不可能有一个统一的request,而且我们也不可能在kernel层面提供ioctl的具体实现,而是由每一个设备的驱动程序提供设备支持的ioctl操作集合。

在fcntl的man手册中列出了支持的所有cmd,但是在ioctl的man手册中没有列出支持的request,这是因为ioctl是每一个设备自身的实现来支持的。系统本身对一些常见的设备提供了一些ioctl支持,如网络设备的SIOCGIFNAME获取网口的名称等,除了这些常见的设备以外其它的设备的ioctl支持都需要自己在驱动中支持。

4. 总结
文件(设备)的常用操作提供了常用的read、write函数,不太常用的属性操作提供了fcntl,单独的控制功能提供了ioctl。


Linux获取文件元数据

除ls可以获取简单的元数据,获取文件详细的元数据使用系统调用函数:stat2

#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>

int stat(const char *path,struct stat *buf);

功能:获取文件的状态信息
参数:
path:指定了文件的名字包含路径
buf:用于接收文件的状态信息注意它的类型

返回值:
成功:0
失败:-1,errno被设置

文件系统中的数据分为两类,分别是数据和元数据。
数据:指的是普通文件中的实际数据;
元数据:指用来描述一个文件的特征的系统数据,诸如访问权限、文件拥有者、以及文件数据块的分布信息等等;

查看文件的元数据信息需要用到一个命令:stat,该命令的作用为显示文件的状态信息,输出的信息比ls命令输出的信息更加详细。

实例:
# stat /etc/sysconfig/network-scripts/ifcfg-eth0
...

File:文件名称;
Size:文件大小;
Blocks:占用的磁盘块数;
IO Block:IO块大小;
regular file:这里是显示文件的类型,这是一个普通文件
Device:所在设备;
Inode:Inode节点号;
Links:被链接的次数;可以使用ln命令为文件添加硬链接或者软链接
Access第一个:访问权限;
Uid:uid号和属主;
Gid:gid号和属组;
Access第二个:文件最近一次的访问时间;
Modify:文件的修改时间;
Chang:文件的改变时间。


修改文件的时间戳需要使用一个命令:touch,该命令有两个功能:
1、用于把已存在文件的时间标签更新为系统当前的时间默认方式,他们的数据将原封不动地保留下来;
2、用来创建新的空文件。

实例:
创建新的空文件:
# touch freeoa
# stat freeoa

修改文件的时间戳:
# touch freeoa
# stat freeoa
...

Access: 2018-10-25 09:56:53.831856377 +0800
Modify: 2018-10-25 09:56:53.831856377 +0800
Change: 2018-10-25 09:56:53.831856377 +0800


stat函数的用途和使用方法

stat 函数讲解

头文件:
#include <sys/stat.h>
#include <unistd.h>

定义函数:int stat(const char *file_name, struct stat *buf);
函数说明:通过文件名filename获取文件信息,并保存在buf所指的结构体stat中
返回值:执行成功则返回0,失败返回-1,错误代码存于errno

错误代码:
ENOENT    参数file_name指定的文件不存在
ENOTDIR    路径中的目录存在但却非真正的目录
ELOOP    欲打开的文件有过多符号连接问题,上限为16符号连接
EFAULT    参数buf为无效指针,指向无法存在的内存空间
EACCESS    存取文件时被拒绝
ENOMEM    核心内存不足
ENAMETOOLONG    参数file_name的路径名称太长


#include<sys/stat.h>
#include<unistd.h>
#include<stdio.h>

int main(){
    struct stat buf;
    stat("/etc/hosts", &buf);
    printf("/etc/hosts file size = %d\n", buf.st_size);
}
/*************************************************************************/
struct stat {
    dev_t        st_dev;      //文件的设备编号
    ino_t        st_ino;//节点
    mode_t        st_mode;//文件的类型和存取的权限
    nlink_t      st_nlink;//连到该文件的硬连接数目,刚建立的文件值为1
    uid_t        st_uid;//用户ID
    gid_t        st_gid;//组ID
    dev_t        st_rdev;//(设备类型)若此文件为设备文件,则为其设备编号
    off_t        st_size;//文件字节数(文件大小)
    unsigned long st_blksize;//块大小(文件系统的I/O 缓冲区大小)
    unsigned long st_blocks;//块数
    time_t        st_atime;//最后一次访问时间
    time_t        st_mtime;//最后一次修改时间
    time_t        st_ctime;//最后一次改变时间(指属性)
};

先前所描述的st_mode则定义了下列数种情况:
S_IFMT  0170000    文件类型的位遮罩
S_IFSOCK 0140000    scoket
S_IFLNK 0120000    符号连接
S_IFREG 0100000    一般文件
S_IFBLK 0060000    区块装置
S_IFDIR 0040000    目录
S_IFCHR 0020000    字符装置
S_IFIFO 0010000    先进先出
S_ISUID 04000    文件的(setuser-idonexecution)位
S_ISGID 02000    文件的(setgroup-idonexecution)位
S_ISVTX 01000    文件的sticky位

S_IRUSR(S_IREAD) 00400    文件所有者具可读取权限
S_IWUSR(S_IWRITE)00200    文件所有者具可写入权限
S_IXUSR(S_IEXEC) 00100    文件所有者具可执行权限

S_IRGRP 00040    用户组具可读取权限
S_IWGRP 00020    用户组具可写入权限
S_IXGRP 00010    用户组具可执行权限

S_IROTH 00004    其他用户具可读取权限
S_IWOTH 00002    其他用户具可写入权限
S_IXOTH 00001    其他用户具可执行权限


上述的文件类型在POSIX中定义了检查这些类型的宏定义:   
S_ISLNK (st_mode)    判断是否为符号连接
S_ISREG (st_mode)    是否为一般文件
S_ISDIR (st_mode)    是否为目录
S_ISCHR (st_mode)    是否为字符装置文件
S_ISBLK (s3e)        是否为先进先出
S_ISSOCK (st_mode)  是否为socket

若一目录具有sticky位(S_ISVTX),则表示在此目录下的文件只能被该文件所有者、此目录所有者或root来删除或改名。

struct statfs {
    long f_type;//文件系统类型
    long f_bsize;//块大小
    long f_blocks;//块多少
    long f_bfree;//空闲的块
    long f_bavail;//可用块
    long f_files;//总文件节点
    long f_ffree;//空闲文件节点
    fsid_t f_fsid;//文件系统id
    long f_namelen;//文件名的最大长度
    long f_spare[6];//spare for later
};


stat、fstat 和 lstat 函数(UNIX)

#include<sys/types.h>
#include<sys/stat.h>

int stat(const char*restrict pathname,structstat *restrict buf);
stat 提供文件名字,获取文件对应属性。感觉一般是文件没有打开的时候这样操作。

int fstat(int filedes,structstat *buf);
fstat 通过文件描述符获取文件对应的属性。文件打开后这样操作

int lstat(const char*restrict pathname,struct stat *restrict buf);
lstat 连接文件

三个函数的返回:若成功则为0,若出错则为-1。

给定一个pathname,stat函数返回一个与此命名文件有关的信息结构,fstat函数获得已在描述符filedes上打开的文件的有关信息。lstat函数类似于stat,但是当命名的文件是一个符号连接时,lstat返回该符号连接的有关信息,而不是由该符号连接引用的文件的信息。

第二个参数是个指针,它指向一个我们应提供的结构。这些函数填写由buf指向的结构。该结构的实际定义可能随实现而有所不同,但其基本形式是:
struct stat{
    mode_t st_mode;    /*file tpye &mode (permissions)*/
    ino_t st_ino;    /*i=node number (serial number)*/
    dev_t st_rdev;    /*device number for special files*/
    nlink_t st_nlink;    /*number of links*/
    uid_t    st_uid;  /*user id of owner*/
    gid_t    st_gid;  /*group ID of owner*/
    off_t  st_size;  /*size in bytes for regular files*/
    time_t st_atime;  /*time of last access*/
    time_t st_mtime;  /*time of last modification*/
    time_t st_ctime;  /*time of last file status change*/
    long st_blksize;    /*best I/O block size */
    long st_blocks;    /*number of 512-byte blocks allocated*/
};

注意,除最后两个以外,其他各成员都为基本系统数据类型。我们将说明此结构的每个成员以了解文件属性。

使用stat函数最多的可能是ls-l命令,用其可以获得有关一个文件的所有信息。

1)、函数都是获取文件普通文件,目录,管道,socket,字符,块的属性。

函数原型:
#include <sys/stat.h>

intstat(constchar*restrict pathname,structstat *restrict buf);
提供文件名字,获取文件对应属性。

intfstat(intfiledes,structstat *buf);
通过文件描述符获取文件对应的属性。

intlstat(constchar*restrict pathname,structstat *restrict buf);
连接文件描述命,获取文件属性。

2)、文件对应的属性

struct stat {
    mode_t    st_mode;    //文件对应的模式,文件,目录等       
    ino_t      st_ino;    //inode节点号       
    dev_t      st_dev;    //设备号码       
    dev_t      st_rdev;    //特殊设备号码       
    nlink_t    st_nlink;    //文件的连接数       
    uid_t      st_uid;    //文件所有者       
    gid_t      st_gid;    //文件所有者对应的组       
    off_t      st_size;    //普通文件,对应的文件字节数       
    time_t    st_atime;    //文件最后被访问的时间       
    time_t    st_mtime;    //文件内容最后被修改的时间       
    time_t    st_ctime;    //文件状态改变时间       
    blksize_t st_blksize;    //文件内容对应的块大小       
    blkcnt_t  st_blocks;    //文件内容对应的块数量
};

可以通过上面提供的函数,返回一个结构体,保存着文件的信息。



参考来源:

fcntl函数

fcntl函数详解

fcntl函数的用法总结

Linux的ioctl和fcntl