Appearance
题目
某进程的虚拟地址空间布局如下图所示(阴影部分为未占用区域):
图(文字版):从高地址到低地址依次为
- 操作系统内核区(最高)
- 用户栈
- (未占用阴影)
- 共享库的存储映射区
- (未占用阴影)
- 动态生成的堆(运行时由 malloc 创建)
- 读/写数据段
- 只读代码段
- 未使用区(最低)

有 C 程序:
c
char *ptr;
void main() {
int length;
ptr = (char *) malloc(100);
scanf("%s", ptr);
length = strlen(ptr);
printf("length=%d\n", length);
free(ptr);
}请回答:
(1) 上述程序执行时,PCB 位于哪个区域?执行 scanf() 等待键盘输入时,进程处于什么状态?
(2) main() 函数的代码位于哪个区域?其直接调用的哪些函数的功能需要通过执行驱动程序实现?
(3) 变量 ptr 被分配在哪个区域?若变量 length 没有被分配在寄存器中,则会被分配在哪个区域?ptr 指向的字符串位于哪个区域?
解析
(1)PCB 位置 + scanf 时进程状态
PCB 位于内核区
PCB(Process Control Block)= 进程控制块——是操作系统用来管理进程的核心数据结构(包含 PID、状态、寄存器现场、页表指针、文件表等)。它是操作系统的私有数据,必须放在用户进程不能直接访问的地方——操作系统内核区。
编者注(关键区分):
- PCB 在内核区——用户看不到、改不了
- 用户栈、堆、代码段都在用户区——用户程序可见、可读写(受权限控制)
- 这是 CPU "两态分离"在内存布局上的体现
scanf 等待时进程状态:阻塞态
scanf() 等待键盘输入是等待 I/O 完成——属于"等待外部事件"型阻塞。
进程状态变化:
题目问"等待键盘输入时"——这一刻进程处于 阻塞态。
(2)main 代码位置 + 哪些函数走驱动
main() 代码 → 只读代码段(.text)
C 编译后所有函数的机器指令都放在程序的"代码段"——也称 .text 段,运行时被加载到只读代码段。只读是为了防止程序意外或恶意修改自己的代码。
哪些被调用函数需要驱动程序?
逐个分析:
| 调用 | 功能 | 是否走驱动 | 用什么驱动 |
|---|---|---|---|
malloc(100) | 分配堆内存 | ❌ | 通过 brk/mmap 系统调用,但不经过设备驱动 |
scanf("%s", ptr) | 从键盘读 | ✅ | 键盘驱动 |
strlen(ptr) | 计算字符串长度 | ❌ | 纯 CPU 计算 |
printf("length=%d\n", length) | 向屏幕输出 | ✅ | 显示器驱动(终端 / 控制台驱动) |
free(ptr) | 释放堆内存 | ❌ | 通过 brk/munmap 系统调用,不走设备驱动 |
→ scanf() 和 printf() 需要驱动程序实现。
编者注(关键概念区分):
- 系统调用 ≠ 设备驱动调用:malloc 走系统调用(brk)但不涉及驱动;scanf 既走系统调用(read)又触发设备驱动(键盘驱动)
- "涉及外设的"才需要驱动:键盘、屏幕、磁盘、网卡都是外设;内存分配是 CPU + MMU 的事,不算外设
(3)三个变量的存储区域
| 变量 | 在哪 | 理由 |
|---|---|---|
ptr | 读/写数据段(.data/.bss) | char *ptr; 是全局变量(在 main 外定义),全局变量进数据段 |
length | 用户栈 | int length; 是 main 内部的局部变量,局部变量入栈(除非编译器优化进寄存器) |
ptr 指向的字符串 | 堆区 | malloc(100) 在堆上分配;ptr 只是栈外的"指针",指针变量和指针指向的目标位置可以不同 |
编者注(典型陷阱:指针 vs 指针指向的目标):
cchar *ptr; // ptr 自己在 .data ptr = malloc(100); // ptr 指向 heap 的某 100 字节这里有 3 个东西容易搞混:
ptr这个变量本身(4 / 8 字节的指针,在数据段)ptr的值(堆上某个地址,如 0x7f1234)ptr指向的内容(堆上 100 字节)题目问"ptr 在哪"是问 1,问"ptr 指向的字符串在哪"是问 3。两者位置不同,是这道题的核心考察点。
如果 ptr 是局部变量
char *ptr; void main() { ptr = ... }——则 ptr 在栈上,不在数据段。本题里 ptr 在 main 外面定义,所以是全局变量。
编者注(关于内存布局的总结):
区 内容 谁创建 谁释放 内核区 PCB、内核代码 / 数据 OS OS 用户栈 局部变量、函数参数、返回地址 编译器自动 编译器自动 共享库映射 libc 等动态库 加载器 加载器 堆 malloc / new 分配的对象 程序员(malloc) 程序员(free) 读/写数据段 全局变量、静态变量 链接器 进程退出时 只读代码段 函数代码、字符串字面量 链接器 进程退出时 Linux 严格按这个布局,本题的图是简化版(把 .data 和 .bss 合成"读/写数据段",省了 PLT/GOT 等小区域)。考研掌握这张表就够了。