LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 5020|回复: 10

Mandrake9.0的启动过程(从init开始)(一)

[复制链接]
发表于 2003-1-31 18:25:37 | 显示全部楼层 |阅读模式
以阅读源代码的方式研究 linux的启动过程,是我早已有之的心愿。今天总算是开工了。由于理解系统初始化过程要有汇编的基础,所以我只好先从init开始。
init的源代码在/usr/src/linux-2.4.19-9mdk/init目录下,在这个目录下共有三个文件do_mounts.c、main.c和version.c。其中main.c就是init进程的源代码。这段代码并不长,只有640行。
首先用ctags -x main.c 生成一个tags文件,用vi 打开后,可以看到各个函数的索引:

LPS_PREC             macro       183  main.c         #define LPS_PREC 8
MAX_INIT_ARGS        macro       125  main.c         #define MAX_INIT_ARGS 8
MAX_INIT_ENVS        macro       126  main.c         #define MAX_INIT_ENVS 8
__KERNEL_SYSCALLS__  macro        12  main.c         #define __KERNEL_SYSCALLS__
argv_init            variable    135  main.c         static char *
argv_init[MAX_INIT_ARGS+2] = { "init", NULL, };
calibrate_delay      function    185  main.c         void __init
calibrate_delay(void)
checksetup           function    160  main.c         static int __init
checksetup(char *line)
child_reaper         variable    498  main.c         struct task_struct
*child_reaper = &init_task;
cols                 variable    131  main.c         int rows, cols;
debug_kernel         function    226  main.c         static int __init
debug_kernel(char *str)
do_basic_setup       function    521  main.c         static void __init
do_basic_setup(void)
do_initcalls         function    500  main.c         static void __init
do_initcalls(void)
envp_init            variable    136  main.c         char *
envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, };
execute_command      variable    133  main.c         char *execute_command;
gr_setup             function    148  main.c         static int __init
gr_setup(char *str)
init                 function    603  main.c         static int init(void
*unused)
loops_per_jiffy      variable    178  main.c         unsigned long
loops_per_jiffy = (1<<12);
parse_options        function    254  main.c         static void __init
parse_options(char *line)
profile_setup        function    138  main.c         static int __init
profile_setup(char *str)
quiet_kernel         function    234  main.c         static int __init
quiet_kernel(char *str)
rest_init            function    389  main.c         static void rest_init(void)
rows                 variable    131  main.c         int rows, cols;
smp_init             function    349  main.c         static void __init
smp_init(void)
smp_init             function    361  main.c         static void __init
smp_init(void)
smp_init             macro       354  main.c         #define smp_init() do { }
while (0)
start_kernel         function    401  main.c         asmlinkage void __init
start_kernel(void)
wait_init_idle       variable    344  main.c         unsigned long
wait_init_idle;

