【Linux文件操作】文件操作系统调用
需要注意的是,write 调用返回的实际写入字节数可能小于请求的字节数,因此在实际应用中可能需要循环写入,直到所有数据都被写入。:当 flags 中包含 O_CREAT 标志时,该参数用于指定新文件的权限模式,与 creat 系统调用中的 mode 参数相同。在对文件进行读写操作时,系统会维护一个文件偏移量(file offset),也称为文件指针,它指示了下一次读写操作开始的位置。展示了 ope
1.1 函数原型
代码语言:javascript
AI代码解释
#include <fcntl.h>
int creat(const char *pathname, mode_t mode);
1.2 参数说明
- pathname:指向要创建的文件路径名的字符串指针。可以是绝对路径,也可以是相对路径。
- mode:指定新文件的权限模式。它由一系列权限位组成,这些权限位决定了文件所有者、所属组和其他用户对该文件的访问权限。常见的权限位有:
- S_IRUSR:所有者具有读权限(0400)
- S_IWUSR:所有者具有写权限(0200)
- S_IXUSR:所有者具有执行权限(0100)
- S_IRGRP:所属组具有读权限(0040)
- S_IWGRP:所属组具有写权限(0020)
- S_IXGRP:所属组具有执行权限(0010)
- S_IROTH:其他用户具有读权限(0004)
- S_IWOTH:其他用户具有写权限(0002)
- S_IXOTH:其他用户具有执行权限(0001)
这些权限位可以通过按位或(|)运算组合使用,例如,S_IRUSR | S_IWUSR 表示所有者具有读写权限。
1.3 返回值
- 成功:返回一个非负的文件描述符(file descriptor),该文件描述符用于后续对文件的操作。
- 失败:返回 - 1,并设置 errno 来指示错误类型。常见的错误有:
- EEXIST:指定的文件已经存在,且没有被截断(当使用 O_CREAT | O_EXCL 标志时,如果文件存在则会返回该错误,但 creat 系统调用本身如果文件存在会截断文件,所以此错误在 creat 中较少见)
- ENOENT:路径名中的目录不存在
- EACCES:没有权限创建文件或访问路径中的目录
- ENOSPC:文件系统没有足够的空间创建新文件
1.4 使用示例
代码语言:javascript
AI代码解释
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
int main() {
int fd;
// 创建一个名为"testfile.txt"的文件,所有者具有读写权限,所属组和其他用户无权限
fd = creat("testfile.txt", S_IRUSR | S_IWUSR);
if (fd == -1) {
perror("creat failed");
exit(EXIT_FAILURE);
}
printf("File created successfully with file descriptor: %d\n", fd);
close(fd); // 创建文件后要记得关闭文件描述符
return 0;
}

