Linux开发之文件IO(2023.04.18)
文件IO
C语言中有文件IO相关的库函数(fopen等),他的实际上是经过了某些步骤,然后调用linux系统调用。
这篇文章,要讲的是linux系统调用的文件IO,如open,close,read等。
预备知识
文件描述符
在linux中,程序打开的文件,会有一个整数指向它,这就叫文件描述符。通过对文件描述符进行操作,就能实际操作到打开的文件上。
文件描述符默认从3开始分配,因为0,1,2已经被占用,分别是标准输入,标准输出以及标准错误,这3个都指向当前终端。每打开一个新的文件,都会分配最小的未使用的文件描述符(系统做这个工作)。
man
linux的官方文档就是man手册,在学习系统调用时候查阅man手册很重要。系统函数在man的第2卷,比如想要查阅open系统函数可以输入man 2 open
。
perror()
打印errno所代表的错误信息。
// 所需头文件(之后将不再解释
#include <stdio.h>
void perror(const char *s);
s是展示给用户的字符串。
struct stat
stat()
中涉及到的结构体,用来存储文件信息。
struct stat {
dev_t st_dev; // 文件的设备编号
ino_t st_ino; // 节点
mode_t st_mode; // 文件的类型和存取的权限
nlink_t st_nlink; // 连到该文件的硬连接数目
uid_t st_uid; // 用户ID
gid_t st_gid; // 组ID
dev_t st_rdev; // 设备文件的设备编号
off_t st_size; // 文件字节数(文件大小)
blksize_t st_blksize; // 块大小
blkcnt_t st_blocks; // 块数
time_t st_atime; // 最后一次访问时间 可能类型为struct timespec
time_t st_mtime; // 最后一次修改时间 同上
time_t st_ctime; // 最后一次改变时间(指属性) 同上
};
其中st_mode含义如下,图源自牛客大学。
IO system call
open()
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, or O_RDWR)。还有一些可选的,可以用按位或来选中。可选项如下:
- O_APPEND:使用追加模式打开文件。
- O_CREAT:如果文件不存在将创建文件。
- mode:创建文件的权限,八进制形式(如0777)。不过文件最终权限=mode&~umask。(通过命令
umask
来查看)。
返回值是文件描述符(>0),如果文件打开失败,将返回-1,并设置errno。
read()
read函数用来从文件中读取数据。
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
- fd:要读取的文件的文件描述符。
- buf:要把读取到的数据存到哪里
- count:读取的大小
返回值是实际读取到的大小,如果>0表示读取正常,如果=0表示读完文件了,如果=-1表示读取错误,并设置errno。
write()
write函数用来在文件中写入数据。
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
- fd:要写入的文件的文件描述符。
- buf:要写入的数据的地址
- count:写入的大小
返回值是实际读写入的大小,如果=-1表示写入错误,并设置errno。
lseek()
修改文件的指针偏移。
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
- fd:文件描述符
- offset:偏移量
- whence:偏移方式,有以下选项
- SEEK_SET:设置文件偏移为 文件开头+offset
- SEEK_CUR:设置文件偏移为 当前偏移+offset
- SEEK_END:设置文件偏移为 文件末尾+offset
文件返回设置成功后的偏移位置。如果=-1表示读取错误,并设置errno。
lseek经常有以下作用
- 移动文件指针到文件头
lseek(df, 0, SEEK_SET)
- 获取当前文件指针位置
lseek(df, 0, SEEK_CUR)
- 获取当前文件文件大小
lseek(df, 0, SEEK_END)
- 扩展文件长度(最后要写入数据)
lseek(df, 100, SEEK_END);write(df, " ", 1);
stat()与lstat()
获取文件信息。
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat *statbuf);
int lstat(const char *pathname, struct stat *statbuf);
- pathname:文件路径
- statbuf:传出参数,获取到的信息将存入其中。
获取信息成功将返回0,如果返回-1表示获取错误,并设置errno。
stat
与lstat
区别就在于,前者获取软连接文件将获取到软连接指向的文件,而后者将获取到软连接文件本身。
实战案例:linux开发之ls -l命令实现
access()
检测用户是否有对应文件的权限,或检测文件是否存在。
#include <unistd.h>
int access(const char *pathname, int mode);
- pathname:文件路径
- mode:指名要检测的权限,有如下权限:
- R_OK:检测是否有读权限。
- W_OK:检测是否有写权限。
- X_OK:检测是否有执行权限。
- F_OK:检测文件是否存在。
如果用户具有对应的权限,或者文件存在则返回0,否则返回-1,并设置errno。
chmod()
更改文件权限。
#include <sys/stat.h>
int chmod(const char *pathname, mode_t mode);
- pathname:文件路径
- mode:可以是八进制数,也可以是具体的宏,具体请查看man。
设置成功将返回0,否则返回-1,并设置errno。
chown()
更改文件所有者,组。
#include <unistd.h>
int chown(const char *pathname, uid_t owner, gid_t group);
- pathname:文件路径
- owner:目标用户的用户号,如为-1则表示不变。
- group:目标组的组号,如为-1则表示不变。
设置成功将返回0,否则返回-1,并设置errno。
truncate()
更改文件大小。
#include <unistd.h>
#include <sys/types.h>
int truncate(const char *path, off_t length);
- path:文件路径
- length:目标长度
设置成功将返回0,否则返回-1,并设置errno。
rename()
重命名或改变路径文件或文件夹。
#include <stdio.h>
int rename(const char *oldpath, const char *newpath);
- oldpath:旧的文件路径
- newpath:新的文件路径
若newpath已存在,则直接覆盖。
更改成功将返回0,否则返回-1,并设置errno。
chdir()
更改进程工作目录。
#include <unistd.h>
int chdir(const char *path);
- path:目标目录
更改成功将返回0,否则返回-1,并设置errno。
getcwd()
获取当前工作目录。
#include <unistd.h>
char *getcwd(char *buf, size_t size);
- buf:传出参数,存放工作目录
- size:指名buf的长度
获取成功将返回buf,失败将返回NULL,并设置errno。
mkdir()
创建目录。
#include <sys/stat.h>
#include <sys/types.h>
int mkdir(const char *pathname, mode_t mode);
- pathname:文件夹路径
- mode:创建文件的权限,八进制形式(如0777)。不过文件最终权限=mode&~umask。(通过命令
umask
来查看)。
创建成功将返回0,否则返回-1,并设置errno。
rmdir()
删除空目录。
#include <unistd.h>
int rmdir(const char *pathname);
- pathname:文件夹路径
删除成功将返回0,否则返回-1,并设置errno。