有了这个索引后,查找函数就方便了。再用vi 打开main.c,找到init函数,如下:

  1. static int init(void * unused)
  2. {
  3.         lock_kernel();
  4.         do_basic_setup();

  5.         prepare_namespace();
  6. #ifdef CONFIG_GRKERNSEC
  7.         grsecurity_init();
  8. #endif
  9.         /*
  10.          * Ok, we have completed the initial bootup, and
  11.          * we're essentially up and running. Get rid of the
  12.          * initmem segments and start the user-mode stuff..
  13.          */
  14.         free_initmem();
  15.         unlock_kernel();

  16.         if (open("/dev/console", O_RDWR, 0) < 0)
  17.                 printk("Warning: unable to open an initial console.\n");

  18.         (void) dup(0);
  19.         (void) dup(0);

  20.         /*
  21.          * We try each of these until one succeeds.
  22.          *
  23.          * The Bourne shell can be used instead of init if we are
  24.          * trying to recover a really broken machine.
  25.          */

  26.         if (execute_command)
  27.                 execve(execute_command,argv_init,envp_init);
  28.         execve("/sbin/init",argv_init,envp_init);
  29.         execve("/etc/init",argv_init,envp_init);
  30.         execve("/bin/init",argv_init,envp_init);
  31.         execve("/bin/sh",argv_init,envp_init);
  32.         panic("No init found.  Try passing init= option to kernel.");
复制代码

在源代码中,可以看到很多如同#ifdef
CONFIG_GRKERNSEC的宏定义,这些宏定义可以在/usr/src/linux-2.4.19-9mdk/目录下的.config文件中找到。用vi
查看.config文件中的宏定义,发现"# CONFIG_GRKERNSEC is not
set",也就是没有定义,因此,这个宏定义可以不管它。先来看执行流程。

一、do_basic_setup()函数
init进程第一个执行的函数是lock_kernel(),这个函数在很多内核的源代码中都有,但我没有找到它的函数定义,只好放弃。
第二个执行的函数就是do_basic_setup(),这个函数的内容如下:

  1. /*
  2. * Ok, the machine is now initialized. None of the devices
  3. * have been touched yet, but the CPU subsystem is up and
  4. * running, and memory and process management works.
  5. *
  6. * Now we can finally start doing some real work..
  7. */
  8. static void __init do_basic_setup(void)
  9. {

  10.         /*
  11.          * Tell the world that we're going to be the grim
  12.          * reaper of innocent orphaned children.
  13.          *
  14.          * We don't want people to have to make incorrect
  15.          * assumptions about where in the task array this
  16.          * can be found.
  17.          */
  18.         child_reaper = current;

  19. #if defined(CONFIG_MTRR)        /* Do this after SMP initialization */
  20. /*
  21. * We should probably create some architecture-dependent "fixup after
  22. * everything is up" style function where this would belong better
  23. * than in init/main.c..
  24. */
  25.         mtrr_init();
  26. #endif   /*mtrr(Memory Type Range Register)是Inter P6系列处理器用来控制处理器读写内存范围的。*/

  27. #ifdef CONFIG_SYSCTL
  28.         sysctl_init();
  29. #endif        /* 对/proc文件系统和sysctl()系统调用相关部分进行初始化*/

  30.         /*
  31.          * Ok, at this point all CPU's should be initialized, so
  32.          * we can start looking into devices..
  33.          */
  34. #if defined(CONFIG_ARCH_S390)
  35.         s390_init_machine_check();
  36. #endif

  37. #ifdef CONFIG_PCI
  38.         pci_init();
  39. #endif   /* 初始化PCI总线 */
  40. #ifdef CONFIG_SBUS
  41.         sbus_init();
  42. #endif
  43. #if defined(CONFIG_PPC)
  44.         ppc_init();
  45. #endif
  46. #ifdef CONFIG_MCA
  47.         mca_init();
  48. #endif
  49. #ifdef CONFIG_ARCH_ACORN
  50.         ecard_init();
  51. #endif
  52. #ifdef CONFIG_ZORRO
  53.         zorro_init();
  54. #endif
  55. #ifdef CONFIG_DIO
  56.         dio_init();
  57. #endif
  58. #ifdef CONFIG_NUBUS
  59.         nubus_init();
  60. #endif
  61. #ifdef CONFIG_ISAPNP
  62.         isapnp_init();
  63. #endif  /* 对ISA总线即插即用初始化 */
  64. #ifdef CONFIG_TC
  65.         tc_init();
  66. #endif

  67.         /* Networking initialization needs a process context */
  68.         sock_init();  /* 初始化网络协议栈 */

  69.         start_context_thread();
  70.         do_initcalls();

  71. #ifdef CONFIG_IRDA
  72.         irda_proto_init();
  73.         irda_device_init(); /* Must be done after protocol initialization */
  74. #endif
  75. #ifdef CONFIG_PCMCIA
  76.         init_pcmcia_ds();                /* Do this last */
  77. #endif
  78. }
复制代码

很明显,这段代码是用来进行对系统初始化的。开头的一段注释告诉我们,系统硬件此时只有cpu子系统在运转,内存管理和进程管理也开始工作了。接下来,就是对硬件的初始化。
这一部分与硬件密切相关,在编译核心时,将根据配置文件.config来编译相应的部分。用vi查看.config文件,发现定义的项目如下:

CONFIG_MTRR=y
CONFIG_SYSCTL=y

CONFIG_PCI=y
# CONFIG_PCI_GOBIOS is not set
# CONFIG_PCI_GODIRECT is not set
CONFIG_PCI_GOANY=y
CONFIG_PCI_BIOS=y
CONFIG_PCI_DIRECT=y
CONFIG_PCI_NAMES=y
CONFIG_PCI_HERMES=m

# CONFIG_SBUS is not set
# CONFIG_MCA is not set
CONFIG_ISAPNP=y

CONFIG_TCIC=y
CONFIG_TC35815=m

CONFIG_IRDA=m
CONFIG_IRDA_ULTRA=y
CONFIG_IRDA_CACHE_LAST_LSAP=y
CONFIG_IRDA_FAST_RR=y
# CONFIG_IRDA_DEBUG is not set

CONFIG_PCMCIA=m
CONFIG_PCMCIA_AHA152X=m
CONFIG_PCMCIA_FDOMAIN=m
CONFIG_PCMCIA_NINJA_SCSI=m
CONFIG_PCMCIA_QLOGIC=m
CONFIG_PCMCIA_HERMES=m
CONFIG_PCMCIA_3C589=m
CONFIG_PCMCIA_3C574=m
CONFIG_PCMCIA_FMVJ18X=m
CONFIG_PCMCIA_PCNET=m
CONFIG_PCMCIA_AXNET=m
CONFIG_PCMCIA_NMCLAN=m
CONFIG_PCMCIA_SMC91C92=m
CONFIG_PCMCIA_XIRC2PS=m
CONFIG_PCMCIA_IBMTR=m
CONFIG_PCMCIA_XIRCOM=m
CONFIG_PCMCIA_XIRTULIP=m
CONFIG_PCMCIA_RAYCS=m
CONFIG_PCMCIA_NETWAVE=m
CONFIG_PCMCIA_WAVELAN=m
CONFIG_PCMCIA_WVLAN=m
CONFIG_PCMCIA_SERIAL_CS=m

呵呵,这样一看,mandrake缺省配置的东西真不少,就连我根本用不上的IRDA和PCMCIA都编译成模块了。有了这些代码后,在开机时,就会看到这些启动信息:

[root@c4 linux-2.4.19-9mdk]#dmesg
......
mtrr: v1.40 (20010327) Richard Gooch (rgooch@atnf.csiro.au)
mtrr: detected mtrr type: Intel
PCI: PCI BIOS revision 2.10 entry at 0xfdb81, last bus=3
PCI: Using configuration type 1
PCI: Probing PCI hardware
Unknown bridge resource 0: assuming transparent
PCI: Using IRQ router PIIX [8086/2440] at 00:1f.0
isapnp: Scanning for PnP cards...
isapnp: No Plug & Play device found
Linux NET4.0 for Linux 2.4
......

二、prepare_namespace()函数
接下来要执行的是prepare_namespace()函数。这个函数在/usr/src/linux-2.4.19-9mdk/init/do_mounts.c文件中。内容如下:

  1. /*
  2. * Prepare the namespace - decide what/where to mount, load ramdisks, etc.
  3. */
  4. void prepare_namespace(void)
  5. {
  6.         int is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;
  7. #ifdef CONFIG_ALL_PPC
  8.         extern void arch_discover_root(void);
  9.         arch_discover_root();
  10. #endif /* CONFIG_ALL_PPC */
  11. #ifdef CONFIG_BLK_DEV_INITRD
  12.         if (!initrd_start)
  13.                 mount_initrd = 0;
  14.         real_root_dev = ROOT_DEV;
  15. #endif
  16.         sys_mkdir("/dev", 0700);
  17.         sys_mkdir("/root", 0700);
  18.         sys_mknod("/dev/console", S_IFCHR|0600, MKDEV(TTYAUX_MAJOR, 1));
  19. #ifdef CONFIG_DEVFS_FS
  20.         sys_mount("devfs", "/dev", "devfs", 0, NULL);
  21.         do_devfs = 1;
  22. #endif

  23.         create_dev("/dev/root", ROOT_DEV, NULL);
  24.         if (mount_initrd) {
  25.                 if (initrd_load() && ROOT_DEV != MKDEV(RAMDISK_MAJOR, 0)) {
  26.                         handle_initrd();
  27.                         goto out;
  28.                 }
  29.         } else if (is_floppy && rd_doload && rd_load_disk(0))
  30.                 ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0);
  31.         mount_root();
  32. out:
  33.         sys_umount("/dev", 0);
  34.         sys_mount(".", "/", NULL, MS_MOVE, NULL);
  35.         sys_chroot(".");
  36.         mount_devfs_fs ();
  37. }
