linux内核从start_kernel到init

init/main.c文件中有个函数叫start_kernel,它是用来启动内核的主函数,我想大家都知道这个函数啦,而在该函数的最后将调用一个函数叫rest_init(),它执行完,内核就起来了,

asmlinkage void __init start_kernel(void)

{

......

/* Do the rest non-__init'ed, we're now alive */

rest_init();

}

现在我们来看一下rest_init()函数,它也在文件init/main.c中,它的前面几行是:

static void noinline __init_refok rest_init(void) __releases(kernel_lock)

{

int pid;

kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);

其中函数kernel_thread定义在文件arch/ia64/kernel/process.c中,用来启动一个内核线程,这里的kernel_init是要执行的函数的指针,NULL表示传递给该函数的参数为空,CLONE_FS | CLONE_SIGHANDdo_fork产生线程时的标志,表示进程间的fs信息共享,信号处理和块信号共享,然后我就屁颠屁颠地追随到kernel_init函数了,现在来瞧瞧它都做了什么好事,它的完整代码如下:

static int __init kernel_init(void * unused)

{

lock_kernel();

/*

* init can run on any cpu.

*/

set_cpus_allowed_ptr(current, CPU_MASK_ALL_PTR);

/*

* Tell the world that we're going to be the grim

* reaper of innocent orphaned children.

* We don't want people to have to make incorrect

* assumptions about where in the task array this

* can be found.

*/

init_pid_ns.child_reaper = current;

cad_pid = task_pid(current);

smp_prepare_cpus(setup_max_cpus);

do_pre_smp_initcalls();

smp_init();

sched_init_smp();

cpuset_init_smp();

do_basic_setup();

/*

* check if there is an early userspace init. If yes, let it do all

* the work

*/

if (!ramdisk_execute_command)

ramdisk_execute_command = "/init";

if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {

ramdisk_execute_command = NULL;

prepare_namespace();

}

/*

* Ok, we have completed the initial bootup, and

* we're essentially up and running. Get rid of the

* initmem segments and start the user-mode stuff..

*/

init_post();

return 0;

}

kernel_init函数的一开始就调用了lock_kernel()函数,当编译时选上了CONFIG_LOCK_KERNEL,就加上大内核锁,否则啥也不做,紧接着就调用了函数set_cpus_allowed_ptr,由于这些函数对init进程的调起还是有影响的,我们还是一个一个来瞧瞧吧,不要忘了啥东东最好,

static inline int set_cpus_allowed_ptr(struct task_struct *p,

const cpumask_t *new_mask)

{

if (!cpu_isset(0, *new_mask))

return -EINVAL;

return 0;

}

这函数其实就调用了cpu_isset宏,定义在文件"include/linux/cpumask.h中,如下:

#define cpu_isset(cpu, cpumask) test_bit((cpu), (cpumask).bits)

再来看看set_cpus_allowed_ptr的第二个参数类型吧,也定义在文件include/linux/cpumask.h中,具体如下:

typedef struct { DECLARE_BITMAP(bits, NR_CPUS); } cpumask_t;

接着尾随着DECLAR_BITMAP宏到文件include/linux/types.h中,定义如下:

#define DECLARE_BITMAP(name,bits) \

unsigned long name[BITS_TO_LONGS(bits)]

而宏BITS_TO_LONGS定义在文件include/linux/bitops.h中,实现如下:

#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))

DIV_ROUND_UP宏定义在文件include/linux/kernel.h中,BITS_PER_BYTE 宏定义在文件include/linux/bitops.h中,实现如下:

#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))

#define BITS_PER_BYTE 8

即当NR_CPUS132时,cpumask_t类型为

struct {

unsigned long bits[1];

}

然后来看看在set_cpus_allowed_ptr(current, CPU_MASK_ALL_PTR);中的 CPU_MASK_ALL_PTR宏,定义在include/linux/cpumask.h中:

#define CPU_MASK_ALL_PTR (&CPU_MASK_ALL)

CPU_MASK_ALL宏也定义在文件include/linux/cpumask.h中:

