操作系统:系统调用(System Calls)
目录
User Mode 和 Kernel Mode(用户态 & 内核态)
User Mode(用户态)
Kernel Mode(内核态)
系统调用是如何进行的?
系统调用(System Call)是应用程序与操作系统之间通信的桥梁。
简单说,用户程序不能直接访问硬件资源(比如硬盘、内存、打印机),它们必须通过系统调用来“请求”操作系统帮它做这些事。
为什么需要系统调用?
因为出于安全与控制的原因,用户程序:
-
不能直接控制硬件;
-
不能直接修改内存;
-
不能访问其他用户的数据。
必须通过系统调用进入操作系统控制下,才可以进行敏感操作。
举例几个常见的系统调用:
功能 | system call 示例(Linux) | 说明 |
---|---|---|
文件操作 | open() , read() , write() , close() | 打开、读写文件 |
进程管理 | fork() , exec() , exit() | 创建或结束进程 |
内存管理 | mmap() , brk() | 请求内存、释放内存 |
设备操作 | ioctl() | 与硬件交互 |
网络通信 | socket() , bind() , send() | 网络通信功能 |
注意:这些是 用户程序通过库函数(如 C 的 stdio.h
)封装后调用的系统接口,底层最终都要通过系统调用与操作系统打交道。
User Mode 和 Kernel Mode(用户态 & 内核态)
系统调用离不开一个概念:处理器的运行模式(CPU Modes)
🔁 系统调用触发“模式切换”
当程序执行系统调用时,操作系统会让 CPU 从:
User Mode(用户态) → Kernel Mode(内核态)
完成请求后,再切换回:
Kernel Mode → User Mode
模式 | 中文 | 权限 | 用于谁? |
---|---|---|---|
User Mode | 用户态 | 受限,不能直接操作硬件 | 普通应用程序运行 |
Kernel Mode | 内核态 | 权限最高,可访问所有资源 | 操作系统核心运行 |
操作系统通过 CPU 的硬件支持,实现两种运行模式。
User Mode(用户态)
User Mode 是程序在“受限环境下”运行的状态。用户程序不能访问内核内存或硬件设备,只能通过系统调用间接访问。
运行于用户态的对象:
-
普通应用程序:浏览器、微信、QQ、游戏
-
命令行程序、写字板等
-
用户自定义代码(如你写的 C/C++ 程序)
用户态的限制:
-
不能访问硬件(如显卡、硬盘、键盘);
-
不能直接控制内存管理;
-
不能调用系统内核函数(除非通过系统调用);
Kernel Mode(内核态)
Kernel Mode 是操作系统内核在运行时的状态,具有最高权限,可访问所有硬件与系统资源。
运行于内核态的对象:
-
操作系统核心(内核);
-
设备驱动程序(驱动显卡、硬盘等);
-
系统调用服务程序;
-
内核线程、调度器、内存管理器等。
内核态的能力:
能力 | 描述 |
---|---|
直接访问硬件 | 读写内存、磁盘、网络等 |
控制进程 | 创建、销毁、调度进程 |
管理内存 | 分配、释放虚拟内存 |
安全检查 | 检查权限、保护数据 |
⚠️ 内核态的风险:
-
一旦出错,可能导致整个系统崩溃(蓝屏);
-
所以,只有操作系统核心部分允许运行在内核态;
-
用户程序无法自行进入内核态,只能“请求”操作系统进入并代为处理。
发生了什么?
-
你打开一个程序(比如写字板);
-
它运行在 User Mode;
-
当你点击“保存文件”时,需要写入硬盘;
-
程序发出一个 系统调用;
-
CPU 切换到 Kernel Mode(因为要访问硬盘);
-
操作系统执行写文件操作;
-
完成后切换回 User Mode,程序继续运行。
👉 这个切换非常迅速,受 CPU 控制。
模式切换流程图:
┌────────────┐
│ User Mode │ ← 运行用户程序
└────┬───────┘│系统调用(例如 write)▼
┌────────────┐
│ Kernel Mode│ ← 进入操作系统内核,执行敏感操作
└────┬───────┘│操作完成,返回用户程序▼
┌────────────┐
│ User Mode │ ← 程序继续运行
└────────────┘
系统调用是如何进行的?
📄 实例:使用系统调用从一个文件读取数据,并复制到另一个文件中
这个例子是操作系统中最常见的文件操作场景。下面我们一步步分析这个过程所涉及的 系统调用(System Call)序列,同时讲清楚每一步背后发生了什么。
写一个程序完成如下操作:📥 从一个“输入文件”读取数据 → 📤 复制到“输出文件”
整体步骤与系统调用对照
1️⃣ Acquire Input Filename(获取输入文件名)
-
功能描述:程序向用户询问“请输入要读取的文件名”。
-
系统调用:
-
write()
→ 把提示写到屏幕 -
read()
→ 从键盘接收输入(标准输入)
-
write(1, "Enter input filename: ", 23); // 输出提示
read(0, buffer, 100); // 从用户读取输入
2️⃣ Acquire Output Filename(获取输出文件名)
-
同样使用:
-
write()
→ 输出提示 -
read()
→ 获取输入
-
3️⃣ Open Input File(尝试打开输入文件)
-
系统调用:
open(filename, O_RDONLY)
-
说明:
-
打开文件用于读取(Read Only)
-
如果文件不存在,返回错误码 → 程序调用
exit()
终止
-
int fd_in = open("input.txt", O_RDONLY);
if (fd_in < 0) {write(1, "File not found.\n", 16);exit(1); // 终止程序
}
4️⃣ Create Output File(创建输出文件)
-
系统调用:
open(filename, O_CREAT | O_EXCL | O_WRONLY, 0644)
-
说明:
-
O_CREAT
: 创建新文件 -
O_EXCL
: 如果文件已存在则失败 -
O_WRONLY
: 只写模式 -
0644
: 设置权限
-
如果目标文件已存在,则中止(避免覆盖)
int fd_out = open("output.txt", O_CREAT | O_EXCL | O_WRONLY, 0644);
if (fd_out < 0) {write(1, "Output file exists.\n", 21);exit(1);
}
5️⃣ Read from Input File(从输入文件读取数据)
-
系统调用:
read(fd_in, buffer, size)
-
循环读取直到文件末尾
char buffer[1024];
int bytesRead;
while ((bytesRead = read(fd_in, buffer, 1024)) > 0) {write(fd_out, buffer, bytesRead); // 同时写入输出文件
}
6️⃣ Write to Output File(写入输出文件)
-
系统调用:
write(fd_out, buffer, bytesRead)
-
使用
write()
把读取到的数据写入目标文件
7️⃣ Close Output File(关闭文件)
-
系统调用:
close(fd_in)
和close(fd_out)
8️⃣ Write Completion Message to Screen(输出完成信息)
-
系统调用:
write(1, "Copy complete.\n", 15);
告知用户复制成功
9️⃣ Terminate Normally(正常终止程序)
-
系统调用:
exit(0);
全流程总结图
系统调用就像“操作系统服务窗口”——你的程序必须通过这些窗口递交表单(open、read、write、exit等),操作系统审核通过后才会帮你操作硬件完成任务。