上次看到do_exit中的free_page_tables(),因为其中牵扯到了内存管理中的分页和分段,看了两天终于明白了。今天继续看我们的init进程,就又回到free_page_tables。
free_page_tables(get_base(current->ldt[1]),get_limit(0x0f));
free_page_tables(get_base(current->ldt[2]),get_limit(0x17));
首先看下他调用的参数。
1、get_base(current->ldt[1])和get_base(current->ldt[2])
A.get_base()函数的定义:
| #define get_base(ldt) _get_base( ((char *)&(ldt)) ); |
| _get_base的代码如下: |
| #define _get_base(addr) ({\ |
| unsigned long __base; \ |
| __asm__( |
| //将基地址的24-31位保存在dh中 |
| "movb %3,%%dh\n\t" \ |
| //将基地址的16-23位保存在dl中 |
| "movb %2,%%dl\n\t" \ |
| //将edx的内容左移16位 |
| //也就是将基地址的16-31位保存在edx的高16位中 |
| "shll $16,%%edx\n\t" \ |
| //将基地址的0-15位保存在dx |
| //也就是 edx的低16位中 |
| "movw %1,%%dx" \ |
| //将返回值保存在_base中 |
| :"=d" (__base) \ |
| //使用内存变量偏移2个字节作为%1 |
| //也就是基地址的0-15位 |
| :"m" (*((addr)+2)), \ |
| //使用内存变量偏移4个字节作为%2 |
| //也就是基地址的16-23位 |
| "m" (*((addr)+4)), \ |
| //使用内存变量偏移7个字节作为%3 |
| //也就是基地址的24-31位 |
| "m" (*((addr)+7))); \ |
| __base;}) |
因此get_base函数的作用就是取出段选择符指向的段描述符中的段基址。
B.current->ldt[1]
根据kernel/sched.c中的定义:struct task_struct *current=&(init_task,task);
而task_struct的定义:
| struct task_struct { |
| /* these are hardcoded - don't touch */ |
| long state; /* -1 unrunnable, 0 runnable, >0 stopped */ |
| long counter; |
| long priority; |
| long signal; |
| struct sigaction sigaction[32]; |
| long blocked; /* bitmap of masked signals */ |
| /* various fields */ |
| int exit_code; |
| unsigned long start_code,end_code,end_data,brk,start_stack; |
| long pid,father,pgrp,session,leader; |
| unsigned short uid,euid,suid; |
| unsigned short gid,egid,sgid; |
| long alarm; |
| long utime,stime,cutime,cstime,start_time; |
| unsigned short used_math; |
| /* file system info */ |
| int tty; /* -1 if no tty, so it must be signed */ |
| unsigned short umask; |
| struct m_inode * pwd; |
| struct m_inode * root; |
| struct m_inode * executable; |
| unsigned long close_on_exec; |
| struct file * filp[NR_OPEN]; |
| /* ldt for this task 0 - zero 1 - cs 2 - ds&ss */ |
| struct desc_struct ldt[3]; |
| /* tss for this task */ |
| struct tss_struct tss; |
| }; |
因此current->ldt[1]和current->ldt[2]分别指向当前任务的代码段CS局部表描述符和数据堆栈段DS&SS局部表描述符。
2、get_limit(0x0f)和get_limit(0x17)
| #define get_limit(segment) ({ \ |
| unsigned long __limit; \ |
| __asm__( |
| //lsll为加载段界限的指令 |
| //即把segment段描述符中的段界限字段装入寄存器 |
| //这里指把segment所指的段选择符中的段限界字段装载到%0寄存器中 |
| "lsll %1,%0\n\t" |
| //将段限界+1,因为0代表1B |
| "incl %0\n\t" |
| //将返回值存放在_limit中 |
| :"=r" (__limit) |
| //将segment 作为参数 |
| :"r" (segment)); |
| __limit;})\ |
| 参数segment是段选择符,get_base函数的作用就是取得segment指向的段描述符中的Limit段限长。 |
而段选择符的结构前面已经看过。由三个部分组成Index(15-3)+TI(2)+RPL(1-0)。其中Index为段描述符在描述符表中的索引值,TI标志指向的描述符表,RPL标志优先级。
这里采用的两个参数0x0f即1111B,因此他指向的段优先级为3,存储在LDT表中,索引为1。也就是当前任务段的代码段描述符。
另外一个参数0x17即10111B,因此他指向的段优先级为3,存储在LDT表中,索引为2.也就是当前任务的数及堆栈段描述符。
这样,通过get_limit(0x0f)和get_limit(0x17)就得到了当前任务代码段和数据堆栈段的长度。
因此,
free_page_tables(get_base(current->ldt[1]),get_limit(0x0f));free_page_tables(get_base(current->ldt[2]),get_limit(0x17));就释放了当前任务的代码段和数据堆栈段。

浙公网安备 33010602011771号