复制代码

这段代码主要是决定根设备安装在那儿,在中间要处理一下RAM disk并判断是不是软盘启动的。
。RAM主要用来在核心安装根文件系统之前,预先装入一些模块。如果在lilo中指定了一个initrd.img映像文件,则内核在安装根设备之前,把它装上,否则正常安装根设备。

三、转入用户态运行
在完成初始化后,系统接着执行free_initmem(),将初始化过程中使用的内在释放。然后执行unlock_kernel(),这个函数想必就是前面lock_kernel()的逆操作了。然后以可读可写方式打开一个控制台设备。并复制两个文件描述符。
最后,init检查是否有给定的指今,如果没有,则按顺序检查是否存在/sbin/init、/etc/init、/bin/init和/bin/sh等文件,如果存在,则跳转执行相应和程序。一般情况下,系统都会启动/sbin/init程序,从此以后创建的进程都会在用户态运行。下一步的系统启动过程,也由/sbin/init来接着完成。
 楼主| 发表于 2003-1-31 18:33:12 | 显示全部楼层
这段代码并不长,但要具体看懂有诸多困难。主要是对很多系统调用和内核的结构不了解。只好蜻蜓点水一般草草带过。但我主要是想弄清启动过程的顺序,只要能了解个大概,目的也就达到了。今天是大年三十,顺祝内核版在新的一年里兴旺发达!
发表于 2003-1-31 19:25:43 | 显示全部楼层
jk501兄开了个好头,辛苦了, 大年三十还在学习
 楼主| 发表于 2003-1-31 19:53:52 | 显示全部楼层
