《UNIX环境高级编程》笔记——3.文件IO

一.引言

  说明几个I/O函数:open、read、write、lseek和close,这些函数都是不带缓冲(不带缓冲,只调用内核的一个系统调用),这些函数不输入ISO C,是POSIX的一部分;

  多进程共享资源(包括文件)时,会有很多额外的烦恼,需要对共享资源、原子操作等概念深入理解,需要理解涉及的内核有关数据结构,这些数据结构对理解文件、共享有重要作用;

  最后介绍dup、fcntl、sync、fsync和ioctl函数。

二.文件描述符

  open或creat文件时,内核——文件描述符fd——>进程,用于read、write等函数。内核中维护fd与文件的对应关系,fd是动态的,内核会先分配最小未使用的fd。

  新进程执行时,shell会默认分配三个文件描述符,STDIN_FILENO/STDOUT_FILENO/STDERR_FILENO,一般为0/1/2,定义在<unistd.h>中。现在linux允许1个进程分配的文件描述符很多,一般不用关心最大值。

  【收获】 <unistd.h>的全称为unix standard head,unix的标准调用。

三.函数open和openat

#include <fcntl.h>
int open( const char * path, int oflag, .../*mode_t mode*/);
int openat( int fd, const char * path, int oflag, .../*mode_t mode*/);

返回值:成功,返回文件描述符fd    出错,-1,具体错误保存在errno全局变量中

只有oflag指定新建文件时,第三个参数才有效,否则没有第三个参数。ISO C用...表示后面参数的数量和类型是可变的

参数说明:

path:要打开或创建文件的名字oflag:  在<fcntl.h>---<bits/fcntl.h>---<bits/fcntl-linux.h>中定义  以下五选一,必选  O_RDONLY:只读打开  O_WRONLY:只写打开  O_RDWR:读写打开  O_EXEC:只执行,在linux里也没找到  O_SEARCH:只搜索,标准有,linux不支持    以下为可选项  O_APPEND:每次write时都追加到文件尾端  O_CLOEXEC:把FD_CLOEXEC常亮设置为文件描述符标志,3.14节说明。与fcntl()函数有关。  O_CREAT:若文件不存在,则创建它,此时需要第三个参数mode_t  O_EXCL: O_CREAT|O_EXCL,如果文件存在,返回错误;如果不存在,创建。不存在时,检测是否存在和创建变成原子操作  O_DIRECTORY:如果不是目录,出错  O_NOCTTY:如果path是终端,则不将该设备作为此进程的控制终端  O_NOFOLLOW:如果path时符号链接,则出错  O_NONBLOCK:如果path时FIFO、块设备、字符特殊文件,则本次open和后续IO操作为非阻塞方式。  O_TRUNC:若文件存在,且打开方式包含WR,则将文件长度截断为0  O_SYNQ:每次write等待物理IO完成,包括文件属性的更新,linux在fcntl时不支持此选项  O_DSYNC:每次write等待物理IO完成,但是如果该写操作不影响读取刚写入的数据,则不需要等待文件属性被更新  O_RSYNQ:linux处理方式与O_SYNC相同  O_TTY_INIT:如果打开一个还未打开的终端设备,设置非标准termios参数值。18章讨论。

mode参数,说明新建文件的权限,头文件<sys/stat.h>  S_IRUSR 用户读  S_IWUSR 用户写  S_IXUSR 用户执行   S_IRGRP 组读  S_IWGRP 组写  S_IXGRP 组执行  S_IROTH 其他读  S_IWOTH 其他写  S_IXOTH 其他执行

  组合形式:S_IRWXU/S_IRWXG/S_IRWXO

  【注意】以上宏定义都采用八进制,例如"chmod 777”时的777是8进制数据0777

openat比open多个fd,可以让线程使用相对目录打开文件,而不再是只能打开工作目录。默认1个进程中的多个线程只共享1个工作目录,所有线程都在这个工作目录里使用相对路径可能不方便。

  如果path为绝对路径,fd被忽略;

  如果path为相对路径,fd指定该相对路径的其实位置,fd是打开目录来获取的;

  如果path为相对路径,fd=AT_FDCWD,则路径名在当前工作目录中获取

四.函数creat

open支持O_CREAT以后,creat()函数基本就没有太大用了。

