> 技术文档 > C语言与操作系统交互探秘

C语言与操作系统交互探秘


系统调用与库函数

在 C语言中,系统调用是用户程序与操作系统内核交互的桥梁。以下是常见系统调用的概述:

  • 文件操作类open()read()write()close()lseek()
  • 进程控制类fork()exec()wait()exit()
  • 信号处理类signal()kill()
  • 进程间通信pipe()shmget()msgget()
  • 网络通信socket()bind()listen()accept()

系统调用 vs 库函数

  • 系统调用是操作系统提供的接口,进入内核态执行
  • 库函数是对系统调用的封装(如 fread 封装了 read)
  • 系统调用开销大但能访问底层资源,库函数效率高且跨平台

工作原理

  1. 用户程序通过特定指令(如 int 0x80syscall)触发系统调用
  2. CPU 切换到内核态执行对应的内核服务例程
  3. 内核处理请求并返回结果
  4. 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 模型

  1. 阻塞 I/O:操作直到完成才返回
  2. 非阻塞 I/O:立即返回,需轮询结果
  3. I/O 多路复用:通过 select/poll/epoll 同时监控多个文件描述符
  4. 异步 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() 可以传递附加数据
  • 信号是一种简单但有限的进程间通信方式