谢谢,Linuxfish兄过年还在为弟兄们工作,真是让人感动ing。。。
发表于 2003-1-31 20:30:19 | 显示全部楼层

呵呵,辛苦了兄弟

我会尽快赶上的,呵呵
发表于 2003-1-31 21:29:16 | 显示全部楼层

thx

kj501辛苦了,祝本版越办越好.
加油!
发表于 2003-2-21 02:21:04 | 显示全部楼层
ctags 生成的tags到底是个什么东西?能具体点说吗?
一个函数索引?一般分析原代码的时候都需要用到吗?
我如果要写原代码,别人怎么用ctags也能有一个tags呢?
 楼主| 发表于 2003-2-21 16:59:16 | 显示全部楼层
ctags是一个生成索引的工具。执行ctags xxx.c 后,会生成一个xxx.c的函数索引。用vi 查看xxx.c时,只要用ta 函数名 就可以转到这个函数名所在的位置。给阅读带来方便。
发表于 2004-5-21 02:16:40 | 显示全部楼层
init进程第一个执行的函数是lock_kernel(),这个函数在很多内核的源代码中都有,但我没有找到它的函数定义,只好放弃。

调用lock_kernel()加上内核锁排除其他会受随后工作的影响的内核模块的干扰;该内核锁随后被unlock_kernel()释放。
这是自旋锁的应用。
自旋锁的思想就是在一个密封的循环中但坚持反复尝试夺取一个资源(一把锁),直到成功为止。
发表于 2006-12-11 14:32:33 | 显示全部楼层

lock kernel

static inline void lock_kernel(void)
{
        if (!++current->lock_depth)
                spin_lock(&kernel_flag);
}

static inline void unlock_kernel(void)
{
        if (--current->lock_depth < 0)
                spin_unlock(&kernel_flag);
}
在includle/asm-???/smplock.h定義,以上為arm的
下面是i386的


/*
* Getting the big kernel lock.
*
* This cannot happen asynchronously,
* so we only need to worry about other
* CPU's.
*/
static __inline__ void lock_kernel(void)
{
#if 1
        if (!++current->lock_depth)
                spin_lock(&kernel_flag);
#else
        __asm__ __volatile__(
                "incl %1\n\t"
                "jne 9f"
                spin_lock_string
                "\n9:"
                :"=m" (__dummy_lock(&kernel_flag)),
                 "=m" (current->lock_depth));
#endif
}

static __inline__ void unlock_kernel(void)
{
        if (current->lock_depth < 0)
                BUG();
#if 1
        if (--current->lock_depth < 0)
                spin_unlock(&kernel_flag);
#else
        __asm__ __volatile__(
                "decl %1\n\t"
                "jns 9f\n\t"
                spin_unlock_string
                "\n9:"
                :"=m" (__dummy_lock(&kernel_flag)),
                 "=m" (current->lock_depth));
#endif
}
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表