linux 0.12 分析,《linux 0.12内核完全剖析》

66b52468c121889b900d4956032f1009.png

8种机械键盘轴体对比

本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?

笔记重点:

重新分析进程的创建,发现最初的笔记还是比较简单,说起进程创建,不得不先了解进程0的创建。进程0之后创建的所有进程都是以这个进程为模板进行创建,进程0的创建过程是在系统启动的main.c中进行的,在系统初始化之前,手动设置这个进程0,更重要的是这个进程是在内核状态下切换到用户态运行的,所以之后的进程也都在用户态下运行;

其次需要理解进程的控制信息(PCB)和进程本身运行的内存空间的分配情况,这点在代码中要了解透彻,进程内核态的堆栈和用户态的堆栈的的分配是分开,同时需要了解他们的分布情况,这对了解用户态下进程如何调用系统调用的理解至关重要;同时一个进程的虚拟内存分布和实际的物理内存分配是分开的,理解这一点对理解linux的内存分配方式以及缺页中断的理解,写时复制机制的理解都很重要;

最后在谈到进程的调度,在此版本的调度中可以说非常简单,但是在进程调度却是linux系统一个非常大且重要的主题。

根据一下问题来看笔记进程占多大的线形地址空间

进程实际分配多少物理内存

创建进程的开销在哪里

一. 从fork系统调用开始

kernel/sys_call.s第222行1

2

3

4

5

6

7

8

9

10

11

12_sys_fork:

call _find_empty_process#为新进程分配id

testl %eax,%eax#测试是否分配了id

js 1f#id若为负数则返回

push %gs #压入copy_process需要的参数

pushl %esi

pushl %edi

pushl %ebp

pushl %eax

call _copy_process#调用copy_process函数

addl $20,%esp#丢弃压栈内容

1:ret

二. copy_process函数分析

作用: 复制当前进程的代码段和数据段以及环境

这里需要说明的是每个任务的线性地址为64M,每个任务的线性地址不重叠。

1. copy_process函数重点函数中新进程的状态改变,创建时设置为TASK_UNINTERRUPTIBLE,完成创建后,态设置为task_running

新进程的内核堆栈的设置以及新进程的返回值1

2

3

4p->tss.ep0 = PAGE_SIZE+ (long)p;

p->tss.ss0 = 0x10;//内核数据段选择子

...

p->tss.eax = 0;//新进程的返回值为0

下表是任务状态段(tss)的字段表格,这里可以参考任务状态段的描述,tss是task_struct中的一个字段.

16.png

进程的内核堆栈和用户堆栈的区别,内核堆栈分配在分配给task_struct结构的一页内存的顶端,也就是地址(long)p + PAGE_SIZE的位置,

进程的内核堆栈示意如图:

18.jpg

进程的用户堆栈示意图:

17.jpg设置进程的tss段和ldt段

代码参考fork.c第130行1

2

3

4

5//gdt为gdt表的首地址,nr<<1表示每个任务有两项状态段和局部段

//p->tss表示tss在task_struct中的偏移

//p->ldt表示ldt在task_struct中的偏移

set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));

set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt));

每个任务都有一个tss段和ldt段,存放在gdt表中,如下图所示:

15.jpg

2. copy_mem函数重点上面代码重点分析copy_mem函数中get_base函数的意思,首先来看段描述符的格式如下图:

13.jpg1

2

3

4

5

6

7

8

9

10

11

12

13

14

15unsigned long __base;

#段描述符有两个32位,%3代表 addr+7的内容,也就是上图第一行的基地址31~24的内容放到dh寄存器

__asm__("movb %3,%%dhnt"

#%2代表addr+4的内容,上图第一行基地址的内容放到dl寄存器

"movb %2,%%dlnt"

#上面两行构成dx的内容,右移16位空出低16位

"shll $16,%%edxnt"

#将addr+2的内容,上图第二行31~16的内容放到%dx,edx中构成了完整的段基址

"movw %1,%%dx"

:"=d" (__base)

:"m" (*((addr)+2)),

"m" (*((addr)+4)),

"m" (*((addr)+7)));

__base;})copy_mem函数的get_limit(0x0f)中0x0f的意思,段选择子的格式如下:

14.jpg

get_limit(0x0f)中的0x0f是段选择子,0x0f为 0000 0000 0000 1111,指定了LDT表中具有RPL=3,索引值为1,T1位为1,指定LDT表copy_mem函数代码分析:拷贝当前进程的页目录和页表给新进程

设置好新进程的ldt表1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31int (int nr,struct task_struct * p)

{

unsigned long old_data_base,new_data_base,data_limit;

unsigned long old_code_base,new_code_base,code_limit;

//0x0f为代码段选择子

//下面有对段选择子的说明

code_limit=get_limit(0x0f);

//0x17为数据段选择子

data_limit=get_limit(0x17);

//下面有重点分析

old_code_base = get_base(current->ldt[1]);

old_data_base = get_base(current->ldt[2]);

if (old_data_base != old_code_base)

panic("We don't support separate I&D");

if (data_limit < code_limit)

panic("Bad data_limit");

//进程线形地址的基地址为 64M X 进程号

new_data_base = new_code_base = nr * TASK_SIZE;

p->start_code = new_code_base;

//设置ldt[1]代码段的线性地址

set_base(p->ldt[1],new_code_base);

//设置ldt[2]数据段的线性地址

set_base(p->ldt[2],new_data_base);

//拷贝页表,在内存管理中详细说明

if (copy_page_tables(old_data_base,new_data_base,data_limit)) {

free_page_tables(new_data_base,data_limit);

return -ENOMEM;

}

return 0;

}

三. 进程管理(创建)依赖内存管理子系统

copy_process的第77行,用来分配一页物理内存(通常是4k)给新进程1

2

3struct task_struct* p;

...

p = (struct task_struct*)get_free_page();

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值