Linux0.11 kernel/exit.c中的free_page_tables()

上次看到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));就释放了当前任务的代码段和数据堆栈段。

你可能感兴趣的:(kernel)