文章目录
- 前言
- 一、文件描述符介绍
- 二、系统调用IO API介绍
- 2.1 open函数
- 2.2 close函数
- 2.3 read函数
- 2.4 write函数
- 2.5 lseek函数
- 三、示例代码
- 总结
前言
在Linux系统中,C语言通过系统调用实现对文件的输入输出(I/O)操作。系统调用提供了访问操作系统核心功能的接口,其中包括文件的创建、读取、写入和关闭等操作。这篇文章将介绍在Linux环境下,如何利用C语言进行基本的文件操作,通过系统调用实现对文件的有效管理。
一、文件描述符介绍
在Linux中,文件描述符是一个用来标识打开文件或者其他I/O资源的整数。每当你打开一个文件、网络连接或者其他的I/O资源时,Linux内核会分配一个唯一的文件描述符来标识这个资源。
文件描述符的作用很重要,因为它允许程序访问和操作打开的文件或者其他I/O资源。通过文件描述符,程序可以读取、写入、关闭以及对文件进行其他操作,比如移动文件指针。文件描述符也被用来进行进程间通信,比如通过管道或者套接字。
在Linux中,标准输入、标准输出和标准错误输出也分别有对应的文件描述符,它们分别是0、1和2。这意味着你可以把标准输出重定向到一个文件,或者把标准错误输出发送到另一个程序,这些都是通过文件描述符来完成的。总之,文件描述符在Linux系统中扮演着非常重要的角色,是程序与文件系统和其他I/O资源进行交互的桥梁。
二、系统调用IO API介绍
在使用系统调用IO之前我们需要加上下面这些头文件:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
2.1 open函数
open函数原型如下:
int open(const char *pathname, int flags);
参数1为打开文件的位置,他可以打开文件还可以打开一个设备device
参数2为一个类似于权限设置的一个标识,他有如下取值:
O_RDONLY
:只读方式打开文件。
O_WRONLY
:只写方式打开文件。
O_RDWR
:读写方式打开文件。
O_CREAT
:如果文件不存在,则创建一个新文件。
O_TRUNC
:如果文件存在,将其长度截断为0。
O_APPEND
:在文件末尾追加写入数据,而不是覆盖文件内容。
O_EXCL
:与 O_CREAT 一起使用,如果文件存在则返回错误。
O_NONBLOCK
:以非阻塞方式打开文件。
O_SYNC
:在每次写操作之后立即将数据写入磁盘,确保数据同步。
这些参数可以单独使用,也可以通过按位或 | 连接
他的返回值为文件描述符,类型为int
2.2 close函数
close
用于关闭一个文件描述符
他的函数原型如下:
int close(int fd);
参数为文件描述符
close 返回 0 表示 成功 , 或者 -1 表示 有 错误 发生 .
2.3 read函数
read函数原型如下:
ssize_t read(int fd, void *buf, size_t count);
函数 read() 用于从文件描述符 fd 指定的文件中读取数据,并将读取的数据存储到 buf 指向的内存空间中,最多读取 count 字节的数据。该函数的参数和返回值如下:
fd:要读取数据的文件描述符。
buf:指向存储读取数据的缓冲区的指针。
count:要读取的最大字节数。
返回值是 ssize_t 类型,表示读取的字节数。如果读取成功,则返回实际读取的字节数;如果到达文件末尾,返回值为0;如果发生错误,返回值为-1,并设置全局变量 errno 来指示发生的错误类型。
2.4 write函数
write函数原型如下:
ssize_t write(int fd, const void *buf, size_t count);
函数 write() 用于将数据从内存中的 buf 写入到文件描述符 fd 所指定的文件中,最多写入 count 字节的数据。该函数的参数和返回值如下:
fd:要写入数据的文件描述符。
buf:指向包含要写入数据的缓冲区的指针。
count:要写入的字节数。
返回值是 ssize_t 类型,表示实际写入的字节数。如果写入成功,则返回写入的字节数;如果发生错误,返回值为-1,并设置全局变量 errno 来指示错误类型。
需要注意的是,write() 函数不保证一次性写入所有请求的字节数,它可能写入部分数据,而不是全部。在实际应用中,我们通常需要在返回值不等于请求写入的字节数时进行额外的处理。
2.5 lseek函数
lseek函数原型如下:
off_t lseek(int fd, off_t offset, int whence);
fd:要设置偏移量的文件描述符。
offset:要移动的偏移量,可以为正数、负数或零。正数表示向文件尾方向移动,负数表示向文件头方向移动,零表示从文件开头计算的绝对偏移量。
whence:指定偏移量的计算方式,可以是以下几个值之一:
SEEK_SET:偏移量相对于文件开头计算。
SEEK_CUR:偏移量相对于当前位置计算。
SEEK_END:偏移量相对于文件末尾计算。
返回值是 off_t 类型,表示设置后的文件偏移量。如果设置成功,则返回新的偏移量;如果发生错误,返回值为-1,并设置全局变量 errno 来指示错误类型。
三、示例代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#define BUFFER_SIZE 1024
int main() {
int fd;
ssize_t bytes_written, bytes_read;
off_t offset;
char buffer[BUFFER_SIZE];
// 打开文件
fd = open("example.txt", O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
// 写入数据到文件
const char *data_to_write = "Hello, World!";
bytes_written = write(fd, data_to_write, strlen(data_to_write));
if (bytes_written == -1) {
perror("write");
exit(EXIT_FAILURE);
}
printf("Bytes written: %ld\n", bytes_written);
// 设置文件偏移量
offset = lseek(fd, 0, SEEK_SET);
if (offset == -1) {
perror("lseek");
exit(EXIT_FAILURE);
}
// 读取文件数据
bytes_read = read(fd, buffer, BUFFER_SIZE);
if (bytes_read == -1) {
perror("read");
exit(EXIT_FAILURE);
}
printf("Bytes read: %ld\n", bytes_read);
printf("Data read: %.*s\n", (int)bytes_read, buffer);
// 关闭文件
if (close(fd) == -1) {
perror("close");
exit(EXIT_FAILURE);
}
return 0;
}
总结
通过系统调用进行文件I/O操作是Linux C编程中的关键方面。我们了解了如何使用系统调用来创建文件、读取文件内容、写入数据,并最终关闭文件。这些基本的文件操作是构建更复杂应用程序的基础。通过合理地利用系统调用,我们能够更好地掌控文件资源,实现高效、稳定的文件处理。在深入学习C语言和Linux编程的过程中,对文件I/O的理解将成为编写强大、可靠程序的不可或缺的一部分。