【系统编程】错误处理、读写缓冲区及位图
文章目录
- 二、常用函数
- 2.1 `perror`
- 2.2 `strerror`
- 三、预读入、缓输出
- 3.1 预读入
- 3.2 缓输出
- 3.3 标准库与系统调用
- 四、位图
- 4.1 简介
- 4.2 基本原理
- 4.3 优点与应用
一、errno
简介
errno
是一个由系统维护的全局错误码变量,位于<errno.h>
中。- 当系统调用(如
open
,read
,write
,malloc
,fopen
等)或C库函数出错时,errno
会被设置为错误原因对应的整型值。 - 不会自动清零!只有函数调用失败时,系统才会修改它。
查看错误号:
vim /usr/include/asm-generic/errno-base.h
vim /usr/include/asm-generic/errno.h
二、常用函数
2.1 perror
#include <stdio.h>
void perror(const char *s);
- 参数 s:用户自定义的错误信息。
- s 会自动与 errno 对应的错误描述拼接
- 打印最近的
errno
所对应的错误信息字符串,输出到stderr
。 - 自动调用
strerror(errno)
,并加上前缀s
。 - 常用于快速调试,示例:
FILE *fp = fopen("nonexist.txt", "r");
if (!fp) {perror("fopen failed");
}
- 输出:
fopen failed: No such file or directory
2.2 strerror
#include <string.h>
char *strerror(int errnum);
- errnum:错误号
- 返回值:错误号对应的错误描述
- 作用:将错误号(如
errno
)转换为对应的文字描述(字符串)。 - 例如:
int main(int argc, char *argv[])
{char *p = strerror(EINTR);printf("strerr: %s\n", p);return 0;
}
- 输出:
strerr: Interrupted system call
三、预读入、缓输出
3.1 预读入
预读入是指一次性从文件中读取一大块数据到用户空间或库函数的缓冲区中,后续的读取请求直接从缓冲区获取,避免频繁的系统调用。
- 减少系统调用次数。
- 提高读取效率,读大文件时效果明显。
char buf[4096];
int n = read(fd, buf, 4096); // 预先读入一大块数据
- 设置一块用户空间缓冲区,用于接收
read()
从内核中读取的数据; - 批量读写,减少系统调用次数
fgetc(fp); // 实际是从内部缓冲区取,如果缓冲区空了才read一次
- 使用
fgetc()
、fgets()
等标准库函数时 libc
(C 标准库)自动管理内部缓冲,默认也是 4KB- 标准库会在合适时机(如缓冲满/
fflush
)调用
3.2 缓输出
缓输出是指将要写入的数据先暂存到缓冲区中,等缓冲区满了或显式调用 fflush()
、程序退出时,再一次性通过 write()
写入文件或设备。
- 使用
fputc()
、fprintf()
、fwrite()
等标准库函数时 - 默认会缓存在输出缓冲区,而不是实时写磁盘
类型 | 行为说明 | 常见场景 |
---|---|---|
行缓冲 | 读/写遇到换行符时才刷新缓冲 | 标准输出终端 |
全缓冲 | 缓冲区满或调用 fflush() 时才刷新 | 写入文件等 |
无缓冲 | 每次操作都会直接系统调用,无缓存 | 标准错误输出(stderr)等 |
3.3 标准库与系统调用
对比项 | 标准库 I/O(如 fgetc /fputc ) | 系统调用 I/O(如 read /write ) |
---|---|---|
缓冲机制 | 有,自动管理(行缓冲、全缓冲、无缓冲) | 无,需用户手动管理缓冲区 |
缓冲位置 | 用户空间,由 stdio 库维护 | 用户空间缓冲由程序员自定义 |
缓冲目的 | 降低系统调用次数,提升性能 | 性能取决于缓冲策略 |
性能 | 一般更慢(特别是逐字符操作) | 更高效,适合大块数据读写 |
适合场景 | 简单 I/O,字符或行处理 | 高性能 I/O,大块数据读写 |
- 标准库 I/O 有自动缓冲,更方便但效率低;
- 系统调用无自动缓冲,需程序员手动控制,但性能更高。
四、位图
4.1 简介
- 位图是一种用位(bit)表示状态的数据结构。
- 每一位(bit)表示某个资源的状态(如:占用/空闲)。
- 常用于管理有限且大量的资源,如:
- 内存页管理
- 硬盘块管理(文件系统)
- ID分配管理等
4.2 基本原理
- 用
0
表示资源空闲,1
表示资源占用(或反过来,根据场景自定义)。 - 每 1 个字节(byte) = 8 位(bit),可表示 8 个资源。
- 位图结构通常使用一个字节数组(
char[]
或unsigned char[]
)实现。
操作 | 说明 |
---|---|
设置位 | 将某一位设为 1,表示资源已分配 |
清除位 | 将某一位设为 0,表示资源已释放 |
查询位 | 检查某一位是否为 1 或 0 |
找空位 | 找到第一个为 0 的位,表示可用资源 |
示例代码
// 设置第 n 位为 1
bitmap[n / 8] |= (1 << (n % 8));// 清除第 n 位为 0
bitmap[n / 8] &= ~(1 << (n % 8));// 检查第 n 位是否为 1
int is_set = bitmap[n / 8] & (1 << (n % 8));
4.3 优点与应用
优点:
- 空间占用小(每个资源仅占 1 bit)
- 操作高效(按位运算)
应用场景:
- 内存页/块管理(如页帧、页面分配器)
- 文件系统中的块分配表(如 ext 位图分配)
- 线程ID/文件描述符管理