#include <fcntl.h>
int creat( const char * path,mode_t mode);
返回值:成功,返回只写打开的文件描述符
      出错,-1

等效: open(path, O_WRONLY|O_CREAT|O_TRUNC,mode);

五.函数close

#include <unistd.h>
int close( int fd );
返回值:若成功,返回0
      若出错,返回-1

【注意】:关闭一个文件,回什邡加在该文件上的所有记录锁;

        进程终止,内核自动关闭它所有打开的文件,很多程序因此不显式的close()文件.

六.函数lseek

  每个打开的文件都有与其关联的“当前文件偏移current file offset”,通常为非负整数,度量从文件开始处计算的字节数。

  读写一般都从当前文件偏移开始;

  open默认将偏移量设置为0,除非用O_APPEN选项。

  可调用lseek显式地设置文件偏移,lseek仅将文件偏移记录在内核中,不引起IO操作。该偏移量用于下一次读写操作。

#include <unistd.h>
off_t lseek( int fd, off_t offset, int whence);
返回值:成功,返回新的文件偏移量
      出错,-1

参数:  whence:SEEK_SET----->偏移设置为“0(头)+offset(正数)”;  whence:SEEK_CUR----->偏移设置为“当前值+offset(正负)”;   whence:SEEK_END----->偏移设置为“文件长度(尾)+offset(正负)”;

获取当前偏移,或检测当前文件是否可以设置偏移量的方法(FIFO,管道,网络套接字等不能设置偏移量):

off_t currpos;
currpos=lseek(fd,0,SEEK_CUR);

实例3_1 是否可以lseek测试

:/work/APUE/3_1$ cat example.c
/* lseek test */
#include <stdio.h>    // printf
#include <stdlib.h>    // exit
#include <unistd.h>

int main(int args, char *argv[])
{
    if( lseek(STDIN_FILENO,0,SEEK_CUR)==-1 )
        printf("Can‘t seek.\r\n");
    else
        printf("Can seek.\r\n")    ;

    exit(0);
}

:/work/APUE/3_1$ ./example < example.c    # 普通文件作为example.c的标准输入(重定向了),可以lseekCan seek.:/work/APUE/3_1$ cat example.c | ./example  # 管道过来的输入不能lseekCan‘t seek.

实例3_2 文件空洞,允许lseek到文件长度之后地方, 下次读或写时,会加大文件长度,中间未操作的地方形成“空洞”,空洞不占用磁盘空间。

七.函数read

#include <unistd.h>
ssize_t read( int fd, void *buf,size_t nbytes);
返回值:成功,读到的字节数,若到文件尾,返回0;
      出错,-1

多种情况会导致读到的字节数少于要求读的字节数:1. 没读够就到文件尾了。例如想要100bytes,但到文件尾还有30bytes,会返回30(实际读到的字节数);2. 已到文件尾,返回0(实际读到的字节数)3. 从特殊文件读,有限制:  终端设备,通常最多1行;  网络设备,缓冲机制能到导致没有那么多数据可读;  管道或FIFO,没那么多数据可读;  某些记录设备,一次最多返回1个记录;4. 读时被信号中断

read对偏移的影响:当前偏移+实际读到的字节数——>新的偏

八.函数write

#include <unistd.h>
ssize_t  write(int fd, const void *buf,size_t nbytes);
返回值:成功,实际写的字节数
      出错,-1

返回值,一般等于nbytes,否则出错,出错原因一般是磁盘满或超过文件长度限制;write与偏移:  一般文件,从当前偏移开始写;  open时用了O_APPEND参数,write时会先定位到文件尾部  write后,偏移+=实际写入的字节

九.IO的效率!!!

  上述程序,BUFFSIZE的值对效率影响比较大,太小,循环次数多,频繁read、write系统调用,效率低。以空间换时间。

  

十.文件共享!!!

  unix允许不同进程共享文件,为对共享进行说明,需要先说明内核IO相关数据结构。

  10.1数据结构

  

  以下数据结构的实例均为linux,linux遵循上述结构,但是也不完全一致。

  1.进程结构体中包含文件表,文件表中可以找到多个文件表项

  2.文件表项:内核为所有打开文件维持一张文件表,包括:

    a. 文件状态标志(读、写、添写、同步和非阻塞等);

    b. 文件当前偏移量

    c.指向该文件V节点的指针(linux没有V节点)

  3.v-node和i-node

    每个文件都有,保存在磁盘上,与文件对应,打开文件时获取的,主要包括文件的所有者、文件长度、指向文件实际数据块在磁盘所在位置的指针等

    v-node是与文件系统无关的,所以单独提出来。linux里没有v-node,而是采用“与文件系统无关的i节点”+“与文件系统有关的i节点”的方式。

  【扩展linux的数据结构】

