Linux IO操作
·
Linux IO操作
Linux IO操作是系统编程的基础。从基本的文件读写到高级的IO模型,理解IO机制对于编写高效程序至关重要。本文将系统讲解Linux IO操作的原理和实践。
一、文件描述符
1.1 什么是文件描述符
文件描述符(File Descriptor)是一个非负整数,用于标识打开的文件、管道、套接字等。
标准文件描述符:
| 值 | 名称 | 说明 |
|---|---|---|
| 0 | STDIN_FILENO | 标准输入 |
| 1 | STDOUT_FILENO | 标准输出 |
| 2 | STDERR_FILENO | 标准错误 |
1.2 文件描述符的限制
# 查看软限制
ulimit -n
# 查看系统限制
cat /proc/sys/fs/file-max
# 临时修改限制
ulimit -n 65535
二、基本IO函数
2.1 open() - 打开文件
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
常用flags:
| 标志 | 说明 |
|---|---|
| O_RDONLY | 只读 |
| O_WRONLY | 只写 |
| O_RDWR | 读写 |
| O_CREAT | 不存在则创建 |
| O_TRUNC | 截断为0 |
| O_APPEND | 追加模式 |
| O_NONBLOCK | 非阻塞 |
示例:
#include <fcntl.h>
#include <unistd.h>
int fd = open("file.txt", O_RDWR | O_CREAT, 0644);
if (fd == -1) {
perror("open");
return -1;
}
2.2 read() - 读取文件
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
返回值:
- 成功:返回读取的字节数
- 到达文件末尾:返回0
- 失败:返回-1
示例:
char buf[1024];
ssize_t n = read(fd, buf, sizeof(buf));
if (n == -1) {
perror("read");
}
2.3 write() - 写入文件
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
示例:
char *msg = "Hello World";
ssize_t n = write(fd, msg, strlen(msg));
if (n == -1) {
perror("write");
}
2.4 close() - 关闭文件
#include <unistd.h>
int close(int fd);
2.5 完整示例
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main() {
int fd = open("test.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
if (fd == -1) {
perror("open");
return 1;
}
// 写入
char *msg = "Hello Linux IO!";
write(fd, msg, strlen(msg));
// 定位到文件开头
lseek(fd, 0, SEEK_SET);
// 读取
char buf[100];
ssize_t n = read(fd, buf, sizeof(buf) - 1);
buf[n] = '\0';
printf("读取到: %s\n", buf);
close(fd);
return 0;
}
三、文件定位
3.1 lseek() - 移动文件指针
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
whence参数:
| 值 | 说明 |
|---|---|
| SEEK_SET | 文件开头 |
| SEEK_CUR | 当前位置 |
| SEEK_END | 文件末尾 |
示例:
// 移动到文件开头
lseek(fd, 0, SEEK_SET);
// 移动到文件末尾
lseek(fd, 0, SEEK_END);
// 从当前位置向后移动100字节
lseek(fd, 100, SEEK_CUR);
// 获取当前偏移
off_t pos = lseek(fd, 0, SEEK_CUR);
四、文件属性操作
4.1 stat() - 获取文件信息
#include <sys/stat.h>
int stat(const char *pathname, struct stat *statbuf);
int fstat(int fd, struct stat *statbuf);
int lstat(const char *pathname, struct stat *statbuf);
struct stat结构体:
struct stat {
dev_t st_dev; // 设备ID
ino_t st_ino; // inode号
mode_t st_mode; // 文件类型和权限
nlink_t st_nlink; // 硬链接数
uid_t st_uid; // 用户ID
gid_t st_gid; // 组ID
off_t st_size; // 文件大小
time_t st_atime; // 访问时间
time_t st_mtime; // 修改时间
time_t st_ctime; // 状态改变时间
};
示例:
struct stat sb;
if (stat("file.txt", &sb) == 0) {
printf("文件大小: %ld 字节\n", sb.st_size);
printf("inode号: %lu\n", sb.st_ino);
if (S_ISREG(sb.st_mode)) {
printf("这是一个普通文件\n");
}
}
五、IO模型
5.1 阻塞IO
默认模式,操作完成才返回。
// 阻塞读取
char buf[1024];
read(fd, buf, sizeof(buf)); // 等待数据
5.2 非阻塞IO
立即返回,通过轮询检查状态。
// 设置非阻塞
int flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
// 非阻塞读取
while (1) {
int n = read(fd, buf, sizeof(buf));
if (n == -1) {
if (errno == EAGAIN) {
// 暂无数据,稍后重试
usleep(1000);
continue;
} else {
perror("read");
break;
}
}
// 处理数据
}
5.3 IO多路复用
使用select、poll、epoll监控多个文件描述符。
// 使用select
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(fd, &readfds);
struct timeval timeout = {5, 0};
int ret = select(fd + 1, &readfds, NULL, NULL, &timeout);
if (ret > 0 && FD_ISSET(fd, &readfds)) {
read(fd, buf, sizeof(buf));
}
六、缓冲机制
6.1 标准IO vs 系统IO
| 特性 | 标准IO | 系统IO |
|---|---|---|
| 缓冲 | 有 | 无 |
| 效率 | 高 | 低 |
| 移植性 | 好 | 差 |
6.2 使用setvbuf设置缓冲
#include <stdio.h>
// 全缓冲
setvbuf(fp, buf, _IOFBF, BUFSIZ);
// 行缓冲
setvbuf(fp, buf, _IOLBF, BUFSIZ);
// 无缓冲
setvbuf(fp, NULL, _IONBF, 0);
更多推荐




所有评论(0)