自从2.6内核放出以后,对于内核爱好者来说,每天都有享不尽的大餐,太令人激动了,每个版本都会推出新东西,本文就很简单的两个方面来讨论一下,一个是2.6内核以后在进入系统空间SAVE_ALL的时候,将ds寄存器设置为了__USER_DS
#define SAVE_ALL /
cld; /
pushl %es; /
pushl %ds; /
pushl %eax; /
pushl %ebp; /
pushl %edi; /
pushl %esi; /
pushl %edx; /
pushl %ecx; /
pushl %ebx; /
movl $(__USER_DS), %edx; /
movl %edx, %ds; /
movl %edx, %es;
这 里将ds和es设置成了__USER_DS,这岂不是胡闹吗?记住,linux之所以设置什么ds,cs段选择符也是例行公事,完全没有必要,要明白为什 么这里将ds设置成__USER_DS,还要先看看ds是干什么用的,ds是数据段寄存器,cs是代码段寄存器(涉及到intel寄存器的可见部分和不可 见部分,这个不谈),ds的保护作用是:如果你访问的段,要求的访问级别高于当前进程的代码段级别的话,访问会导致GP异常。现在进入了内核,当前代码段 的访问级别为0,处于最高级别,它访问任何段都不会出错,再者,linux为了减少复杂性,只用了2个级别,那么就没有任何问题了,如果要像intel规 定的那样将ds寄存器设置成 __KERNEL_DS,那么看看从内核返回的时候:恢复CS,EIP的值,此时CS的CPL是3.因为DS,ES被设为了__KERNEL_DS,所以其DPL是0,所以要将DS,ES中的值清除.这样就要一个清除动作,损失了效率,因此就先将ds段寄存器设置成__USER_DS,这么做的一切都是 intel的什么狗屁分段机制造成的,汗!另外在创建内核线程的时候还有一处诡异也是这个道理:
int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
{
struct pt_regs regs;
memset(®s, 0, sizeof(regs));
regs.ebx = (unsigned long) fn;
regs.edx = (unsigned long) arg;
regs.xds = __USER_DS;//一会ret_from_fork的时候要RESTORE_ALL
regs.xes = __USER_DS;
regs.orig_eax = -1;
regs.eip = (unsigned long) kernel_thread_helper;
regs.xcs = __KERNEL_CS;
regs.eflags = X86_EFLAGS_IF | X86_EFLAGS_SF | X86_EFLAGS_PF | 0x2;
/* Ok, create the new process.. */
return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL);
}
ENTRY(ret_from_fork)
pushl %eax
call schedule_tail
GET_THREAD_INFO(%ebp)
popl %eax
jmp syscall_exit
...
syscall_exit:
cli # make sure we don't miss an interrupt
# setting need_resched or sigpending
# between sampling and the iret
movl TI_flags(%ebp), %ecx
testw $_TIF_ALLWORK_MASK, %cx # current->work
jne syscall_exit_work
restore_all:
RESTORE_ALL
#define RESTORE_ALL / #这里RESTORE_ALL的正是上面伪造的regs现场
RESTORE_REGS /
addl $4, %esp; /
1: iret; /
#define RESTORE_REGS /
RESTORE_INT_REGS; /
1: popl %ds; /
2: popl %es; /
.section .fixup,"ax"; /
3: movl $0,(%esp); /
jmp 1b; /
4: movl $0,(%esp); /
jmp 2b; /
.previous; /
.section __ex_table,"a";/
.align 4; /
.long 1b,3b; /
.long 2b,4b; /
.previous
上面就是说2.6内核已经不再用__KERNEL_DS了,因为没有任何意义。(传统的intel段寄存器里面会有一些“门”,linux包括windows并没有用那么
多复杂的专为老式系统设计的“门”)
说完了这个再说说内核线程的创建,以前都是直接调用kernel_thread就完事了,最新的2.6.26和27内核倒好,专门为创建内核线程在系统保 留了一个内核守护线程,这下好了,你要创建一个内核线程也要向这个守护线程请求,然后排队,唤醒这个守护线程,这样这个守护线程会替你把内核线程创建出来,linux对进程/线程真是情有独钟啊!重要的是,进程/线程拥有上下文,于是它就可以接受系统的管理,这样就不会有那么多的限制,管理起来更方便, 毕竟将创建内核线程这件事情给统一管理了,但是我担心的是。这会不会出现单点故障。唉,分离和统一,一对矛盾啊,当你觉得不能再进一步统一时,就尝试分离吧!前面的博客还说windows喜欢将一切纳入自己的“管理器”,现在linux也差不多了,但是不同的是,windows钟情于自己设计的对象,一切 都是对象,所以管理器都是对象管理器,而linux更钟情于一个古老的概念----进程,实际上,linux能进程/线程化的都进程/线程化了。
设计上的东西,说起来没完,就此打住,且待下回!