include/linux/sched.h
struct task_struct {
    ......
    struct files_struct *files;  // 文件描述符列表    ......
}
include/linux/fdtable.h/* * Open file table structure */struct files_struct {  /*   * read mostly part   */    atomic_t count;    struct fdtable __rcu *fdt;    struct fdtable fdtab;  /*   * written part on a separate cache line in SMP   */    spinlock_t file_lock ____cacheline_aligned_in_smp;    int next_fd;    unsigned long close_on_exec_init[1];    unsigned long open_fds_init[1];    struct file __rcu * fd_array[NR_OPEN_DEFAULT];    //各文件表项};
include/linux/fs.hstruct file {    /*     * fu_list becomes invalid after file_free is called and queued via     * fu_rcuhead for RCU freeing     */    union {        struct list_head    fu_list;        struct rcu_head     fu_rcuhead;    } f_u;    struct path        f_path;#define f_dentry    f_path.dentry    struct inode        *f_inode;    /* cached value */  // i节点指针    const struct file_operations    *f_op;

    /*     * Protects f_ep_links, f_flags, f_pos vs i_size in lseek SEEK_CUR.     * Must not be taken from IRQ context.     */    spinlock_t        f_lock;#ifdef CONFIG_SMP    int            f_sb_list_cpu;#endif    atomic_long_t        f_count;    unsigned int         f_flags;  // 对应open的flag参数中的一部分    fmode_t            f_mode;       loff_t            f_pos;    // 偏移    struct fown_struct    f_owner;    const struct cred    *f_cred;    struct file_ra_state    f_ra;

    u64            f_version;#ifdef CONFIG_SECURITY    void            *f_security;#endif    /* needed for tty driver, and maybe others */    void            *private_data;

#ifdef CONFIG_EPOLL    /* Used by fs/eventpoll.c to link all the hooks to this file */    struct list_head    f_ep_links;    struct list_head    f_tfile_llink;#endif /* #ifdef CONFIG_EPOLL */    struct address_space    *f_mapping;#ifdef CONFIG_DEBUG_WRITECOUNT    unsigned long f_mnt_write_state;#endif};

  10.2 两个进程打开同一文件

  虽然是同一个文件,但是每个进程都有自己对应的文件表项,文件表项中保存着该进程对该文件的当前偏移量;

  在此说明write和lseek中关于偏移的操作:

  1. write nbytes——>该进程对应文件表项的偏移量增加nbytes——>如果偏移大于当前文件长度,则修改i节点中的当前文件长度;

  2. O_APPEND打开的文件,相应标记保存在文件表项中——>每次write,先把文件选项中的当前偏移=i节点中的文件长度

  3. lseek只改变文件表项中当前文件偏移

  可能有多个fd指向同一文件表项的情况,fork子进程时,此时与上图有点差别。文件描述符标志(task_struct)和文件状态标志(文件表项中)的作用范围不同,前者对应进程,后者应用于指向该文件表项的所有进程。

十一.原子操作

  多个进程打开同一文件,如果有write操作,可能存在已执行问题。以下为几种出问题的情况:  

 11.1. 向文件尾部写入数据

if( lseek(fd, 0,SEEK_END) < 0)  // 定位到文件尾
    err();    
if( write(fd,buf,100)!=100 )   // 写
    err();

  lseek和write是分开的,进程1 lseek定位到尾部了,但是还没写,进程2 在尾部write了,此时文件的实际变大了,进程1再写时会覆盖刚才进程2的内容,导致出错。

  解决方法1:是使用O_APPEND打开文件,每次只调用write就可以了,不用再lseek,每次都是原子的。

  解决方法2: 使用pread和pwrite,这两个函数自带偏移,就不存在先lseek在write/read的非原子操作问题了。

#include <unistd.h>
ssize_t pread( int fd, void * buf, size_t nbytes,off_t offset);
返回值:成功:读到的字节数;
      出错:-1
ssize_t pwrite( int fd, void * buf, size_t nbytes,off_t offset);
返回值:成功:写入的字节数;
      出错:-1

pread与“lseek后再read”的区别pread无法中断定位和读操作;不更新文件偏移

pwrite区别也类似。

11.2. 创建一个文件

  先open检测,再创建,也是非原子的。

  解决方法:open使用O_CREAT|O_EXCL创建。

  【注意】其实最好的方法应该还是给文件上锁,比较保险而且直观,后面会介绍。

十二.函数dup和dup2

  复制1个fd,使新的fd与原来的fd指向同一个文件表项,这种在多线程操作1个文件的场合应该有些用处。

  

  

#include <unistd.h>
/* Duplicate fd, returning a new file descriptor on the same file.  */
int dup( int fd);
/* Duplicate FD to FD2, closing FD2 and making it open on the same file. */
int dup2( int fd, int fd2);

返回值:成功:新的文件描述符    失败:-1

dup一定返回最小未使用的fd;dup2可以用fd2制定新描述符的值:  如果fd2已经打开,先关闭;  如果fd2=fd,返回fd2,不关闭  否则,fd2的FD_CLOEXEC标记被清除,fd2在进程调用exec时是打开状态

newfd = dup(1);  // 见上图

  fcntl也可以实现dup的功能

dup(fd) ~~~~ fcntl(fd,F_DUPFD,0)
dup(fd,fd2) ~~~~ close(fd2); fcntl(fd,F_DUPFD,fd2)dup2与fcntl稍有差别: dup2原子,close+fcntl不是; errno可能不同

十三.函数sync、fsync和fdatasync

  大多数磁盘操作——>缓冲区,排入队列——>晚些时候真正写入磁盘,这种方式叫延迟写。内核需要重用缓冲区写入其他内容时,原本在缓冲区的内容会实际写入磁盘。跟cpu的cache机制差不多,为了提高效率。有几个函数可以操作缓冲区与磁盘的一致性:

#include <unistd.h>
int fsync( int fd);
int fdatasync( int fd);
void sync(void);
  • sync:所有修改的块缓冲区——>写队列,然后返回,不等待写磁盘完成通常,称为update的守护进程,周期性的调用sync函数,定期flush块缓冲区;
  • fsync:只对fd一个文件有作用,且等待写磁盘完成后返回,更新“数据+属性”;
  • fdatasync:与fsync差不多,区别为只更新“数据”;

十四.函数fcntl

14.1 fcntl函数

   改变已经打开文件的属性。

#include <fcntl.h>
int fcntl( int fd , int cmd, .../*int arg*/);返回值:成功,依赖cmd     失败,-1

参数说明:cmd:  F_DUPFD:复制fd,返回未使用、>=第三个参数(int arg)、最小的描述符。      与fd共享文件表项,但有自己的一套文件描述符标志,其中FD_CLOEXEC标志被清除。  F_DUPFD_CLOEXEC:同上,区别是额外设置FD_CLOEXEC标志。  F_GETFD:返回fd的文件描述符标志,目前仅有FD_CLOEXEC  F_SETFD:使用第三个参数(int arg)设置文件描述符标志

  F_GETFL:返回fd对应的文件状态标志,是open(fd,flg,...)函数flg参数的一部分,具体标志见后面的表格  F_SETFL:将文件状态标志设置为第三个参数(int arg)的值,目前支持除了前5个外的其他标志

  F_GETOWN:返回当前接收SIGIO/SIGURG信号的进程ID和进程组ID,后面介绍。  F_SETOWN:设置接收SIGIO/SIGURG信号的进程ID和进程组ID,第三个参数,正的arg指定进程ID,负的arg指定进程组ID(arg)。

实例1,获取文件属性

example.c

/* lseek test */
#include <stdio.h>    // printf
#include <stdlib.h>    // exit
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>    // errno
#include <string.h>    // strerror
#include <sys/stat.h>    // mode

int main(int args, char *argv[])
{
    int fd;
    int flag;

    if( args < 2 ){
        printf("input pere err.\r\n");
        exit(1);
    }

    fd = atoi(argv[1]);
    if( (flag=fcntl( fd, F_GETFL )) < 0 ){
        printf("fcntl F_GETFL err.\r\n");
        exit(1);
    }

    switch(flag&O_ACCMODE){
    case     O_RDONLY:
        printf("read only.\r\n");
        break;
    case     O_WRONLY:
        printf("write only.\r\n");
        break;
    case     O_RDWR:
        printf("read & write.\r\n");
        break;
    default:
        printf("unknow access mode.\r\n");
        break;
    }

    if( flag&O_APPEND )
        printf("flag:APPEND.\r\n");

    // 其他属性就不一一写了
    exit(0);
}

运行结果::/work/APUE/3_3$./example 0 < /dev/ttyread only.#说明:先把标准输入重定向为/dev/tty文件(该文件只读),./example 0把标准输入传给测试程序,此时的0相当于/dev/tty,所以显示read only

:/work/APUE/3_3$ ./example 1 > file:/work/APUE/3_3$ cat filewrite only.#说明: 先把标准输出重定向到文件file,./example 1把标准输出传给测试程序,相当于file,注意由于已经重定位,所以信息会输出到file里。

:/work/APUE/3_3$ ./example 1 >> file:/work/APUE/3_3$ cat file write only.write only.flag:APPEND.#说明:>>追加重定位

:/work/APUE/3_3$ ./example 5 5<>file  #5<>file意思是在文件描述符5上打开文件, <>是可读可写read & write.:/work/APUE/3_3$ ./example 5 5>file   #5>file意思是在文件描述符5上打开文件, >是可写write only.:/work/APUE/3_3$ ./example 5 5<file   #5<file意思是在文件描述符5上打开文件, >是可读read only.

14.2  O_SYNC与write

  write时,只讲数据排入队列,不等到磁盘操作完成;如果在open时,使用O_SYNC,则write会等待磁盘操作完成。

  

  上表的设置O_SYNC是通过fcntl(fd,F_SETFL,arg)设置的,在linux里没有效果。

  1和2,1只有read,没有write,2是read和write,所以2的时间比1长;

  2和3,3的O_SYNC没有实际生效,所以时间没有明显增大;

  3和456,4/5/6额外调用sync函数,真正写磁盘,所以时间要长。

  4、5、6只是fdatasync(数据)和fsync(数据+属性)的区别,时间差别不大。

十五.函数ioctl

  杂货铺

  

十六./dev/fd

  /dev/fd下面的0/1/2对应STDIN/STDOUT/STDERR, 没有别的啥用处。

:/work/APUE/3_2$ ls | cat -  # -是标准输入
example
example.c
example.o
file.hole
Makefile:/work/APUE/3_2$ ls | cat /dev/fd/0  #用/dev/fd/0代替-,都为标准输入,直观一点
example
example.c
example.o
file.hole
Makefile

十七.小结

  除了熟悉本章介绍的函数原型和使用,还要掌握如下知识:

  1. 文件共享问题,熟悉内核与文件相关的数据结构,便于理解;

  2. IO效率:

  • 读写文件的buffer区大小不同,对整体效率的影响
  • 延迟写与sync的概念
时间: 2024-10-20 05:50:30

《UNIX环境高级编程》笔记——3.文件IO的相关文章

UNIX环境高级编程笔记之文件I/O

一.看图说话 一图胜过千言,看图! 二.唠一唠 在写之前,先唠几句,<UNIX环境高级编程>,简称APUE,这本书简直是本神书,像我这种小白,基本上每看完一章都是“哇”这种很吃惊的表情.其实大概三年前,那会大三,我就买了这本书,也看过一些,但好像没有留下什么印象,今天再看,依然觉得像新的一样.很大的原因我想是一直以来都在用windows(用windows做开发为什么学不到真正的技术,我想大家都懂的),当然知识结构不完整,学习能力这些就不说了.所以,对于那些致力于想在Linux下做开发的人来说,

4.28 UNIX环境高级编程 笔记

int dup(int filedes); int dup2(int filedes,int filedes2); 这两个函数都可以实现复制一个现存的文件描述符,但是dup一定返回当前可用最小文件描述符,dup2可以用filedes2参数指定新描述符数值.如果filedes2已经打开,则先将其关闭.如果filedes等于filedes2,则返回filedes2而不关闭. 这些函数返回的新文件描述符与参数参数filesdes共享同一个文件表项. 图3 执行dup之后的内核数据结构 dup2是原子操

《UNIX环境高级编程》--5 标准IO库

标准IO库 流和 FILE对象 标准IO库与文件IO区别: 标准IO库处理很多细节,如缓冲区分片.以优化的块长度执行IO等. 文件IO函数都是围绕文件描述符进行.首先打开一个文件,返回一个文件描述符:后续的文件IO操作都使用该文件描述符 标准IO库是围绕流进行的.当用标准IO库打开或者创建一个文件时,就有一个内建的流与之相关联 标准IO库的函数很多都是以 f开头,如fopen.fclose 对于ASCII字符集,一个字符用一个字节表示:对于国际字符集,一个字符可以用多个字节表示. 标准IO文件流

unix环境高级编程笔记(2)-- 文件I/O之不带缓冲的I/O

1 引言 不带缓冲区的I/O(unbuffered I/o)是指每个read 和 write 都调用内核中的一个系统调用,这些不带缓冲区的I/O 不是ISO C 的组成部分. 2 文件描述符 每个打开的文件都通过文件描述符引用,文件描述符是一个非负整数.open 和 create 函数返回的文件描述符供 read write 和 close 使用. unix中文件描述符0与标准输入相关联,文件描述符1与标准输出相关联,文件描述符2与标准出错输出相关联. 3 不带缓冲区的I/O函数 下面介绍6个常

unix环境高级编程笔记(4)—— 文件和目录(1)

1 引言 本文将描述文件系统的一些特征和文件的性质,从stat函数开始,逐个讲解stat结构的成员以了解文件的属性. 2 stat,fstat 和 lstat函数 #include <sys/stat.h> int stat(const char *restrict pathname,struct stat *restrict buf); int fstat(int filedes,struct stat *restrict buf); int lstat(const char *restri

unix环境高级编程笔记(3)-- 文件I/O(2)

本文讨论如何在多个进程间共享文件,以及所涉及的内核数据结构.然后会介绍dup,dup2,fcntl等函数的使用. 1 数据结构 内核使用三种数据结构表示打开的文件: (1)每个进程在进程表中都有一个记录项,记录项中有一张打开文件文件描述符表,每项包括: a )文件描述符标志(close_on_exec) b)指向一个文件表的指针 (2)内核为所有打开的文件维护一张文件表,每个文件表项包括: a)文件状态标志(读 写  添加  同步和非阻塞) b)  当前文件偏移量 c)  指向v节点的指针 (3

unix环境高级编程笔记(5)—— 文件和目录(2)

1 文件截短 有时我们需要把文件尾端处截去一些数据以缩短文件,将一个文件清0是一个特例.在打开文件时使用O_TRUNC标志可以做到这一点. #include <unistd.h> int truncate(const char *pathname,off_t length); int ftruncate(int filedes,off_t length); 成功返回0,出错返回-1. 如果length 小于文件长度,则使文件长度截断至length,剩下的不可访问.如果length大于文件长度,

unix环境高级编程笔记(6)—— 文件和目录(3)

这一节主要是讲目录的操作,涉及创建目录.删除目录.读取目录等 1 mkdir 和 rmdir 用mkdir创建目录,用rmdir删除目录. #include <sys/stat.h> int mkdir(const char *pathname,mode_t mode); 成功返回0,出错返回-1. 此函数创建一个新的空目录,. 和.. 自动创建,文件访问权限mode由进程的文件模式创建屏蔽字进行修改.(必须指定一个执行权限位) #include <unistd.h> int rm

《UNIX环境高级编程》---3.文件I/O

一.打开.创建文件.关闭文件 文件描述符:一个非负整数,范围是0~OPEN_MAX-1.内核用它来标识进程正在访问的文件.当进程创建时,默认为它打开了3个文件描述符,它们都链接向终端: 0: 标准输入 1: 标准输出 2: 标准错误输出 通常我们应该使用STDIN_FILENO,STDOUT_FILENO和 STDERR_FILENO来替代这三个幻数,从而提高可读性.这三个常量位于<unistd.h>中. 2. open和openat函数:打开文件 ``` #include<fcntl.

《UNIX环境高级编程》---4文件和目录

文件和目录 一. stat 结构和权限相关 四个stat函数:返回文件或者目录的信息结构: #include<sys/stat.h> int stat(const char * restrict pathname, struct stat*restrict buf); int fstat(int fd, struct stat* buf); int lstat(const char* restrict pathname,struct stat *restrict buf); int fstat