系统调用文件I/O函数(一)

C标准库中的fopen,fclose,fread,fwrite来对文件进行打开,关闭,读取,写入操作。在调用以上函数时,都会涉及到一个FILE指针,那这个FILE指针的意义是什么?
这里,首先要知道C库函数是对系统调用的一层封装,也就是说,在执行C库函数时,这些函数调用了系统提供的接口函数。上述四个函数调用的系统接口分别是open,close,read,write。
库函数与系统调用的层次关系
在这里插入图片描述

1.open、read、write、close等系统函数称为无缓冲I/O(Unbuffered I/O)函数,因为它们位于C标准库的I/O缓冲区的底层
2.用Unbuffered I/O函数每次读写都要进内核,调一个系统调用比调一个用户空间的函数要慢很多,所以在用户空间开辟I/O缓冲区还是必要的,用C标准I/O库函数就比较方便,省去了自己管理I/O缓冲区的麻烦
3.无缓冲I/O函数不仅用于读写常规文件,也用于读写设备,比如终端或网络设备。在读写设备时通常是不希望有缓冲的,例如向代表网络设备的文件写数据就是希望数据通过网络设备发送出去,而不希望只写到缓冲区里就算完事儿了,当网络设备接收到数据时应用程序也希望第一时间被通知到,所以网络编程通常直接调用Unbuffered I/O函数.
4.关于文件描述符,每个进程在Linux内核中都有一个task_struct结构体来维护进程相关的信息,称为进程描述符,而在操作系统理论中称为进程控制块(PCB)。task_struct中有一个指针指向files_struct结构体,称为文件描述符表,其中每个表项包含一个指向已打开的文件的指针,如下图所示。

在这里插入图片描述

5.文件描述符表的索引称为文件描述符(File Descriptor),用int型变量保存。当调用open打开一个文件或创建一个新文件时,内核分配一个文件描述符并返回给用户程序,该文件描述符表项中的指针指向新打开的文件。当读写int open(const char* pathname,int flags);

int open(const char* pathname,int flags,mode_t mode);文件时,用户程序把文件描述符传给read或write,内核根据文件描述符找到相应的表项,再通过表项中的指针找到相应的文件。
总结:
无缓存IO操作数据流向路径:数据——内核缓存区——磁盘
标准IO操作数据流向路径:数据——流缓存区——内核缓存区——磁盘

1.open函数

int open(const char* pathname,int flags);
int open(const char* pathname,int flags,mode_t mode);

参数说明:

  1. pathname:要打开的文件名(实际是文件路径)

  2. flags:选项如下,传入多个选项时,用“或”运算连接

     O_RDONLY:以只读方式打开文件
    
     O_WRONLY:以只写方式打开文件
    
     O_RDWR:以读写方式打开
    
     注意:以上三个选项必须指定一个且只能指定一个
    
     O_CREAT:若此文件不存在则创建它,创建时要设置文件权限mode
    
     O_APPEND:追加写
    
     注意:以下可选项可以同时指定0个或多个,和必选项按位或起来作
     flags参数。可选项有很多,这里只介绍一部分
    
  3. 若文件打开成功,返回新打开文件的文件描述符,若失败,返回-1。

2.close函数

int clode(int fd);

参数fd是要关闭的文件描述符。需要说明的是,当一个进程终止时,内核对该进程所有尚未关闭的文件描述符调用close关闭,所以即使用户程序不调用close,在终止时内核也会自动关闭它打开的所有文件。

3.read

ssize_t read(int fd, void *buf, size_t count);

参数count是请求读取的字节数,读上来的数据保存在缓冲区buf中,同时文件的当前读写位置向后移。这个读写位置和使用C标准I/O库时的读写位置有可能不同,这个读写位置是记在内核中的,而使用C标准I/O库时的读写位置是用户空间I/O缓冲区中的位置。
如果count=0:read不会读取数据,只返回0。
返回值表示0:如果已经到文件尾或是无可读取的数据。
如果返回字节数与要求读的字节数一样:则没问题。
如果小:可能读到了文件尾部或者read被信号中断了读取过程

4.write

ssize_t write(int fd, const void *buf, size_t count);

注意:读常规文件是不会阻塞的,不管读多少字节,read一定会在有限的时间内返回。从终端设备或网络读则不一定,如果从终端输入的数据没有换行符,调用read读终端设备就会阻塞,如果网络上没有接收到数据包,调用read从网络读就会阻塞

5.lseek

off_t lseek(int fd, off_t offset, int whence);

每个打开的文件都记录着当前读写位置,打开文件时读写位置是0,表示文件开头,通常读写多少个字节就会将读写位置往后移多少个字节。如果以O_APPEND方式打开,每次写操作都会在文件末尾追加数据,然后将读写位置移到新的文件末尾。偏移量允许超过文件末尾,这种情况下对该文件的下一次写操作将延长文件,中间空洞的部分读出来都是0
若lseek成功执行,则返回新的偏移量

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

whence:有三种

SEEK_SET:从文件开始计算偏移量
SEEK_CUR:从文件当前位置
SEEK_END:从文件结尾处

6.fcntl

int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);

当read终端设备为例介绍了非阻塞I/O,STDIN_FILENO在程序启动时已经被自动打开了,而我们需要在调用open时指定O_NONBLOCK标志。fcntl函数可以改变一个已打开的文件的属性,可以重新设置读、写、追加、非阻塞等标志(这些标志称为File Status Flag),而不必重新open文件。
当cmd=F_GETFL时,它的作用是取得文件描述符filedes的文件状态标志。
当cmd=F_SETFL时,它的作用是设置文件描述符filedes的文件状态标志,这时第三个参数为新的状态标志。

7.ioctl

int ioctl(int d, int request, ...);

ioctl用于向设备发控制和配置命令,有些命令也需要读写一些数据,但这些数据是不能用read/write读写的,称为Out-of-band数据。也就是说,read/write读写的数据是in-band数据,是I/O操作的主体,而ioctl命令传送的是控制信息,其中的数据是辅助的数据。例如,在串口线上收发数据通过read/write操作,而串口的波特率、校验位、停止位通过ioctl设置,A/D转换的结果
通过read读取,而A/D转换的精度和工作频率通过ioctl设置。

8.mmap

 void *mmap(void *addr, size_t len, int prot, int flag, int filedes,off_t off);
 int munmap(void *addr, size_t len);

mmap函数可以把磁盘文件的一部分直接映射到内存,这样文件中的位置直接就有对应的内存地址,对文件的读写可以直接用指针来做而不需要read/write函数。
在这里插入图片描述

  • addr参数为NULL,内核会自己在进程地址空间中选择合适的地址建立映射。如果addr不是NULL,则给内核一个提示,应该从什么地址开始映射,内核会选择addr之上的某个合适的地址开始映射。
  • 建立映射后,真正的映射首地址通过返回值可以得到。len参数是需要映射的那一部分文件的长度。
  • off参数是从文件的什么位置开始映射,必须是页大小的整数倍。
  • filedes是代表该文件的描述符。

prot参数有四种取值:

PROT_EXEC表示映射的这一段可执行,例如映射共享库
PROT_READ表示映射的这一段可读 
PROT_WRITE表示映射的这一段可写
PROT_NONE表示映射的这一段不可访问

flag参数有很多种取值,现在有两种

MAP_SHARED多个进程对同一个文件的映射是共享的,一个进程对映射的内存做了修改,另一个进程也会看到这种变化。
MAP_PRIVATE多个进程对同一个文件的映射不是共享的,一个进程对映射的内存做了修改,另一个进程并不会看到这种变化,也不会真的写到文件中去。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值