标准C库IO函数
- 标准C库IO和Linux系统IO
- 标准C库IO函数中,通过
fopen
方法打开一个文件时,返回的是一个文件指针FILE *fp
(是一个结构体),其包含文件描述符(整型,指向已打开的文件)、文件读写指针(读写文件过程中指针/光标的实际位置)以及I/O缓冲区(内存地址,用于寻址找到对应的内存块,通常为8192byte)。 - 数据从内存刷新到磁盘的三种情况:
- 强制刷新缓冲区:fflush
- 缓冲区已满
- 正常关闭文件: flose(), return(main函数),exit(main函数)。
- 标准C库IO是首先由用户程序进行调用,然后将数据写到缓冲区,最后调用内核函数将缓冲区的数据刷新到磁盘中。因此,标准C库IO和Linux系统IO的关系是前者调用后者的关系。
对于效率要求较高的如网络通信,则需要直接通过调用Linux的IO函数,直接将数据接收到磁盘中。
- 虚拟地址空间
- 虚拟地址空间是不存在的,通过MMU映射到实际的物理内存。
- 堆是由低地址向高地址存储,而栈则是由高地址向低地址存储。
- 内核区的资源只能通过调用系统API来使用。
进程与应用程序的区别:应用程序指可执行程序,只占用磁盘空间而不占用内存;进程是指正在运行的应用程序,当运行某个应用程序时,系统需要为该程序分配一部分计算机资源,因而进程是占用系统内存的(每个进程都拥有自己的虚拟地址空间)。
文件描述符位于内核区,由内核中的进程控制块(PCB)进行管理。当进程创建后,PCB中实际上就有一个数组,称为文件描述符表,用于存放程序所使用的文件的信息,默认大小为1024(最多同时打开1024个文件). 表中前三个位置被标准输入(
STDIN_FILENO
)、标准输出(STDOUT_FILENO
)和标准错误(STDERR_FILENO
)占用,且这三个文件默认是打开状态的,对应当前终端(计算机系统的所有资源都被抽象为文件,当前终端为/dev/tty/
)。单个文件多次打开,其文件描述符都是不一样的,只有当调用flose
时才会销毁。Linux系统IO函数
- 打开文件
- 所需头文件:
<sys/types.h>
、<sys/stat.h>
、<fcntl.h>
- 函数原型:
int open(const char *pathname, int flags);
- 函数参数:
- pathname: 目标文件路径
- flags: 对文件的操作权限设置,包括
O_RDONLY
,O_WRONLY
, orO_RDWR
, 三种模式是互斥的. - 返回值: 返回一个新的文件描述符,如果调用失败则返回
-1
,并设置errno
.
- 所需头文件:
errno: 属于Linux系统函数库,是一个全局变量,记录最近的错误号
根据errno打印相关的错误信息的函数原型为:void perror(const char *s);
参数*s
为所要显示的错误操作的字符串信息,其位于<stdio.h>
中.
- 创建文件:
- 所需头文件:
<sys/types.h>
、<sys/stat.h>
、<fcntl.h>
- 函数原型:
int open(const char *pathname, int flags, mode_t mode);
- 函数参数:
pathname
:创建文件的路径/文件名flags
: 对文件的操作权限设置(读写权限)- 必选:
O_RDONLY
,O_WRONLY
, orO_RDWR
, 三种模式是互斥的 - 可选:
O_CREAT
: 创建文件O_CLOEXEC, O_DIRECTORY, O_EXCL, O_NOCTTY, O_NOFOLLOW, O_TMPFILE, and O_TRUNC
- 备注:必选和可选用或操作取完整的标志位
- 必选:
mode
: 八进制数,表示创建出来的文件的操作权限,最终的权限为(mode & ~umax),umax的作用是抹去某些权限,可以进行修改。
- 所需头文件:
- 关闭文件
- 所需头文件:
<unistd.h>
- 函数原型:
int close(int fd);
- 函数参数:
fd
为文件标识符,通过open
函数获得。
- 所需头文件:
- 读文件
- 所需头文件:
<unistd.h>
- 函数原型:
ssize_t read(int fd, void *buf, size_t count);
- 函数参数:
- fd: 文件描述符,通过open获取
- buf: 缓冲区(需要读取的数据要存放的地址)
- count: 指定数组的大小
- 所需头文件:
- 写文件
- 所需头文件:
<unistd.h>
- 函数原型:
ssize_t write(int fd, const void *buf, size_t count);
- 函数参数
- fd: 文件描述符,通过open获取
- buf: 缓冲区(待写入的数据存放的地址)
- count: 指定数组的大小
- 返回值:写入的字节数,可能小于
count
;如果写入失败,则返回-1
并设置errno
- 所需头文件:
- 移动文件指针(文件中的光标)
- 所需头文件:
<sys/types.h>
、<unistd.h>
- 函数原型:
off_t lseek(int fd, off_t offset, int whence);
- 函数参数
- fd: 文件描述符,通过open获取
- offset: 偏移量
- whence:
- SEEK_SET: 设置文件指针偏移量
- SEEK_CUR: 当前位置+offset的值
- SEEK_END:文件大小+offset的值
- 返回值:文件指针的位置
- 所需头文件:
举例:
- 将指针移到开头:lseek(fd, 0, SEEK_SET);
- 获取当前指针位置:lseek(fd, 0, SEEK_CUR);
- 获取文件长度:lseek(fd, 0, SEEK_END);
- 拓展文件长度:lseek(fd, 100, SEEK_END);
- 获取文件相关的信息
- 所需头文件:
<sys/types.h>
、<sys/stat.h>
、<unistd.h>
- 函数原型:
int stat(const char *pathname, struct stat *statbuf);
- 函数参数
- pathname: 文件路径/文件名
- statbuf:结构体,用于保存获取到的信息
- 所需头文件:
- 获取软连接文件相关的信息(不是软连接指向的文件,可以理解为某个指针指向某个数组,这里求的实际是指针的大小,而上边那个则是求数组的大小)
- 所需头文件:
<sys/types.h>
、<sys/stat.h>
、<unistd.h>
- 函数原型:
int stat(const char *pathname, struct stat *statbuf);
- 函数参数
- pathname: 文件路径/文件名
- statbuf:结构体,用于保存获取到的信息
- 所需头文件:
- 文件属性操作函数
- 判文件权限/存在
- 所需头文件:
<unistd.h>
- 函数原型:
int access(const char *pathname, int mode);
- 函数参数:
- pathname: 文件路径/文件名
- mode:
R_OK
:是否有读权限;W_OK
:是否有写权限;X_OK
:是否有执行权限;F_OK
:文件是否存在
- 所需头文件:
- 修改文件权限
- 所需头文件:
<sys/stat.h>
- 函数原型:
int chmod(const char *pathname, mode_t mode);
- 函数参数:
- pathname: 所要修改的文件的文件路径/文件名
- mode:需要修改的权限值(八位数)
- 所需头文件:
- 修改文件用户id和组id
- 所需头文件:
<unistd.h>
- 函数原型:
int chown(const char *pathname, uid_t owner, gid_t group);
- 函数参数:
- pathname: 所要修改的文件的文件路径/文件名
- mode:需要修改的权限值(八位数)
- 所需头文件:
- 缩减或拓展文件尺寸
- 所需头文件:
<unistd.h>
、<sys/stat.h>
- 函数原型:
int truncate(const char *path, off_t length);
- 函数参数:
- path:需要修改的文件的路径
- length: 需要的最终大小(字节数)
- 所需头文件:
- 目录操作函数
- 创建目录
- 所需头文件:
<sys/stat.h>
、<sys/types.h>
- 函数原型:
int mkdir(const char *pathname, mode_t mode);
- 函数参数:
- pathname:需要修改的文件的路径
- mode: 权限,八进制数(必须要有执行权限才可打开该文件)
- 所需头文件:
- 删除空文件
- 所需头文件:
<unistd.h>
- 函数原型:
int rmdir(const char *pathname);
- 函数参数:
- pathname:待删除空目录
- 所需头文件:
- 目录重命名
- 所需头文件:
<stdio.h>
- 函数原型:
int rename(const char *oldpath, const char *newpath);
- 函数参数:
- path:需要修改的文件的路径
- length: 需要的最终大小(字节数)
- 所需头文件:
- 更改当前目录: 修改进程的工作目录(默认在当前的工作路径,即可执行程序所在目录)
- 所需头文件:
<unistd.h>
- 函数原型:
int chdir(const char *path);
,int fchdir(int fd);
- 函数参数:
- path:需要修改的工作目录
- 所需头文件:
- 获取当前工作
- 所需头文件:
<unistd.h>
- 函数原型:
char *getcwd(char *buf, size_t size);
- 函数参数:
- buf:用于保存工作路径,指向一个数组(传出参数)
- size: 指定数组大小
- 返回值:返回指向的一块内存,其实就是buf
- 所需头文件:
- 目录遍历函数
- 打开目录
- 所需头文件:
<sys/types.h>
、<dirent.h>
- 函数原型:
DIR *opendir(const char *name);
- 函数参数:
- name:所要打开的目录
- 返回值:DIR *类型,文件目录流,指向目录流中的第一个条目(可能是下一级文件目录,也可能是普通文件),若错误则返回NULL
fdopendir()
功能类似,但是不通过目录名来打开,而是通过open()
函数获取文件标识符,然后通过文件标识符fd
来打开目录。但注意,不应该继续在应用中使用该fd。
- 所需头文件:
读取目录中的数据
- 所需头文件:
<dirent.h>
- 函数原型:
struct dirent *readdir(DIR *dirp);
- 函数参数:
- dirp:待删除空目录,通过opendir获取
- 返回值:struct dirent结构体指针,指向目录流中下一个目录条目,读取失败或者末尾则返回NULL。
结构定义为:
struct dirent { ino_t d_ino; /* 此目录进入点的inode */ off_t d_off; /* 目录文件开头至此目录进入点的偏移 */ unsigned short d_reclen; /* d_name的长度,不包括空字符 */ unsigned char d_type; /* d_name所指的文件类型 */ char d_name[256]; /* 文件名 */ }; 其中文件类型包括: DT_BLK(块设备)、DT_CHR(字符设备)、DT_DIR(目录)、DT_FIFO(管道)、 DT_LNK(软连接)、DT_REG(普通文件)、DT_SOCK(套接字)、DT_UNKNOWN(未知类型)
- 所需头文件:
关闭目录
- 所需头文件:
<sys/types.h>
、<dirent.h>
- 函数原型:
int closedir(DIR *dirp);
- 函数参数:
- dirp:DIR *类型,可以理解为目录流
- 所需头文件:
- 文件描述符相关函数
- 复制文件描述符:用文件描述符表中最小可用位置来拷贝目标文件描述符
- 所需头文件:
<unistd.h>
- 函数原型:
int dup(int oldfd);
- 函数参数:oldfd为待拷贝的文件描述符
- 返回值:成功则返回新的文件描述符,失败则返回
-1
并修改errno
.复制后指向同一个文件,通过其中一个描述符关闭文件,仍然可用用另一个描述符来修改文件,即两个是独立的。
- 所需头文件:
- 重定向文件描述符
- 所需头文件:
<unistd.h>
- 函数原型:
int dup2(int oldfd, int newfd);
- 函数参数
- 返回值
oldfd指向a.txt, newfd指向b.txt,函数调用成功后,相当于调用了close(newfd),并将newfd指向a.txt。注意,a.txt必须是有效的文件,并且和复制描述符一样,最后都需要对两个文件描述符进行close.
- 所需头文件:
- fcntl函数: 复制文件描述符、设置/获取文件状态标志
- 所需头文件:
<unistd.h>
,<fcntl.h>
- 函数原型:
int fcntl(int fd, int cmd, ... /* arg */ );
函数参数:
- fd: 文件描述符
cmd:表示要如何擦欧总该文件
F_DUPFD
: 复制文件描述符, 复制的是第一个参数的fd,返回一个新的文件描述符,如:int ret = fcntl(fd, F_DUPFD);
- 返回值:新的文件描述符
F_GETFL
:获取指定文件的文件描述符的状态flag,获取的flag和open函数参数中的flag是一样的- 返回值:文件的状态flag
F_SET_FL
:设置文件描述符状态flag- 必选项:
O_RDONLY
,O_WRONLY
, orO_RDWR
(不可修改) 可选项:
O_APPEND
(追加数据),O_ASYNC
,O_DIRECT
,O_NOATIME
, andO_NONBLOCK
(设置成非阻塞)阻塞和非阻塞:描述的是函数调用的行为。阻塞是调用函数后进程/线程被挂起,直到得到结果后才会返回;而非阻塞则是立刻返回(但结果准确性不能保证),比如等待用户输入就是阻塞。
- 必选项:
- 其他
touch xxx.xx
: 创建一个xxx.xx文件- 标准C库IO在
man 3
中,Linux的IO、文件相关的在man 2
和man 3
中