使用 creat 函数创建了一个名为 “testfile.txt” 的文件,设置所有者具有读写权限。如果创建成功,会输出文件描述符;如果失败,会通过 perror 函数打印错误信息。
二、文件打开(open 系统调用)
open 系统调用是 Linux 中用于打开一个已存在的文件或创建一个新文件的主要系统调用。它比 creat 系统调用功能更强大,可以通过不同的标志组合实现多种操作。
2.1 函数原型
代码语言:javascript
AI代码解释
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
2.2 参数说明
pathname:与 creat 系统调用中的 pathname 参数相同,指向要打开或创建的文件路径名。
flags:用于指定打开文件的方式和行为,是一个整数,可以由多个标志按位或组合而成。常见的标志有:
- O_RDONLY:以只读方式打开文件
- O_WRONLY:以只写方式打开文件
- O_RDWR:以读写方式打开文件
- O_CREAT:如果指定的文件不存在,则创建该文件。此时需要提供第三个参数 mode 来指定新文件的权限。
- O_EXCL:与 O_CREAT 一起使用,如果文件已经存在,则 open 调用失败(返回 - 1),可以用于防止覆盖已存在的文件。
- O_TRUNC:如果文件已经存在且以可写方式打开(O_WRONLY 或 O_RDWR),则将文件截断为零长度。
- O_APPEND:以追加方式打开文件,每次写操作都将数据添加到文件的末尾。
mode:当 flags 中包含 O_CREAT 标志时,该参数用于指定新文件的权限模式,与 creat 系统调用中的 mode 参数相同。如果 flags 中不包含 O_CREAT,则该参数被忽略。
2.3 返回值
- 成功:返回一个非负的文件描述符,用于后续对文件的操作。
- 失败:返回 - 1,并设置 errno 指示错误类型。常见的错误与 creat 系统调用类似,此外还有:
- EINVAL:flags 参数无效
- EMFILE:进程已打开的文件描述符达到上限
2.4 使用示例
代码语言:javascript
AI代码解释
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
int fd1, fd2, fd3;
// 以只读方式打开已存在的文件"testfile.txt"
fd1 = open("testfile.txt", O_RDONLY);
if (fd1 == -1) {
perror("open for read failed");
exit(EXIT_FAILURE);
}
// 以读写方式打开文件,如果文件不存在则创建,权限为所有者读写,组和其他用户只读
fd2 = open("newfile.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (fd2 == -1) {
perror("open for read-write and create failed");
close(fd1);
exit(EXIT_FAILURE);
}
// 以只写、追加方式打开文件,若文件不存在则创建
fd3 = open("logfile.txt", O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR);
if (fd3 == -1) {
perror("open for write and append failed");
close(fd1);
close(fd2);
exit(EXIT_FAILURE);
}
printf("fd1: %d, fd2: %d, fd3: %d\n", fd1, fd2, fd3);
// 关闭文件描述符
close(fd1);
close(fd2);
close(fd3);
return 0;
}

展示了 open 系统调用的几种常见用法:只读打开已存在文件、读写打开并创建新文件、只写追加打开并创建新文件。
三、文件读写(read 和 write 系统调用)
打开文件后,就可以进行读写操作了。Linux 提供了 read 和 write 系统调用来实现对文件的读写。
3.1 read 系统调用
read 系统调用用于从已打开的文件中读取数据。
①函数原型
代码语言:javascript
AI代码解释
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
②参数说明
- fd:文件描述符,即 open 或 creat 系统调用返回的非负整数,标识要读取的文件。
- buf:指向一个缓冲区的指针,用于存储读取到的数据。
- count:指定要读取的字节数。
③ 返回值
- 成功:返回实际读取到的字节数。如果已经到达文件末尾,则返回 0。
- 失败:返回 - 1,并设置 errno 指示错误类型,如 EBADF(文件描述符无效)、EIO(I/O 错误)等。
3.2 write 系统调用
write 系统调用用于向已打开的文件中写入数据。
①函数原型
代码语言:javascript
AI代码解释
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
②参数说明
- fd:文件描述符,标识要写入的文件。
- buf:指向一个缓冲区的指针,该缓冲区中存储了要写入的数据。
- count:指定要写入的字节数。
③返回值
- 成功:返回实际写入的字节数。注意,实际写入的字节数可能小于 count,这并不一定表示错误,可能是由于各种原因(如磁盘空间不足、信号中断等)导致的。
- 失败:返回 - 1,并设置 errno 指示错误类型,如 EBADF(文件描述符无效或不具有写权限)、EIO(I/O 错误)等。
3.3 使用示例
代码语言:javascript
AI代码解释
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main() {
int fd;
char write_buf[] = "Hello, Linux File Operation!";
char read_buf[1024];
ssize_t nwritten, nread;
// 以读写方式打开文件,不存在则创建
fd = open("rwtest.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (fd == -1) {
perror("open failed");
exit(EXIT_FAILURE);
}
// 写入数据
nwritten = write(fd, write_buf, strlen(write_buf));
if (nwritten == -1) {
perror("write failed");
close(fd);
exit(EXIT_FAILURE);
}
printf("Wrote %zd bytes: %s\n", nwritten, write_buf);
// 将文件指针移到文件开头(否则读取不到刚写入的数据)
off_t offset = lseek(fd, 0, SEEK_SET);
if (offset == -1) {
perror("lseek failed");
close(fd);
exit(EXIT_FAILURE);
}
// 读取数据
nread = read(fd, read_buf, sizeof(read_buf) - 1); // 留一个字节给'\0'
if (nread == -1) {
perror("read failed");
close(fd);
exit(EXIT_FAILURE);
} else if (nread == 0) {
printf("Reached end of file\n");
} else {
read_buf[nread] = '\0'; // 加上字符串结束符
printf("Read %zd bytes: %s\n", nread, read_buf);
}
close(fd);
return 0;
}

