《Linux启动过程分析》内核启动init进程

2.6.35.11为mstar801平台使用内核版本;也为第一次比较系统学习内核使用版本。在此留念!

一、0号进程idle进程启动,这是系统唯一不通过do_fork创建的进程

kernel2.6.35.11/init/main.c

asmlinkage void __init start_kernel(void)  //内核线程,0号进程idle进程
{  
  ......  
  tick_init();
  boot_cpu_init();
  page_address_init();   //内存管理相关     kernel2.6.35.11/mm/highmem.c
  ......
  setup_arch(&command_line);  //内核解析uboot参数就在这步;内核页表  kernel2.6.35.11/arch/arm/kernel/setup.c
  mm_init_owner(&init_mm,&init_task);   //kernel2.6.35.11/kernel/fork.c
  mm_init_cpumask(&init_mm);
  ......
  page_alloc_init();    //内存页表相关  kernel2.6.35.11/mm/page_alloc.c
  ......
  vfs_caches_init(totalram_pages); //rootfs创建,并将rootfs设置为系统根文件系统、rootfs根目录设置为系统根目录;这部是决定驱动加载关键
  ......
  mm_init();  //构建整个页表 kernel2.6.35.11/kernel/fork.c 
  sched_init();    //进程管理相关  kernel2.6.35.11/kernel/sched.c
  ......
  init_timers();  //内核定时器   kernel2.6.35.11/kernel/timer.c
  hrtimers_init();  //内核定时器  kernel2.6.35.11/kernel/timer.c
  ......
  timekeeping_init();
  time_init();
  ......
  page_cgroup_init();  //内核页表相关 kernel2.6.35.11/mm/page_cgroup.c
  ......
  fork_init(totalram_pages);  //kernel2.6.35.11/kernel/fork.c
  proc_caches_init();  //kernel2.6.35.11/kernel/fork.c
  ......
  signals_init();  //信号量相关  kernel2.6.35.11/kernel/signal.c
  ......
  rest_init();
}

如上对理解流程比较重要的是如下三个函数:

setup_arch(&command_line):完成对uboot传入参数的解析;

vfs_caches_init(totalram_pages):rootfs的建立,并将rootfs设置为系统根文件系统、rootfs根目录为系统根目录;

rest_init():完成驱动及内核静态模块(ramdisk释放到rootfs;如果rootfs没有/init还需要完成磁盘文件系统的挂载及重新设置系统根文件系统)加载;用户进程init的启动。

1.将rootfs挂载至自己的“/”目录,见Linux内核启动之根文件系统挂载分析

rootfs:它不属于任何设备(包括虚拟内存盘设备initrd)的文件系统,既不是flash的文件系统、也不是虚拟内存盘的文件系统。

引入虚拟rootfs的原因如下:

磁盘(包括初始RAM磁盘initrd)文件系统的挂载需要它们的设备文件、即需要它们的驱动被加载;但它们驱动的加载,又必须有要文件系统、以便创建设备节点。这是矛盾的!

所以,rootfs的目的是创建目录树,如创建/dev、以供设备文件的创建。

kernel2.6.35.11/fs/Dcache.c

void __init vfs_caches_init(unsigned long mempages)
{
  ......
  mnt_init();
  ......
}

kernel2.6.35.11/fs/Namespace.c

void __init mnt_init(void)
{
  ......
  err = sysfs_init();  //注册sysfs至内核
  init_rootfs();  //注册虚拟rootfs至内核
  init_mount_tree();  //创建rootfs的根目录“/”,并将rootfs挂载至该目录
}
static void __init init_mount_tree(void)
{
  ......
  mnt = do_kern_mount("rootfs", 0, "rootfs", NULL); 
  ......
  set_fs_root(current->fs, &root); //设置rootfs为系统的根文件系统,rootfs的根目录为系统的根目录
}

说明:这部结束以后,就可以加载驱动程序了、因为驱动程序需要在根文件系统上创建设备节点。

2.磁盘(包括初始RAM磁盘initrd)文件系统的挂载、重新设置系统根目录和根文件系统以及init、kthreadd进程启动

0号进程:内核IDLE进程

1号进程:用户空间init进程,所有用户空间进程的父进程

2号进程:内核空间kthreadd进程,所有内核kthread_create出的进程的父进程(除了idle进程)

kernel2.6.35.11/init/main.c

static noinline void __init_refok rest_init(void)
{
  ......
  kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);  //内核线程
  ......
  pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);  //内核线程
  ......
  init_idle_bootup_task(current);
  ......
  schedule();  //kernel2.6.35.11/kernel/sched.c 进程调度
  ......
}

二、由内核进程0创建的内核线程装载可执行程序init,成为一个普通进程——1号进程

kernel2.6.35.11/init/main.c

static int __init kernel_init(void * unused)
{
  ......
  do_basic_setup();  //初始化设备驱动,加载静态内核模块;释放ramdisk到rootfs
  ......
  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(); //加载磁盘文件系统;也即磁盘的文件系统挂载至rootfs的/root目录,并重新设备系统根文件系统和根目录
  }
  init_post(); //启动init进程
  ......
}

