进程存储相关的关键数据结构
以下是Linux内核中进程存储相关的关键数据结构与代码解析:
1. 核心结构体定义(内核源码截取)
task_struct(进程描述符)
// include/linux/sched.h
struct task_struct {
volatile long state; // 进程状态(TASK_RUNNING等)
struct mm_struct *mm; // 用户空间内存管理器
struct task_struct __rcu *parent; // 父进程指针
pid_t pid; // 进程ID
struct files_struct *files; // 打开文件表
struct vm_struct *stack; // 内核栈指针(与用户栈分离)
// ... 其他200+个字段(调度、信号、命名空间等)
};
mm_struct(内存描述符)
// include/linux/mm_types.h
struct mm_struct {
struct vm_area_struct *mmap; // VMA链表头
struct rb_root mm_rb; // VMA红黑树根节点(加速查找)
pgd_t * pgd; // 页全局目录(对应CR3寄存器值)
unsigned long start_code, end_code; // 代码段起止地址
unsigned long start_data, end_data; // 数据段
unsigned long start_brk, brk; // 堆区域当前边界
unsigned long start_stack; // 用户栈起始地址
// ... 引用计数、内存策略等字段
};
vm_area_struct(虚拟内存区域)
// include/linux/mm_types.h
struct vm_area_struct {
unsigned long vm_start; // 区域起始地址
unsigned long vm_end; // 区域结束地址
struct mm_struct *vm_mm; // 所属内存描述符
pgprot_t vm_page_prot; // 访问权限(RWX)
unsigned long vm_flags; // 属性标志(VM_READ等)
struct file * vm_file; // 映射文件指针(如.so文件)
// ... 反向映射、匿名映射等字段
};
2. 关键操作代码示例
进程创建时复制内存描述符(fork实现)
// kernel/fork.c
static int copy_mm(unsigned long clone_flags, struct task_struct *tsk)
{
if (clone_flags & CLONE_VM) { // 共享地址空间(线程场景)
atomic_inc(¤t->mm->mm_users);
tsk->mm = current->mm;
tsk->active_mm = current->mm;
} else {
tsk->mm = dup_mm(tsk); // 复制父进程mm_struct
if (!tsk->mm)
return -ENOMEM;
}
return 0;
}
内存扩展(brk系统调用实现)
// mm/mmap.c
SYSCALL_DEFINE1(brk, unsigned long, brk)
{
struct mm_struct *mm = current->mm;
unsigned long newbrk = PAGE_ALIGN(brk);
// 锁定内存描述符
down_write(&mm->mmap_sem);
// 检查堆扩展合法性
if (check_data_rlimit(rlimit(RLIMIT_DATA), newbrk, mm->start_brk))
goto out;
// 调用内部函数调整堆区域
ret = do_brk(mm->start_brk, newbrk - mm->start_brk);
mm->brk = brk;
out:
up_write(&mm->mmap_sem);
return ret;
}
查找虚拟内存区域(缺页中断处理)
// mm/memory.c
struct vm_area_struct *find_vma(struct mm_struct *mm, unsigned long addr)
{
struct rb_node *rb_node;
struct vm_area_struct *vma;
// 通过红黑树快速查找
rb_node = mm->mm_rb.rb_node;
while (rb_node) {
vma = rb_entry(rb_node, struct vm_area_struct, vm_rb);
if (addr < vma->vm_start)
rb_node = rb_node->rb_left;
else if (addr >= vma->vm_end)
rb_node = rb_node->rb_right;
else
return vma;
}
return NULL;
}
3. 用户态观测工具示例
查看进程内存映射(/proc/PID/maps解析)
# 示例输出(对应vm_area_struct链表)
00400000-00401000 r-xp 00000000 08:01 123456 /path/to/program # 代码段
00600000-00602000 rw-p 00000000 08:01 123456 /path/to/program # 数据段
7f8d12345000-7f8d12367000 r-xp 00000000 08:01 7890 /lib/x86_64-linux-gnu/libc.so.6 # 共享库
7ffeefbff000-7ffeefc00000 rw-p 00000000 00:00 0 [stack] # 用户栈
内核调试示例(通过crash工具)
crash> ps -p 1234 # 查找目标进程的task_struct地址
PID PPID CPU TASK ST %MEM VSZ RSS COMM
1234 1212 0 ffff88003d45a000 IN 0.2 23456 4567 myapp
crash> struct mm_struct ffff88003d45a000.mm # 查看mm_struct
struct mm_struct {
mmap = 0xffff88003a12b000,
mm_rb = {
rb_node = 0xffff88003a12b0a0
},
pgd = 0xffff88003d123000,
start_code = 0x400000,
end_code = 0x401000,
start_data = 0x600000,
end_data = 0x602000,
// ...其他字段
}
crash> list vm_area_struct -l 0xffff88003a12b000 # 遍历VMA链表
ffff88003a12b000
vm_start = 0x400000
vm_end = 0x401000
vm_flags = VM_READ | VM_EXEC
vm_file = 0xffff88003d456000 ("/usr/bin/myapp")
4. 物理内存映射关键函数
页表项设置(x86_64架构)
// arch/x86/mm/pgtable.c
void set_pte_vaddr(unsigned long vaddr, pte_t pteval)
{
pgd_t *pgd = __va(read_cr3_pa()); // 获取当前进程的PGD
pud_t *pud = pud_offset(pgd, vaddr);
pmd_t *pmd = pmd_offset(pud, vaddr);
pte_t *pte = pte_offset_kernel(pmd, vaddr);
set_pte(pte, pteval); // 设置具体页表项
__flush_tlb_one_kernel(vaddr); // 刷新TLB
}
关键数据流图示
用户进程虚拟地址空间
|
|-- 通过vm_area_struct描述每个区域属性
V
mm_struct (管理整个地址空间)
|
|-- pgd指向页表根目录
V
物理页框 (通过页表项PTE连接)
通过以上代码和结构体,内核精确控制用户进程的内存生命周期。开发调试时可结合/proc文件系统和内核工具进行深度观测。