先向文件写入一段字符串,然后使用 lseek 系统调用将文件指针移到文件开头,再读取文件内容并打印。需要注意的是,write 调用返回的实际写入字节数可能小于请求的字节数,因此在实际应用中可能需要循环写入,直到所有数据都被写入。同样,read 调用也可能需要循环读取,直到读取到预期的数据量或到达文件末尾。
四、文件定位(lseek 系统调用)
在对文件进行读写操作时,系统会维护一个文件偏移量(file offset),也称为文件指针,它指示了下一次读写操作开始的位置。lseek 系统调用用于修改这个文件偏移量,实现对文件的随机访问。
4.1 函数原型
代码语言:javascript
AI代码解释
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
4.2 参数说明
- fd:文件描述符,标识要操作的文件。
- offset:偏移量,是一个有符号整数,表示要移动的字节数。
- whence:用于指定偏移量的参考位置,有以下三种取值:
- SEEK_SET:将文件偏移量设置为 offset 字节(从文件开头开始计算)。
- SEEK_CUR:将文件偏移量设置为当前偏移量加上 offset 字节。
- SEEK_END:将文件偏移量设置为文件末尾加上 offset 字节(offset 为负时表示向文件开头方向移动)。
4.3 返回值
- 成功:返回新的文件偏移量(从文件开头开始计算的字节数)。
- 失败:返回 - 1,并设置 errno 指示错误类型,如 EBADF(文件描述符无效)、ESPIPE(文件是管道、FIFO 或套接字,这些文件不支持定位操作)等。
4.4 使用示例
代码语言:javascript
AI代码解释
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main() {
int fd;
char buf[100];
off_t offset;
// 创建并打开一个文件
fd = open("seektest.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (fd == -1) {
perror("open failed");
exit(EXIT_FAILURE);
}
// 写入一些数据
const char *data = "0123456789abcdefghijklmnopqrstuvwxyz";
write(fd, data, strlen(data));
// 将文件指针移到开头
offset = lseek(fd, 0, SEEK_SET);
if (offset == -1) {
perror("lseek to beginning failed");
close(fd);
exit(EXIT_FAILURE);
}
printf("Moved to beginning, offset: %ld\n", (long)offset);
// 读取5个字节
ssize_t nread = read(fd, buf, 5);
if (nread == -1) {
perror("read failed");
close(fd);
exit(EXIT_FAILURE);
}
buf[nread] = '\0';
printf("Read %zd bytes: %s\n", nread, buf);
// 从当前位置向后移动10个字节
offset = lseek(fd, 10, SEEK_CUR);
if (offset == -1) {
perror("lseek from current failed");
close(fd);
exit(EXIT_FAILURE);
}
printf("Moved 10 bytes from current, new offset: %ld\n", (long)offset);
// 读取5个字节
nread = read(fd, buf, 5);
if (nread == -1) {
perror("read failed");
close(fd);
exit(EXIT_FAILURE);
}
buf[nread] = '\0';
printf("Read %zd bytes: %s\n", nread, buf);
// 从文件末尾向前移动20个字节
offset = lseek(fd, -20, SEEK_END);
if (offset == -1) {
perror("lseek from end failed");
close(fd);
exit(EXIT_FAILURE);
}
printf("Moved 20 bytes before end, new offset: %ld\n", (long)offset);
// 读取5个字节
nread = read(fd, buf, 5);
if (nread == -1) {
perror("read failed");
close(fd);
exit(EXIT_FAILURE);
}
buf[nread] = '\0';
printf("Read %zd bytes: %s\n", nread, buf);
close(fd);
return 0;
}

展示了 lseek 系统调用的三种使用方式:从文件开头设置偏移量、从当前位置调整偏移量、从文件末尾调整偏移量。通过 lseek,我们可以灵活地定位到文件的任意位置进行读写操作,实现随机访问。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐



所有评论(0)