#define CPU_MASK_ALL \

(cpumask_t) { { \

[BITS_TO_LONGS(NR_CPUS)-1] = CPU_MASK_LAST_WORD \

} }

NR_CPUS宏定义在文件include/linux/threads.h中,实现如下:

#ifdef CONFIG_SMP

#define NR_CPUS CONFIG_NR_CPUS

#else

#define NR_CPUS 1

#endif

CPU_MASK_LAST_WORD宏定义在文件include/linux/cpumask.h中,实现如下:

#define CPU_MASK_LAST_WORD BITMAP_LAST_WORD_MASK(NR_CPUS)

BITMAP_LAST_WORD_MASK(NR_CPUS)宏定义在文件include/linux/bitmap.h中,实现如下:

#define BITMAP_LAST_WORD_MASK(nbits) \

( \

((nbits) % BITS_PER_LONG) ? \

(1UL<<((nbits) % BITS_PER_LONG))-1 : ~0UL \

)

NR_CPUS1时,CPU_MASK_LAST_WORD1

NR_CPUS2时,CPU_MASK_LAST_WORD2

NR_CPUSn时,CPU_MASK_LAST_WORD2n-1次方

有点晕了,我们现在把参数带入,即set_cpus_allowed_ptr(current, CPU_MASK_ALL_PTR)

-->cpu_isset(0,CPU_MASK_ALL_PTR)-->test_bit(0,CPU_MASK_ALL_PTR.bits)

即当NR_CPUSn时,就把usigned long bits[0]的第n位置1,应该就如注释所说的,init能运行在任何CPU上吧。

现在kernel_init中的set_cpus_allowed_ptr(current, CPU_MASK_ALL_PTR); 分析完了,我们接着往下看,首先 init_pid_ns.child_reaper = current; init_pid_ns定义在kernel/pid.c文件中

struct pid_namespace init_pid_ns = {

.kref = {

.refcount = ATOMIC_INIT(2),

},

.pidmap = {

[ 0 ... PIDMAP_ENTRIES-1] = { ATOMIC_INIT(BITS_PER_PAGE), NULL }

},

.last_pid = 0,

.level = 0,

.child_reaper = &init_task,

};

它是一个pid_namespace结构的变量,先来看看pid_namespace的结构,它定义在文件

include/linux/pid_namespace.h中,具体定义如下:

struct pid_namespace {

struct kref kref;

struct pidmap pidmap[PIDMAP_ENTRIES];

int last_pid;

struct task_struct *child_reaper;

struct kmem_cache *pid_cachep;

unsigned int level;

struct pid_namespace *parent;

#ifdef CONFIG_PROC_FS

struct vfsmount *proc_mnt;

#endif

};

即把当前进程设为接受其它孤儿进程的进程,然后取得该进程的进程ID,如:

cad_pid = task_pid(current);

然后调用 smp_prepare_cpus(setup_max_cpus);如果编译时没有指定CONFIG_SMP,它什么也不做,接着往下看,调用do_pre_smp_initcalls()函数,它定义在init/main.c文件中,实现如下:

static void __init do_pre_smp_initcalls(void)

{

extern int spawn_ksoftirqd(void);

migration_init();

spawn_ksoftirqd();

if (!nosoftlockup)

spawn_softlockup_task();

}

其中migration_init()定义在文件include/linux/sched.h中,具体实现如下:

#ifdef CONFIG_SMP

void migration_init(void);

#else

static inline void migration_init(void)

{

}

#endif

好像什么也没有做,然后是调用spawn_ksoftirqd()函数,定义在文件kernel/softirq.c中,代码如下:

__init int spawn_ksoftirqd(void)

{

void *cpu = (void *)(long)smp_processor_id();

int err = cpu_callback(&cpu_nfb, CPU_UP_PREPARE, cpu);

BUG_ON(err == NOTIFY_BAD);

cpu_callback(&cpu_nfb, CPU_ONLINE, cpu);

register_cpu_notifier(&cpu_nfb);

return 0;

}


你可能感兴趣的:(linux)