上次看到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));