1.内核静态模块加载、驱动加载等

kernel2.6.35.11/init/main.c

static void __init do_basic_setup(void)
{
  cpuset_init_smp();
  usermodehelper_init();
  init_tmpfs();
  driver_init();  //设备驱动初始化 kernel2.6.35.11/drivers/base/init.c
  init_irq_proc();
  do_ctors();
  do_initcalls();  //加载内核模块
}
static void __init do_initcalls(void)
{
  ......
  for (fn = __early_initcall_end; fn < __initcall_end; fn++)
    do_one_initcall(*fn);
  ......
}

在do_initcalls中,是否支持ramdisk取决与kernel2.6.35.11/init/Makefile;以下是释放ramdisk至rootfs:

kernel2.6.35.11/init/initramfs.c

rootfs_initcall(populate_rootfs);
static int __init populate_rootfs(void)
{
  char *err = unpack_to_rootfs(__initramfs_start, __initramfs_size);  //加压cpio包
  ......
  if (initrd_start) {  //uboot将启动ramdisk文件系统拷贝到了initrd_start处
    ......
    err = unpack_to_rootfs((char *)initrd_start,
			initrd_end - initrd_start);
    ......
    fd = sys_open((const char __user __force *) "/initrd.image",
			      O_WRONLY|O_CREAT, 0700);
    if (fd >= 0) {
      sys_write(fd, (char *)initrd_start,
					initrd_end - initrd_start);
      sys_close(fd);
      free_initrd();
    }
    ......
  }
  return 0;
}

注意:此时系统的根文件系统还是rootfs,系统根目录还是rootfs的根目录

补充:设备及总线驱动

kernel2.6.35.11/drivers/base/init.c

void __init driver_init(void)
{
  /* These are the core pieces */
  devtmpfs_init();
  devices_init();  //kernel2.6.35.11/drivers/core.c
  buses_init();   //kernel2.6.35.11/drivers/bus.c
  classes_init();
  firmware_init();
  hypervisor_init();
  /* These are also core pieces, but must come after the
	 * core core pieces.
   */
  platform_bus_init();  //kernel2.6.35.11/drivers/platform.c
  system_bus_init();  //kernel2.6.35.11/drivers/sys.c 
  cpu_dev_init();
  memory_dev_init();
}

2.如果系统当前根文件系统也就是rootfs文件系统中没有init,就要挂载其他磁盘文件系统了

kernel2.6.35.11/init/do_mounts.c

void __init prepare_namespace(void)
{
  ......
  if (root_delay) {
    printk(KERN_INFO "Waiting %dsec before mounting root device...\n",
		       root_delay);
    ssleep(root_delay);
  }
  ......
  wait_for_device_probe();
  ......
  if (saved_root_name[0]) {
    root_device_name = saved_root_name;
    if (!strncmp(root_device_name, "mtd", 3) ||
		    !strncmp(root_device_name, "ubi", 3)) {
      mount_block_root(root_device_name, root_mountflags);
      goto out;
    }
    ROOT_DEV = name_to_dev_t(root_device_name);
    if (strncmp(root_device_name, "/dev/", 5) == 0)
      root_device_name += 5;
  }
  if (initrd_load())
    goto out;
  if ((ROOT_DEV == 0) && root_wait) {
    printk(KERN_INFO "Waiting for root device %s...\n",
			saved_root_name);
    while (driver_probe_done() != 0 ||
			(ROOT_DEV = name_to_dev_t(saved_root_name)) == 0)
      msleep(100);
    async_synchronize_full();
  }
  ......
  mount_root();//将磁盘文件系统挂载到rootfs的/root目录
  sys_chroot(".");//将系统的根文件系统和根目录切换为磁盘的文件系统和目录、也就是rootfs的/root目录
}

上处代码执行完后,系统的根文件系统将从rootfs切换到实际的、同时系统根目录也会切换!

3.init进程启动

kernel2.6.35.11/init/main.c

static noinline int init_post(void)
{
  ......
  if (ramdisk_execute_command) {
    (void) sys_dup(0);
    run_init_process(ramdisk_execute_command);
    printk(KERN_WARNING "Failed to execute %s\n",
                                ramdisk_execute_command);
  } 
  ......
}
static void run_init_process(const char *init_filename)
{
  argv_init[0] = init_filename;
  kernel_execve(init_filename, argv_init, envp_init);    //内核空间调用用户空间函数kernel_execve
}

kernel2.6.35.11/arch/arm/kernel/sys_arm.c

