第16章 C语言教程 - 系统函数库
第16章 系统函数库
1. 系统函数库概述
1.1 什么是系统函数库
系统函数库是操作系统提供的一组函数集合,用于访问系统功能和资源。这些函数封装了底层的系统调用,为应用程序提供了一个统一的接口,使应用程序能够与操作系统进行交互。
1.2 系统函数库的分类
C语言中的系统函数库主要包括:
- 标准I/O库:用于文件输入/输出操作
- 字符串处理库:用于字符串操作
- 数学库:用于数学计算
- 时间和日期库:用于时间和日期操作
- 内存分配库:用于动态内存分配
- 进程控制库:用于进程管理
- 网络编程库:用于网络通信
- 信号处理库:用于信号处理
- 线程编程库:用于多线程编程
- 系统调用库:用于直接调用系统服务
1.3 系统函数库的使用
使用系统函数库需要:
- 包含相应的头文件:如
#include <stdio.h> - 链接相应的库:如使用
-lm链接数学库 - 遵循函数的调用约定:正确传递参数,处理返回值
2. 标准I/O库
2.1 标准I/O库概述
标准I/O库(<stdio.h>)提供了文件输入/输出操作的函数,是C语言中最常用的库之一。它通过缓冲区机制和文件流抽象,为应用程序提供了高效、统一的I/O操作接口。
2.1.1 标准I/O库的底层实现
标准I/O库的核心是文件流(FILE)和缓冲区管理:
文件流结构:
FILE是一个包含文件描述符、缓冲区状态和定位信息的结构体- 每个文件流都有一个关联的文件描述符,用于底层系统调用
- 流状态包括错误标志、文件结束标志和缓冲区状态
缓冲区管理:
- 全缓冲:只有当缓冲区满或显式刷新时才执行I/O操作(如普通文件)
- 行缓冲:当遇到换行符或缓冲区满时执行I/O操作(如标准输入/输出)
- 无缓冲:直接执行I/O操作,无缓冲区(如标准错误)
缓冲区大小:
- 默认缓冲区大小通常为8192字节(8KB)
- 可通过
setvbuf函数自定义缓冲区大小和类型
2.1.2 标准I/O库的性能考量
标准I/O库的性能优势主要来自于:
- 减少系统调用:通过缓冲区减少了系统调用次数,提高了I/O性能
- 统一接口:为不同类型的设备提供了统一的操作接口
- 错误处理:提供了更详细的错误信息和处理机制
- 格式化功能:支持复杂的数据格式化和解析
2.2 常用函数
2.2.1 文件操作函数
| 函数名 | 功能 | 原型 | 底层实现 | 性能考量 |
|---|---|---|---|---|
fopen | 打开文件 | FILE *fopen(const char *filename, const char *mode); | 调用 open 系统调用,初始化 FILE 结构体和缓冲区 | 开销较大,应避免频繁打开/关闭文件 |
fclose | 关闭文件 | int fclose(FILE *stream); | 刷新缓冲区,调用 close 系统调用,释放 FILE 结构体 | 确保所有数据被写入,避免资源泄漏 |
fread | 读取文件 | size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); | 从缓冲区读取数据,必要时调用 read 系统调用填充缓冲区 | 大块读取更高效,避免频繁调用 |
fwrite | 写入文件 | size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); | 写入缓冲区,必要时调用 write 系统调用刷新缓冲区 | 大块写入更高效,考虑使用缓冲区优化 |
fseek | 设置文件位置 | int fseek(FILE *stream, long offset, int whence); | 刷新缓冲区,调用 lseek 系统调用,更新文件位置指针 | 可能触发缓冲区刷新,影响性能 |
ftell | 获取文件位置 | long ftell(FILE *stream); | 返回当前文件位置指针,无需系统调用 | 开销很小,可频繁使用 |
rewind | 重置文件位置 | void rewind(FILE *stream); | 等同于 fseek(stream, 0, SEEK_SET) | 可能触发缓冲区刷新 |
feof | 检查文件结束 | int feof(FILE *stream); | 检查文件结束标志,无需系统调用 | 开销很小,可频繁使用 |
ferror | 检查文件错误 | int ferror(FILE *stream); | 检查错误标志,无需系统调用 | 开销很小,可频繁使用 |
clearerr | 清除文件错误 | void clearerr(FILE *stream); | 清除错误标志和文件结束标志,无需系统调用 | 开销很小,可频繁使用 |
setvbuf | 设置缓冲区 | int setvbuf(FILE *stream, char *buf, int mode, size_t size); | 设置缓冲区类型和大小,影响I/O性能 | 应在打开文件后立即调用 |
fflush | 刷新缓冲区 | int fflush(FILE *stream); | 将缓冲区数据写入文件,调用 write 系统调用 | 可能触发系统调用,影响性能 |
2.2.2 格式化输入/输出函数
| 函数名 | 功能 | 原型 | 安全考量 | 性能考量 |
|---|---|---|---|---|
printf | 格式化输出到标准输出 | int printf(const char *format, ...); | 安全,无缓冲区溢出风险 | 频繁调用可能影响性能,考虑使用 puts 或 fputs |
scanf | 格式化输入到标准输入 | int scanf(const char *format, ...); | 不安全,可能导致缓冲区溢出 | 输入验证不足,建议使用 fgets + sscanf |
fprintf | 格式化输出到文件 | int fprintf(FILE *stream, const char *format, ...); | 安全,无缓冲区溢出风险 | 频繁调用可能影响性能,考虑批量写入 |
fscanf | 格式化输入到文件 | int fscanf(FILE *stream, const char *format, ...); | 不安全,可能导致缓冲区溢出 | 输入验证不足,建议使用 fgets + sscanf |
sprintf | 格式化输出到字符串 | int sprintf(char *str, const char *format, ...); | 不安全,可能导致缓冲区溢出 | 应使用 snprintf 替代 |
sscanf | 格式化输入到字符串 | int sscanf(const char *str, const char *format, ...); | 相对安全,输入受限于源字符串 | 性能较好,适合解析已知格式的字符串 |
snprintf | 安全的格式化输出到字符串 | int snprintf(char *str, size_t size, const char *format, ...); | 安全,可防止缓冲区溢出 | 性能略低于 sprintf,但安全性更高 |
vsnprintf | 可变参数的安全格式化输出 | int vsnprintf(char *str, size_t size, const char *format, va_list ap); | 安全,可防止缓冲区溢出 | 适合实现自定义格式化函数 |
2.2.3 字符输入/输出函数
| 函数名 | 功能 | 原型 | 性能考量 | 使用场景 |
|---|---|---|---|---|
getchar | 从标准输入读取字符 | int getchar(void); | 行缓冲,适合交互式输入 | 逐字符读取标准输入 |
putchar | 向标准输出写入字符 | int putchar(int c); | 行缓冲,适合交互式输出 | 逐字符写入标准输出 |
fgetc | 从文件读取字符 | int fgetc(FILE *stream); | 可能触发缓冲区填充,频繁调用影响性能 | 逐字符读取文件 |
fputc | 向文件写入字符 | int fputc(int c, FILE *stream); | 可能触发缓冲区刷新,频繁调用影响性能 | 逐字符写入文件 |
ungetc | 放回字符到输入流 | int ungetc(int c, FILE *stream); | 操作流内部缓冲区,开销很小 | 实现词法分析器、解析器等 |
2.2.4 字符串输入/输出函数
| 函数名 | 功能 | 原型 | 安全考量 | 性能考量 |
|---|---|---|---|---|
gets | 从标准输入读取字符串(不安全) | char *gets(char *s); | 极不安全,已从C11标准中移除 | 应使用 fgets 替代 |
puts | 向标准输出写入字符串 | int puts(const char *s); | 安全,自动添加换行符 | 性能较好,适合输出字符串 |
fgets | 从文件读取字符串 | char *fgets(char *s, int size, FILE *stream); | 安全,可防止缓冲区溢出 | 性能较好,适合读取文本行 |
fputs | 向文件写入字符串 | int fputs(const char *s, FILE *stream); | 安全,不自动添加换行符 | 性能较好,适合写入字符串 |
2.3 高级文件操作
2.3.1 二进制文件操作
二进制文件操作与文本文件操作的主要区别在于:
- 打开模式:使用
"rb"、"wb"、"ab"等模式打开二进制文件 - 数据处理:二进制文件直接读写字节,不进行行尾转换
- 性能考量:二进制文件操作通常比文本文件操作更高效
示例:二进制文件操作
1 |
|
2.3.2 文件定位与随机访问
文件定位是实现随机访问文件的基础,通过 fseek、ftell 和 rewind 函数实现:
- 文件位置指示器:每个文件流都有一个文件位置指示器,指向当前读写位置
- 定位模式:
SEEK_SET:从文件开头计算偏移量SEEK_CUR:从当前位置计算偏移量SEEK_END:从文件末尾计算偏移量
- 大文件支持:对于超过 2GB 的文件,应使用
fseeko和ftello函数,它们使用off_t类型
示例:随机访问文件
1 |
|
2.3.3 临时文件操作
临时文件用于存储临时数据,通常在程序结束后自动删除:
临时文件创建:
tmpfile():创建一个临时文件,程序结束时自动删除tmpnam():生成一个唯一的临时文件名mkstemp():创建一个唯一的临时文件,更安全
临时文件安全:
- 避免使用固定的临时文件名,防止文件覆盖攻击
- 使用
mkstemp()或tmpfile()生成唯一的临时文件名 - 临时文件应设置适当的权限,防止未授权访问
示例:使用临时文件
1 |
|
2.4 性能优化技巧
2.4.1 缓冲区优化
选择合适的缓冲区大小:
- 过小的缓冲区会增加系统调用次数
- 过大的缓冲区会浪费内存
- 建议缓冲区大小为4KB或8KB,与系统页大小匹配
使用自定义缓冲区:
1
2
3char buffer[8192];
FILE *fp = fopen("file.txt", "r");
setvbuf(fp, buffer, _IOFBF, sizeof(buffer));批量读写:
- 避免逐字符或逐行读写大文件
- 使用
fread和fwrite进行批量读写 - 合理设置读写块大小,通常为8KB或16KB
2.4.2 错误处理优化
集中错误处理:
- 定义统一的错误处理函数
- 避免在每个I/O操作后都检查错误
使用
perror和strerror:- 提供详细的错误信息
- 有助于快速定位问题
检查返回值:
- 始终检查I/O函数的返回值
- 不要依赖文件结束标志作为唯一的错误指示
2.4.3 文件操作优化
减少文件打开/关闭次数:
- 避免在循环中频繁打开和关闭文件
- 一次性打开文件,完成所有操作后关闭
使用二进制模式:
- 对于非文本文件,使用二进制模式读写
- 避免行尾转换开销
合理使用文件定位:
- 避免频繁的文件定位操作
- 尽量顺序访问文件,利用预读机制
使用内存映射:
- 对于大文件,考虑使用
mmap进行内存映射 - 可以显著提高大文件的访问性能
- 对于大文件,考虑使用
2.5 安全编程实践
2.5.1 防止缓冲区溢出
使用安全的输入函数:
- 使用
fgets替代gets - 使用
snprintf替代sprintf - 使用
strncpy替代strcpy
- 使用
输入验证:
- 始终验证输入数据的长度和格式
- 对用户输入进行严格检查
缓冲区大小计算:
- 使用
sizeof计算缓冲区大小 - 避免硬编码缓冲区大小
- 使用
2.5.2 资源管理
文件句柄管理:
- 始终关闭打开的文件
- 使用
fclose确保所有数据被写入
异常处理:
- 在函数返回前确保所有资源被释放
- 考虑使用
goto语句进行统一的资源清理
内存管理:
- 避免内存泄漏
- 及时释放不再使用的内存
2.6 使用示例
2.6.1 高效文件复制
1 |
|
2.6.2 大文件读取与处理
1 |
|
2.7 常见问题与解决方案
2.7.1 缓冲区问题
问题:程序输出不显示,或者文件内容未写入
解决方案:
- 对于标准输出,添加换行符或使用
fflush(stdout) - 对于文件,使用
fflush(fp)或确保调用fclose(fp)
2.7.2 文件权限问题
问题:fopen 失败,错误信息为 “Permission denied”
解决方案:
- 检查文件权限
- 确保文件路径正确
- 避免在只读文件系统中写入文件
2.7.3 文件锁定问题
问题:多个进程同时访问同一个文件,导致数据损坏
解决方案:
- 使用文件锁(
flock、fcntl) - 实现互斥访问机制
- 避免多个进程同时写入同一个文件
2.7.4 大文件处理问题
问题:无法处理超过2GB的文件
解决方案:
- 使用
fseeko和ftello替代fseek和ftell - 编译时定义
_FILE_OFFSET_BITS=64 - 考虑使用内存映射(
mmap)处理大文件
2.8 总结
标准I/O库是C语言中最常用的库之一,它提供了丰富的文件操作函数和格式化输入/输出函数。通过合理使用标准I/O库,可以:
- 提高I/O性能:通过缓冲区机制减少系统调用次数
- 简化编程:提供统一的接口处理不同类型的I/O设备
- 增强安全性:使用安全的输入/输出函数防止缓冲区溢出
- 提高可移植性:标准I/O库在不同平台上的实现基本一致
在实际编程中,应根据具体需求选择合适的I/O函数,并注意性能优化和安全编程实践,以编写高效、可靠的I/O操作代码。
3. 字符串处理库
3.1 字符串处理库概述
字符串处理库(<string.h>)提供了字符串操作的函数,是C语言中最常用的库之一。
3.2 常用函数
3.2.1 字符串操作函数
| 函数名 | 功能 | 原型 |
|---|---|---|
strlen | 获取字符串长度 | size_t strlen(const char *s); |
strcpy | 复制字符串(不安全) | char *strcpy(char *dest, const char *src); |
strncpy | 安全的复制字符串 | char *strncpy(char *dest, const char *src, size_t n); |
strcat | 连接字符串(不安全) | char *strcat(char *dest, const char *src); |
strncat | 安全的连接字符串 | char *strncat(char *dest, const char *src, size_t n); |
strcmp | 比较字符串 | int strcmp(const char *s1, const char *s2); |
strncmp | 比较字符串前n个字符 | int strncmp(const char *s1, const char *s2, size_t n); |
strchr | 查找字符 | char *strchr(const char *s, int c); |
strrchr | 反向查找字符 | char *strrchr(const char *s, int c); |
strstr | 查找子字符串 | char *strstr(const char *haystack, const char *needle); |
strtok | 分割字符串 | char *strtok(char *str, const char *delim); |
memset | 填充内存 | void *memset(void *s, int c, size_t n); |
memcpy | 复制内存 | void *memcpy(void *dest, const void *src, size_t n); |
memmove | 安全的复制内存 | void *memmove(void *dest, const void *src, size_t n); |
memcmp | 比较内存 | int memcmp(const void *s1, const void *s2, size_t n); |
3.3 使用示例
1 |
|
4. 数学库
4.1 数学库概述
数学库(<math.h>)提供了数学计算的函数,使用时需要链接数学库(-lm)。
4.2 常用函数
4.2.1 基本数学函数
| 函数名 | 功能 | 原型 |
|---|---|---|
sin | 正弦函数 | double sin(double x); |
cos | 余弦函数 | double cos(double x); |
tan | 正切函数 | double tan(double x); |
asin | 反正弦函数 | double asin(double x); |
acos | 反余弦函数 | double acos(double x); |
atan | 反正切函数 | double atan(double x); |
atan2 | 反正切函数(两个参数) | double atan2(double y, double x); |
sinh | 双曲正弦函数 | double sinh(double x); |
cosh | 双曲余弦函数 | double cosh(double x); |
tanh | 双曲正切函数 | double tanh(double x); |
4.2.2 指数和对数函数
| 函数名 | 功能 | 原型 |
|---|---|---|
exp | 指数函数 | double exp(double x); |
log | 自然对数 | double log(double x); |
log10 | 以10为底的对数 | double log10(double x); |
pow | 幂函数 | double pow(double x, double y); |
sqrt | 平方根 | double sqrt(double x); |
cbrt | 立方根 | double cbrt(double x); |
4.2.3 取整和绝对值函数
| 函数名 | 功能 | 原型 |
|---|---|---|
ceil | 向上取整 | double ceil(double x); |
floor | 向下取整 | double floor(double x); |
round | 四舍五入 | double round(double x); |
fabs | 绝对值 | double fabs(double x); |
abs | 整数绝对值 | int abs(int x); |
labs | 长整数绝对值 | long labs(long x); |
llabs | 长 long 整数绝对值 | long long llabs(long long x); |
4.3 使用示例
1 |
|
5. 时间和日期库
5.1 时间和日期库概述
时间和日期库(<time.h>)提供了时间和日期操作的函数。
5.2 常用函数
5.2.1 时间操作函数
| 函数名 | 功能 | 原型 |
|---|---|---|
time | 获取当前时间 | time_t time(time_t *t); |
ctime | 转换时间为字符串 | char *ctime(const time_t *t); |
asctime | 转换结构体为字符串 | char *asctime(const struct tm *tm); |
localtime | 转换时间为本地时间 | struct tm *localtime(const time_t *t); |
gmtime | 转换时间为GMT时间 | struct tm *gmtime(const time_t *t); |
mktime | 转换结构体为时间 | time_t mktime(struct tm *tm); |
difftime | 计算时间差 | double difftime(time_t time1, time_t time0); |
strftime | 格式化时间为字符串 | size_t strftime(char *s, size_t maxsize, const char *format, const struct tm *tm); |
5.3 使用示例
1 |
|
6. 内存分配库
6.1 内存分配库概述
内存分配库(<stdlib.h>)提供了动态内存分配的函数,是C语言中管理内存的重要工具。
6.2 常用函数
6.2.1 内存分配函数
| 函数名 | 功能 | 原型 |
|---|---|---|
malloc | 分配内存 | void *malloc(size_t size); |
calloc | 分配并清零内存 | void *calloc(size_t nmemb, size_t size); |
realloc | 重新分配内存 | void *realloc(void *ptr, size_t size); |
free | 释放内存 | void free(void *ptr); |
aligned_alloc | 分配对齐内存 | void *aligned_alloc(size_t alignment, size_t size); |
6.2.2 其他常用函数
| 函数名 | 功能 | 原型 |
|---|---|---|
exit | 终止程序 | void exit(int status); |
atoi | 字符串转整数 | int atoi(const char *nptr); |
atol | 字符串转长整数 | long atol(const char *nptr); |
atoll | 字符串转长 long 整数 | long long atoll(const char *nptr); |
strtod | 字符串转双精度浮点数 | double strtod(const char *nptr, char **endptr); |
strtol | 字符串转长整数 | long strtol(const char *nptr, char **endptr, int base); |
strtoll | 字符串转长 long 整数 | long long strtoll(const char *nptr, char **endptr, int base); |
rand | 生成随机数 | int rand(void); |
srand | 设置随机数种子 | void srand(unsigned int seed); |
qsort | 快速排序 | void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)); |
bsearch | 二分查找 | void *bsearch(const void *key, const void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)); |
6.3 使用示例
1 |
|
7. 进程控制库
7.1 进程控制库概述
进程控制库(<unistd.h>)提供了进程管理的函数,主要用于Unix-like系统。
7.2 常用函数
| 函数名 | 功能 | 原型 |
|---|---|---|
fork | 创建子进程 | pid_t fork(void); |
exec 系列 | 执行程序 | 如 int execl(const char *path, const char *arg, ...); |
wait | 等待子进程结束 | pid_t wait(int *status); |
waitpid | 等待指定子进程结束 | pid_t waitpid(pid_t pid, int *status, int options); |
getpid | 获取进程ID | pid_t getpid(void); |
getppid | 获取父进程ID | pid_t getppid(void); |
sleep | 睡眠指定秒数 | unsigned int sleep(unsigned int seconds); |
usleep | 睡眠指定微秒数 | int usleep(useconds_t usec); |
nanosleep | 睡眠指定纳秒数 | int nanosleep(const struct timespec *req, struct timespec *rem); |
getuid | 获取用户ID | uid_t getuid(void); |
geteuid | 获取有效用户ID | uid_t geteuid(void); |
getgid | 获取组ID | gid_t getgid(void); |
getegid | 获取有效组ID | gid_t getegid(void); |
chdir | 改变当前目录 | int chdir(const char *path); |
getcwd | 获取当前目录 | char *getcwd(char *buf, size_t size); |
unlink | 删除文件 | int unlink(const char *pathname); |
rename | 重命名文件 | int rename(const char *oldpath, const char *newpath); |
7.3 使用示例
1 |
|
8. 网络编程库
8.1 网络编程库概述
网络编程库(<sys/socket.h>)提供了网络通信的函数,是C语言中进行网络编程的基础。
8.2 常用函数
8.2.1 套接字函数
| 函数名 | 功能 | 原型 |
|---|---|---|
socket | 创建套接字 | int socket(int domain, int type, int protocol); |
bind | 绑定地址 | int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); |
listen | 监听连接 | int listen(int sockfd, int backlog); |
accept | 接受连接 | int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); |
connect | 连接服务器 | int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); |
send | 发送数据 | ssize_t send(int sockfd, const void *buf, size_t len, int flags); |
recv | 接收数据 | ssize_t recv(int sockfd, void *buf, size_t len, int flags); |
sendto | 发送数据(UDP) | ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); |
recvfrom | 接收数据(UDP) | ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); |
close | 关闭套接字 | int close(int fd); |
getaddrinfo | 获取地址信息 | int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res); |
freeaddrinfo | 释放地址信息 | void freeaddrinfo(struct addrinfo *res); |
inet_ntop | 网络地址转字符串 | const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); |
inet_pton | 字符串转网络地址 | int inet_pton(int af, const char *src, void *dst); |
8.3 使用示例
服务器端:
1 |
|
客户端:
1 |
|
9. 信号处理库
9.1 信号处理库概述
信号处理库(<signal.h>)提供了信号处理的函数,用于处理系统发送的各种信号。
9.2 常用函数
| 函数名 | 功能 | 原型 |
|---|---|---|
signal | 设置信号处理函数 | void (*signal(int signum, void (*handler)(int)))(int); |
sigaction | 设置信号处理函数(更灵活) | int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); |
kill | 发送信号 | int kill(pid_t pid, int sig); |
raise | 向自身发送信号 | int raise(int sig); |
alarm | 设置闹钟 | unsigned int alarm(unsigned int seconds); |
pause | 暂停进程直到信号到达 | int pause(void); |
sigprocmask | 阻塞/解除阻塞信号 | int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); |
sigemptyset | 初始化信号集为空 | int sigemptyset(sigset_t *set); |
sigfillset | 初始化信号集为所有信号 | int sigfillset(sigset_t *set); |
sigaddset | 向信号集添加信号 | int sigaddset(sigset_t *set, int signum); |
sigdelset | 从信号集删除信号 | int sigdelset(sigset_t *set, int signum); |
sigismember | 检查信号是否在信号集中 | int sigismember(const sigset_t *set, int signum); |
9.3 使用示例
1 |
|
10. 线程编程库
10.1 线程编程库概述
线程编程库(<pthread.h>)提供了多线程编程的函数,使用时需要链接线程库(-lpthread)。
10.2 常用函数
| 函数名 | 功能 | 原型 |
|---|---|---|
pthread_create | 创建线程 | int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg); |
pthread_join | 等待线程结束 | int pthread_join(pthread_t thread, void **retval); |
pthread_detach | 分离线程 | int pthread_detach(pthread_t thread); |
pthread_exit | 终止线程 | void pthread_exit(void *retval); |
pthread_self | 获取线程ID | pthread_t pthread_self(void); |
pthread_equal | 比较线程ID | int pthread_equal(pthread_t t1, pthread_t t2); |
pthread_mutex_init | 初始化互斥锁 | int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); |
pthread_mutex_lock | 加锁 | int pthread_mutex_lock(pthread_mutex_t *mutex); |
pthread_mutex_trylock | 尝试加锁 | int pthread_mutex_trylock(pthread_mutex_t *mutex); |
pthread_mutex_unlock | 解锁 | int pthread_mutex_unlock(pthread_mutex_t *mutex); |
pthread_mutex_destroy | 销毁互斥锁 | int pthread_mutex_destroy(pthread_mutex_t *mutex); |
pthread_cond_init | 初始化条件变量 | int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr); |
pthread_cond_wait | 等待条件变量 | int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); |
pthread_cond_signal | 唤醒一个等待的线程 | int pthread_cond_signal(pthread_cond_t *cond); |
pthread_cond_broadcast | 唤醒所有等待的线程 | int pthread_cond_broadcast(pthread_cond_t *cond); |
pthread_cond_destroy | 销毁条件变量 | int pthread_cond_destroy(pthread_cond_t *cond); |
10.3 使用示例
1 |
|
11. 系统调用库
11.1 系统调用库概述
系统调用库(<sys/syscall.h>)提供了直接调用系统服务的函数,是操作系统与应用程序之间的接口。
11.2 常用函数
| 函数名 | 功能 | 原型 |
|---|---|---|
syscall | 直接调用系统调用 | long syscall(long number, ...); |
read | 读取文件 | ssize_t read(int fd, void *buf, size_t count); |
write | 写入文件 | ssize_t write(int fd, const void *buf, size_t count); |
open | 打开文件 | int open(const char *pathname, int flags, mode_t mode); |
close | 关闭文件 | int close(int fd); |
lseek | 设置文件位置 | off_t lseek(int fd, off_t offset, int whence); |
mkdir | 创建目录 | int mkdir(const char *pathname, mode_t mode); |
rmdir | 删除目录 | int rmdir(const char *pathname); |
chmod | 修改文件权限 | int chmod(const char *pathname, mode_t mode); |
chown | 修改文件所有者 | int chown(const char *pathname, uid_t owner, gid_t group); |
link | 创建硬链接 | int link(const char *oldpath, const char *newpath); |
symlink | 创建符号链接 | int symlink(const char *target, const char *linkpath); |
readlink | 读取符号链接 | ssize_t readlink(const char *pathname, char *buf, size_t bufsiz); |
unlink | 删除文件 | int unlink(const char *pathname); |
rename | 重命名文件 | int rename(const char *oldpath, const char *newpath); |
11.3 使用示例
1 |
|
12. 系统函数库的最佳实践
12.1 一般原则
- 包含正确的头文件:确保包含了所有需要的头文件
- 链接必要的库:如数学库(
-lm)、线程库(-lpthread)等 - 检查函数返回值:始终检查函数的返回值,处理错误情况
- 释放资源:确保所有分配的资源都被释放,如文件描述符、内存等
- 使用安全的函数:避免使用不安全的函数,如
gets、strcpy等 - 遵循函数的调用约定:正确传递参数,处理返回值
- 了解函数的限制:如缓冲区大小、参数范围等
- 使用适当的错误处理:如
perror、strerror等 - 测试边界情况:测试函数在边界情况下的行为
- 参考文档:查阅系统函数库的文档,了解函数的详细用法
12.2 性能优化
- 减少函数调用:对于频繁调用的函数,考虑内联或使用宏
- 缓存计算结果:对于计算密集型函数,缓存结果
- 使用适当的数据结构:选择高效的数据结构
- 避免不必要的转换:如字符串和数字之间的转换
- 使用编译器优化:如
-O2、-O3等优化选项
12.3 安全性
- 检查参数:验证所有函数参数的有效性
- 防止缓冲区溢出:使用安全的字符串处理函数
- 释放资源:确保所有分配的资源都被释放
- 避免使用不安全的函数:如
gets、strcpy等 - 使用地址随机化:编译时启用地址随机化
- 限制权限:最小化程序的权限
- 加密敏感数据:对敏感数据进行加密
- 防止注入攻击:如SQL注入、命令注入等
13. 系统函数库的跨平台兼容性
13.1 跨平台兼容性问题
- 头文件差异:不同平台的头文件可能不同
- 函数差异:不同平台的函数可能有不同的实现或行为
- 类型差异:不同平台的类型大小可能不同
- 系统调用差异:不同平台的系统调用可能不同
- 路径分隔符:不同平台的路径分隔符可能不同(
/vs\)
13.2 解决方案
- 使用条件编译:根据不同平台使用不同的代码
- 使用宏定义:定义平台相关的宏
- 使用抽象层:为平台相关的功能创建抽象层
- 使用跨平台库:如SDL、Boost等
- 测试多个平台:确保代码在多个平台上都能正常工作
- 参考标准:遵循C语言标准,避免使用非标准功能
13.3 示例
1 |
|
14. 系统函数库的调试
14.1 调试工具
- gdb:GNU调试器
- valgrind:内存分析工具
- strace:系统调用跟踪工具
- ltrace:库函数调用跟踪工具
- gprof:性能分析工具
- addr2line:地址转换工具
14.2 调试技巧
- 使用
printf:在关键位置添加打印语句 - 使用
assert:在关键位置添加断言 - 检查返回值:始终检查函数的返回值
- 使用调试器:使用gdb等调试器逐步执行代码
- 使用内存分析工具:使用valgrind等工具检查内存问题
- 使用系统调用跟踪:使用strace等工具跟踪系统调用
- 查看核心转储:分析程序崩溃时生成的核心转储文件
- 使用日志:使用日志记录程序的执行情况
14.3 常见问题
14.3.1 段错误
原因:访问了无效的内存地址
解决:使用gdb或valgrind检查内存访问
14.3.2 内存泄漏
原因:分配的内存未被释放
解决:使用valgrind检查内存泄漏
14.3.3 文件操作错误
原因:文件不存在、权限不足等
解决:检查文件路径、权限,使用perror查看错误信息
14.3.4 网络连接错误
原因:网络不可达、端口未开放等
解决:检查网络连接、防火墙设置,使用perror查看错误信息
15. 示例代码
15.1 综合示例:文件复制程序
1 |
|
15.2 综合示例:简单的HTTP服务器
1 |
|



