Linux 0.11总结

    1、内存分布示意图


    再附一张,描述符表。


    主要说一下全局描述符中的内容:

    每一项都是8个字节。

    第一项为全0,没有被使用。

    第二项为存储段描述符,DT=1,DPL=00,代码段描述符,基地址是0,段界限是16MB。

    第三项为存储段描述符,DT=1,DPL=00,数据段描述符,基地址是0,段界限是16MB。

    第四项为全0,没有被使用。

    第五项为系统段描述符,DT=0,DPL=00,进程0的任务状态段,基地址指向进程0的TSS的地址,段界限是进程0的TSS的长度。

    第六项为系统段描述符,DT=0,DPL=00,进程0的局部描述符表,基地址指向进程0的LDT的地址,段界限是进程0的LDT的长度。

    第七项 以后类推 进程1 进程2 ......


    最后形成如下图:


    中断描述符表如下:

    每一项都是门描述符,分为三种,一是中断门,TYPE为110,DPL为00。二是陷阱门,TYPE为111,DPL为00。三是系统门,TYPE为111,DPL为11。seletor都是0x08,选择了代码段描述符,基地址是0,段界限是16MB。偏移就是具体中断处理函数的偏移。


    进程0的LDT如下:

    第一项为全零。

    第二项为存储段描述符,DT=1,DPL=11,代码段描述符,基地址是0,段界限是640KB。

    第三项为存储段描述符,DT=1,DPL=11,数据段描述符,基地址是0,段界限是640KB


    进程1的LDT如下:

    第一项为全零。

    第二项为存储段描述符,DT=1,DPL=11,代码段描述符,基地址是64MB,段界限是640KB。

    第三项为存储段描述符,DT=1,DPL=11,数据段描述符,基地址是64MB,段界限是640KB。


    进程2的LDT如下:

    第一项为全零。

    第二项为存储段描述符,DT=1,DPL=11,代码段描述符,基地址是128MB,段界限是640KB。

    第三项为存储段描述符,DT=1,DPL=11,数据段描述符,基地址是128MB,段界限是640KB。

    以此类推。

    但进程2后来执行了如下代码:

