最近free download一个Linux kernel
void __init console_init(void)
{
initcall_t *call;
/* Setup the default TTY line discipline. */
(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
/*
* set up the console device so that later boot sequences can
* inform about problems etc..
*/
call = __con_initcall_start;
while (call <>
(*call)();
call++;
}
}
__con_initcall_start这个变量你就是找遍所有C和汇编代码你也找不到它的原型。它是出现在内核编译的链接阶段,是一个编程语言中的标记,或者说是一个地址,在这里也确实表现为地址,__con_initcall_start和__con_initcall_end一起前后界定了了代码段中.con_initcall.init。
#define console_initcall(fn) \
static initcall_t __initcall_##fn \
__attribute_used__ __attribute__((__section__(".con_initcall.init")))=fn
搞这个macro声明了很多已赋值的函数指针,如32位CPU那么就占用4个字节,console_init就是循环运行这些函数指针多对应的函数。这个架构真是太好了,很多驱动程序只要用这个macro就可以声明自己,而内核初始化部分就无须直接关心有多少console驱动程序做这样的声明,靠着强大的编译器就可以帮助你了解到。还是GCC,LD厉害,微软的编译器功能太少,灵活度不高(至少微软对外放出的编译器是这样,说不定他们内部有很高级的)。不过毕竟微软不希望你利用他们的操作系统再去开发别的系统。呵呵。
所以分析内核代码是一定要想象这段代码的运行环境,否则你很难理解作者到底要干什么。文境切换代码更是如此,经过schedule函数直接就到另外一个进程去了,很神奇。还有在SMP环境下,一个函数直接会分叉,编程同时在多个CPU运行。今天就看了这点东西,以后再遇到什么好玩的东西,再和大家分享。
没有评论:
发表评论