C语言与操作系统交互探秘
系统调用与库函数
在 C语言中,系统调用是用户程序与操作系统内核交互的桥梁。以下是常见系统调用的概述:
- 文件操作类:
open()
、read()
、write()
、close()
、lseek()
- 进程控制类:
fork()
、exec()
、wait()
、exit()
- 信号处理类:
signal()
、kill()
- 进程间通信:
pipe()
、shmget()
、msgget()
- 网络通信:
socket()
、bind()
、listen()
、accept()
系统调用 vs 库函数:
- 系统调用是操作系统提供的接口,进入内核态执行
- 库函数是对系统调用的封装(如
fread
封装了read
) - 系统调用开销大但能访问底层资源,库函数效率高且跨平台
工作原理:
- 用户程序通过特定指令(如
int 0x80
或syscall
)触发系统调用 - CPU 切换到内核态执行对应的内核服务例程
- 内核处理请求并返回结果
- CPU 切回用户态继续执行
以下是一个简单的系统调用示例:
#include #include #include int main() { int fd; char buffer[20] = \"Hello, World!\\n\"; // 系统调用:打开文件 fd = open(\"test.txt\", O_WRONLY | O_CREAT, 0644); if (fd == -1) { perror(\"open failed\"); return 1; } // 系统调用:写入文件 write(fd, buffer, 14); // 系统调用:关闭文件 close(fd); return 0;}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
文件操作与 I/O 系统
C语言提供了两套文件操作接口:
- 标准 I/O 库:
fopen()
、fread()
、fwrite()
、fclose()
等 - 系统调用:
open()
、read()
、write()
、close()
等
标准 I/O 与系统调用的关系:
- 标准 I/O 基于系统调用实现,提供缓冲机制
- 系统调用直接操作文件描述符,无缓冲
文件系统基础:
- inode:存储文件元数据(权限、所有者、时间戳等)
- 目录结构:目录文件存储文件名到 inode 的映射
- 文件描述符表:每个进程维护一个打开文件的描述符表
I/O 模型:
- 阻塞 I/O:操作直到完成才返回
- 非阻塞 I/O:立即返回,需轮询结果
- I/O 多路复用:通过
select
/poll
/epoll
同时监控多个文件描述符 - 异步 I/O:操作完成后通过回调通知
以下是一个使用 select
实现的 I/O 多路复用示例:
#include #include #include #include int main() { fd_set readfds; struct timeval timeout; int fd_stdin = fileno(stdin); char buffer[100]; while (1) { // 清空文件描述符集 FD_ZERO(&readfds); // 添加标准输入到监控集 FD_SET(fd_stdin, &readfds); // 设置超时时间 timeout.tv_sec = 5; timeout.tv_usec = 0; // 调用 select 监控 int result = select(fd_stdin + 1, &readfds, NULL, NULL, &timeout); if (result < 0) { perror(\"select failed\"); exit(EXIT_FAILURE); } else if (result == 0) { printf(\"Timeout occurred!\\n\"); } else { // 标准输入就绪,读取数据 if (FD_ISSET(fd_stdin, &readfds)) { fgets(buffer, sizeof(buffer), stdin); printf(\"Read: %s\", buffer); } } } return 0;}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
信号处理机制
信号基本概念:
- 信号是软件中断,用于通知进程发生了异步事件
- 常见信号:SIGINT(2)、SIGTERM(15)、SIGKILL(9)、SIGSEGV(11)
信号处理方式:
- 默认处理:执行系统预设动作(如终止进程)
- 忽略信号:不做任何处理
- 捕获信号:注册信号处理函数
信号处理函数注意事项:
- 只能调用异步信号安全的函数
- 避免执行复杂操作
- 使用 volatile sig_atomic_t 变量进行进程间通信
以下是一个信号处理的示例:
#include #include #include #include volatile sig_atomic_t flag = 0;// 信号处理函数void sigint_handler(int signum) { if (signum == SIGINT) { printf(\"\\nCaught SIGINT, setting flag...\\n\"); flag = 1; }}int main() { // 注册信号处理函数 if (signal(SIGINT, sigint_handler) == SIG_ERR) { perror(\"signal registration failed\"); return 1; } printf(\"Running... Press Ctrl+C to exit.\\n\"); // 主循环 while (!flag) { printf(\"Working...\\n\"); sleep(1); } printf(\"Exiting gracefully...\\n\"); return 0;}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
信号在进程间通信中的应用:
- 使用
kill()
函数向指定进程发送信号 - 使用
sigqueue()
可以传递附加数据 - 信号是一种简单但有限的进程间通信方式