序言

一、基本概念

由于Linux系统下“一切皆文件”,也就是Linux系统下的数据和程序都是以文件的形式存储的,所以Linux内核会提供一组操作文件的函数接口,这组函数接口也被称为系统IO

二、系统IO和标准IO的区别

标准I/O可以看成是在系统I/O的基础上封装了缓冲机制。这样可以先读写缓冲区,必要时再访问实际文件,从而减少了系统调用的次数,提高访问效率。

三、文件描述符

文件描述符本质就是一个非负整数,从内核源码角度分析,这个整数实际上是内核中的一个称为fd_array 的数组下标

打开文件时,内核产生一个指向 file{} 的指针,并将该指针放入一个位于 file_struct{} 中的数组 fd_array[] 中,而该指针所在数组的下标,就被 open() 返回给用户,所以内核把这个数组下标称为文件描述符。

文件描述符从0开始分配,每打开一个文件,就产生一个新的文件描述符。当然,用户可以重复打开同一个文件,每次打开文件都会使内核产生新的结构体,并得到不同的文件描述符。系统在每个进程开始运行时,都默认打开了一次键盘、两次屏幕,因此0、1、2描述符分别代表标准输入(stdin)、标准输出(stdout)和标准出错(stderr)三个文件(两个硬件)

image-20260623225238304

Linux常用API接口

一、打开文件 open

功能:打开文件
头文件: 
	    #include <sys/types.h>
	    #include <sys/stat.h>
	    #include <fcntl.h>
函数原型:
int open(const char *pathname, int flags); 
int open(const char *pathname, int flags, mode_t mode); 
参数分析:
@ pathname  : 要打开的文件的路径
@ flags		: 打开文件时的选项(可以多选)
 	     必选(三选一):
    	           O_RDONLY   只读
                   O_WRONLY   只写
                   O_RDWR     可读可写
         可选:
    	           O_APPEND  把文件的读写位置调整到文件末尾(追加模式)
                   O_TRUNC   如果文件已存在并且是以写入模式打开的,则截断文件为零长度
                   O_ASYNC   设置信号驱动
                   O_CREAT   如果文件不存在则自动创建文件 (当需要创建文件时,第三个参数启动mode )
				   O_EXCL	 和O_CREAT一块使用,如果文件存在则报错
                   O_NONBLOCK 使用非阻塞模式
@ mode		: 指定文件权限(只有在open函数的第二个参数flags使用 O_CREAT 或者 O_TMPFILE 才会使用)
返回值:
    	成功 返回文件的描述符(一个非负整数 文件描述符从最低数字0开始分配,从小到大)
    	失败 返回-1 并设置错误码(通过perror可得到具体的错误原因)
备注:	注意:mode参数应该采用八进制。
  • Linux系统下的文件权限

    image-20260623224951551

二、关闭文件 close

功能:关闭文件
头文件: 
	#include <unistd.h>
函数原型:
int close(int fd);
参数分析:
@ fd  : 要关闭的文件描述符
返回值:
    	成功 返回0
    	失败 返回-1 并设置错误码(通过perror可得到具体的错误原因)
备注:	无

三、文件读取 read

功能:从fd对应的文件中读取最多count个字节的数据并存储到buf指向的缓冲区中	
头文件: 
	#include <unistd.h>
函数原型:
ssize_t read(int fd, void * buf, size_t count);
参数分析:
@ fd    : 指定要读取数据的文件描述符
@ buf	: 自定义缓冲区
@ count : 要读取的数据的个数(以字节为单位)
返回值:
    	成功 返回读取的字节数量 (如果返回值是0,说明读取到文件末尾)
    	失败 返回-1 并设置错误码(通过perror可得到具体的错误原因)
备注:	无

四、文件写入 write

功能:文件写入
头文件:
	#include <unistd.h>
函数原型:
ssize_t write(int fd, const void * buf, size_t count);
参数分析:
@ fd    : 指定要写入数据的文件描述符
@ buf	: 自定义缓冲区
@ count : 要写入的数据的个数(以字节为单位)
返回值:
    	成功 返回写入的字节数量 (如果返回值是0表示写入0个数据)
    	失败 返回-1 并设置错误码(通过perror可得到具体的错误原因)
备注:	无

五、位置偏移 lseek

功能:设置文件的位置指示器的偏移量
头文件: 
	#include <sys/types.h>
    	#include <unistd.h>
函数原型:
int lseek(int fd, off_t offset, int whence);
参数分析:
@ fd 	  	: 需要设置位置偏移量的文件描述符
@ offset	: 新位置偏移量相对基准点的偏移
@ whence	: 基准点	: 
    				SEEK_SET -->文件开头处
    				SEEK_CUR -->当前位置
    				SEEK_END -->文件末尾处
返回值:
    	成功 返回文件指示器相较于文件开头的偏移量(以字节为单位)
    	失败 返回-1 并设置错误码(通过perror可得到具体的错误原因)
备注:	无

六、文件控制 ioctl

功能:文件控制 (一个专用于设备输入输出操作的系统调用,该调用传入一个跟设备有关的请求码,系统调用的功能完全取决于请求码)
头文件: 
	#include <sys/ioctl.h>
函数原型:
int ioctl(int fd, unsigened long request, ...);
参数分析:
@ fd 	  	: 要控制的文件描述符
@ request	: 和具体硬件相关的请求码,针对不同文件的各种控制命令字
@ argp		: 一个指针,指针类型由请求码决定
返回值:
    	成功 返回0
    	失败 返回-1 并设置错误码(通过perror可得到具体的错误原因)
备注:	请求码是通过宏定义的方式写入到硬件的驱动程序中,当驱动安装成功后,用户就可以通过头文件中的请求码向内核中的驱动程序发出请求,然后内核通过请求码去访问硬件,再把访问结果返回给用户。

思维碎片与问题集

练习:利用open函数测试默认情况下程序最多可以打开多少个文件。提示:同一个文件可以被多次打开。

思考:使用 ulimit命令可以知道一个程序中能打开的文件的数量最大为1024个,使用ulimit -n可以修改这个值。