void init(void)
{
	int pid,i;
        ...
	if (!(pid=fork())) {//进程1创建进程2
		close(0);
		if (open("/etc/rc",O_RDONLY,0))
			_exit(1);
		execve("/bin/sh",argv_rc,envp_rc);
		_exit(2);
	}
       	if (pid>0)
		while (pid != wait(&i))
			/* nothing */;
        ...
}

    那么:shell的LDT如下:

    第一项为全零。

    第二项为存储段描述符,DT=1,DPL=11,代码段描述符,基地址是128MB,段界限是shell程序的实际长度。

    第三项为存储段描述符,DT=1,DPL=11,数据段描述符,基地址是128MB,段界限是64MB(这样页目录项会占从32~48的位置)。


    2、下面介绍80386寄存器

    内核态,CS选择器 TI=0,表示从GDT表中选择,RPL=00,内核态,描述符索引是1,选择GDT表中第二个代码段描述符。   

    内核态,SS,DS,ES,FS,GS选择器 TI=0,表示从GDT表中选择,RPL=00,内核态,描述符索引是2,选择GDT表中第三个数据段描述符。 

    用户态,CS选择器 TI=1,表示从LDT表中选择,RPL=11,用户态,描述符索引是1,选择LDT表中第二个代码段描述符。

    用户态,SS,DS,ES,FS,GS选择器 TI=1,表示从LDT表中选择,RPL=11,用户态,描述符索引是2,选择LDT表中第三个数据段描述符。 

    LDTR选择器,TI=1,表示从GDT表中选择,RPL=00,内核态,描述符索引和进程有关,进程0的描述符索引是3,进程1的描述符索引是5,选择GDT表中第四个,或者第六个系统段描述符。依次类推。

    TR选择器,TI=1,表示从GDT表中选择,RPL=00,内核态,描述符索引和进程有关,进程0的描述符索引是4,进程1的描述符索引是6,选择GDT表中第五个,或者第七个系统段描述符。依次类推。

    描述符高速缓存寄存器和LDTR高速缓存寄存器和TR高速缓存寄存器,0-31存放段首地址,0-19存放段界限,0-11存放段属性。就是分别对应上面取得的存储段描述符和系统段描述符(也是64位)。


    GDTR,0~31位存放的是内存分配图中GDT(全局描述符表)的基地址,0~15位存放的是内存分配图中GDT(全局描述符表)的界限。

    IDTR,0~31位存放的是内存分配图中IDT(中断描述符表)的基地址,0~15位存放的是内存分配图中IDT(中断描述符表)的界限。


    3、特权级切换的本质:

    中断int 0x80从特权级3进入特权级0,并把ss,esp,eflags,cs,eip等寄存器保存在特权级0的堆栈中(TSS的esp0,ss0),iret从特权级0返回特权级3。

    由于seletor都是0x08,表示是内核态的cs选择器,从GDT中取描述符,所以中断处理程序已经处于0特权级,内核态。压栈是ss也0x10,后来ds,es,fs,gs也变成了0x10。

    所以特权级变化的本质是,cs,ds,es,fs,gs,ss的不同,特权级0从GDT中取得描述符,前面这些寄存器后3位为000,描述符特权级为00;特权级3从LDT中取得描述符,前面这些寄存器后3位为111,描述符特权级为11。


    4、进程切换的本质:

    把当前的寄存器状态保存在当前进程的TSS中,把要切换的进程的寄存器恢复成要切换的进程的TSS中的内容。要着重理解LDTR选择器,TR选择器。LDTR选择器在GDT表中找出了当前进程的LDTR高速缓存,也就是LDT表的基地址和界限。TR选择器在GDT表中找出了当前进程的TR高速缓存,也就是TSS表的基地址和界限。


    5、中断的本质:

    中断和特权级切换的过程类似,只是中断可能发生在用户态或者内核态,而特权级切换只发生在用户态。当中断发生在内核态时,压入特权级0堆栈的cs,esp,eflags,cs,eips是内核态的寄存器值。


    6、创建进程的本质:

    获取了一页内存如下图:

    

    (1) task_struct是复制父进程的数据得来的。会修改pid,father,tss中数据eip,cs指向的是父进程将要执行的下行代码。ldt中数据直接继承下来,后面还要修改。

    (2)设置子进程ldt代码段和数据段的基地址为nr * 64MB,段界限为640KB。

    (3)设置进程的页目录项,进程0是第一项;进程1是第16项,进程2是第32项。先获取一页内存,再复制父进程的页表项到这个内存中。这样父子进程共享实际内存。

    (4)子进程也继承了父进程的pwd,root,filp。计数加1。

    (5)设置tast_stuct中的tss,ldt在gdt中系统段描述符。把tss,ldt的基地址和段界限放入gdt中。

    父进程fork返回非0,子进程fork返回0,但都是执行fork后面的代码,可以通过if语句来区分。


    7、执行程序的本质:

    首先释放原进程页目录项和页表项,然后重新建立起页目录项和页表项,最后返回到用户空间执行。具体参考通过进程2加载shell进程,详解execve


    8、进程调度的本质:

    一种是时钟中断,每10ms一次,但是只有发生时钟时,处于用户态,才会产生进程切换。

    一种是进程在内核态主动调用schedue()。


    9、schedue函数:

    找出counter最大的就绪进程运行。如果没有就绪的进程就切换到进程0怠速。如果有就绪的进程但counter值都为0,那么就重新分配时间片。


    10、进程状态TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE。

    TASK_INTERRUPTIBLE如果当前进程接收到信号,那么schedue时会把这个进程变成TASK_RUN。但是TASK_UNINTERRUPTIBLE,是不会理睬信号的。

    TASK_UNINTERRUPTIBLE只能由wake_up唤醒,变成TASK_RUN。

    一般在pause时会把进程设置为TASK_INTERRUPTIBLE。在sleep_on时会把进程设置为TASK_UNINTERRUPTIBLE。


    11、信号的本质

    发送信号:就是在目标进程的signal,对应的设置为1。

    接收信号:一种在系统调用返回之前检测当前进程是否接收到信号;另一种方式是时钟中断发生后,其中断服务程序执行结束之前,检测当前进程是否接收到信号。

    会根据signal检测是否接收到信号,并根据signal找到current->sigaction + signr - 1找到对应的信号处理函数。

    从系统调用返回后先执行信号处理函数,然后再转到发生系统调用的下一条指令执行。参考Linux内核设计的艺术-进程间通信-信号


    12、管道的本质

    管道的本质是操作系统在内存中为每个管道开辟一页内存,给这一页内存赋予了文件的属性。这一页内存由两个进程共享。

    inode->i_size=get_free_page();

    f[0]->f_inode = f[1]->f_inode = inode;


    13、put_page的本质:

    unsigned long put_page(unsigned long page,unsigned long address)

    address是线性地址,page指向页面的地址。

    执行完函数后,会建立从线性地址到页面的地址的映射。也就是说建立了页目录项和页表项。然后线性地址-->分页机制-->实际物理页面的地址。


    14、write的本质

    首先写入缓冲区,b_dirt为1,然后再同步到硬盘中。


    15、bread_page的本质

    首先从硬盘的数据写入缓冲区,然后再复制缓冲区的数据到新申请的页面。


    16,Linux2.4,申请的页面(换入)用于存放可执行的代码,用于存放要同步到硬盘的数据,用于task_struct,页表。应该是用buffer_head统一管理。有申请就有释放,就有页面不够用的情况,就要释放,以便能重新用于申请。这就是所谓的换出。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值