int kernel_execve(const char *filename,
                  const char *const argv[],
                  const char *const envp[])
{
        struct pt_regs regs;
        int ret;

        memset(®s, 0, sizeof(struct pt_regs));
        ret = do_execve(filename,
                        (const char __user *const __user *)argv,
                        (const char __user *const __user *)envp, ®s);
        if (ret < 0)
                goto out;

        /*
         * Save argc to the register structure for userspace.
         */
        regs.ARM_r0 = ret;

        /*
         * We were successful.  We won't be returning to our caller, but
         * instead to user space by manipulating the kernel stack.
         */
        asm(    "add    r0, %0, %1\n\t"
                "mov    r1, %2\n\t"
                "mov    r2, %3\n\t"
                "bl     memmove\n\t"    /* copy regs to top of stack */
                "mov    r8, #0\n\t"     /* not a syscall */
                "mov    r9, %0\n\t"     /* thread structure */
                "mov    sp, r0\n\t"     /* reposition stack pointer */
                "b      ret_to_user"
                :
                : "r" (current_thread_info()),
                  "Ir" (THREAD_START_SP - sizeof(regs)),
                  "r" (®s),
                  "Ir" (sizeof(regs))
                : "r0", "r1", "r2", "r3", "ip", "lr", "memory");

 out:
        return ret;
}

注意:上处代码决定了init进程是一个用户态进程;即进程调度它执行时ARM是在用户模式、Linux是在用户态。

三、由内核进程0创建的内核线程kthreadd——2号进程

注意:区别与init进程(用户态普通进程),该进程是一个内核进程;主要作用是管理调度其他的内核线程。

kernel2.6.35.11/kernel/kthread.c

int kthreadd(void *unused)
{
  ......
  struct kthread_create_info *create;
  create = list_entry(kthread_create_list.next,
                                            struct kthread_create_info, list);
  list_del_init(&create->list);
  create_kthread(create);
  ......
}
static void create_kthread(struct kthread_create_info *create)
{
  int pid;
#ifdef CONFIG_NUMA
  current->pref_node_fork = create->node;
#endif
  /* We want our own signal handler (we take no signals by default). */
  pid = kernel_thread(kthread, create, CLONE_FS | CLONE_FILES | SIGCHLD);
  if (pid < 0) {
    create->result = ERR_PTR(pid);
    complete(&create->done);
  }
}

补充说明,附:

一、内核空间执行用户空间的一段应用程序有两种方法:

1. call_usermodehelper

2. kernel_execve

二、kernel_thread函数实现

kernel2.6.35.11/arch/arm/kernel/process.c

pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
    struct pt_regs regs;

    memset(®s, 0, sizeof(regs));

    regs.ARM_r4 = (unsigned long)arg;
    regs.ARM_r5 = (unsigned long)fn;
    regs.ARM_r6 = (unsigned long)kernel_thread_exit;
    regs.ARM_r7 = SVC_MODE | PSR_ENDSTATE | PSR_ISETSTATE;
    regs.ARM_pc = (unsigned long)kernel_thread_helper;
    regs.ARM_cpsr = regs.ARM_r7 | PSR_I_BIT;

    return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, ®s, 0, NULL, NULL);
}
EXPORT_SYMBOL(kernel_thread);

三、kthreadd进程簇

1.keventd进程

执行keventd_wq工作队列中的函数。

2.kapmd

处理与高级电源管理相关的事件。

3.kswapd

执行内存周期回收。

4.pdflush

刷新“脏”缓冲区的内容到磁盘以回收内存。

5.kblockd

执行kblockd_workqueue工作队列中的函数。实质上,它周期性地激活块设备驱动程序。

6.ksoftirqd

运行tasklet;系统中每个CPU都有这样一个内核线程。

四、关于内核函数do_fork

  其实,供用户创建进程的系统调用fork()函数的响应函数sys_fork()、sys_clone、sys_vfork都是通过调用内核函数do_fork()来实现的。

即:在内核编程中使用内核函数kernel_thread创建内核线程与在用户空间编程中使用fork传见用户线程是一样的。

1.jb/bionic/libc/bionic/fork.c

#include <unistd.h>
#include "pthread_internal.h"
#include "bionic_pthread.h"
#include "cpuacct.h"
extern int  __fork(void);
int  fork(void)
{
  ......
  int  ret;
  ret = __fork();
  ......
}

2.jb/bionic/libc/arch-arm/syscalls/__fork.S

ENTRY(__fork)
    .save   {r4, r7}
    stmfd   sp!, {r4, r7}
    ldr     r7, =__NR_fork
    swi     #0
    ldmfd   sp!, {r4, r7}
    movs    r0, r0
    bxpl    lr
    b       __set_syscall_errno
END(__fork)

============================================

3.系统调用表

kernel2.6.35.11/arch/arm/include/asm/unistd.h

#define __NR_fork                       (__NR_SYSCALL_BASE+  2)

kernel2.6.35.11/arch/arm/kernel/calls.S

CALL(sys_fork_wrapper)

4.实现

kernel2.6.35.11/arch/arm/kernel/entry-common.S

sys_fork_wrapper:
                add     r0, sp, #S_OFF
                b       sys_fork
ENDPROC(sys_fork_wrapper)

kernel2.6.35.11/arch/arm/kernel/sys_arm.c

asmlinkage int sys_fork(struct pt_regs *regs)
{
#ifdef CONFIG_MMU
        return do_fork(SIGCHLD, regs->ARM_sp, regs, 0, NULL, NULL);
#else
        /* can not support in nommu mode */
        return(-EINVAL);
#endif
}

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值