8.6. The init Thread (or Process 1)We now explore the init thread. Note that we skip over all SMP-related routines for brevity: ---------------------------------------------------------------------- init/main.c 601 static int init(void * unused) 602 { 603 lock_kernel(); ... 612 child_reaper = current; ... 627 populate_rootfs(); 629 do_basic_setup(); ... 635 if (sys_access((const char __user *) "/init", 0) == 0) 636 execute_command = "/init"; 637 else 638 prepare_namespace(); ... 645 free_initmem(); 646 unlock_kernel(); 647 system_state = SYSTEM_RUNNING; 649 if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) 650 printk("Warning: unable to open an initial console.\n"); 651 652 (void) sys_dup(0); 653 (void) sys_dup(0); ... 662 if (execute_command) 663 run_init_process(execute_command); 664 665 run_init_process("/sbin/init"); 666 run_init_process("/etc/init"); 667 run_init_process("/bin/init"); 668 run_init_process("/bin/sh"); 669 670 panic("No init found. Try passing init= option to kernel."); 671 } ----------------------------------------------------------------------- Line 612The init thread is set to reap any thread whose parent has died. The child_reaper variable is a global pointer to a task_struct and is defined in init/main.c. This variable comes into play in "reparenting functions" and is used as a reference to the thread that should become the new parent. We refer to functions such as reparent_to_init() (kernel/exit.c), choose_new_parent() (kernel/exit.c), and forget_original_parent() (kernel/exit.c) because they use child_reaper to reset the calling thread's parent. Line 629The do_basic_setup() function initializes the driver model, the sysctl interface, the network socket interface, and work queue support: ---------------------------------------------------------------------- init/main.c 551 static void __init do_basic_setup(void) 552 { 553 driver_init(); 554 555 #ifdef CONFIG_SYSCTL 556 sysctl_init(); 557 #endif ... 560 sock_init(); 561 562 init_workqueues(); 563 do_initcalls(); 564 } ---------------------------------------------------------------------- Line 553The driver_init() (drivers/base/init.c) function initializes all the subsystems involved in driver support. This is the first part of device driver initializations. The second comes on line 563 with the call to do_initcalls(). Lines 555557The sysctl interface provides support for dynamic alteration of kernel parameters. This means that the kernel parameters that sysctl supports can be modified at runtime without the need for recompiling and rebooting the kernel. sysctl_init() (kernel/sysctl.c) initializes the interface. For more information on sysctl, read the man page (man sysctl). Line 560The sock_init() function is a dummy function with a simple printk if the kernel is configured without net support. In this case, sock_init() is defined in net/nonet.c. In the case that network support is configured then sock_init() is defined in net/socket.c, it initializes the memory caches to be used for network support and registers the filesystem that supports networking. Line 562The call to init_workqueues sets up the work queue notifier chain. Chapter 10, "Adding Your Code to the Kernel," discusses work queues. Line 563The do_initcalls() (init/main.c) function constitutes the second part of device driver initialization. This function sequentially calls the entries in an array of function pointers that correspond to built-in device initialization functions.[11]
Lines 635638If an early user space init exists, the kernel does not prepare the namespace; it allows it to perform this function. Otherwise, the call to prepare_namespace() is made. A namespace refers to the mount point of a filesystem hierarchy: ---------------------------------------------------------------------- init/do_mounts.c 383 void __init prepare_namespace(void) 384 { 385 int is_floppy; 386 387 mount_devfs(); ... 391 if (saved_root_name[0]) { 392 root_device_name = saved_root_name; 393 ROOT_DEV = name_to_dev_t(root_device_name); 394 if (strncmp(root_device_name, "/dev/", 5) == 0) 395 root_device_name += 5; 396 } 397 398 is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR; 399 400 if (initrd_load()) 401 goto out; 402 403 if (is_floppy && rd_doload && rd_load_disk(0)) 404 ROOT_DEV = Root_RAM0; 405 406 mount_root(); 407 out: 408 umount_devfs("/dev"); 409 sys_mount(".", "/", NULL, MS_MOVE, NULL); 410 sys_chroot("."); 411 security_sb_post_mountroot(); 412 mount_devfs_fs (); 413 } ---------------------------------------------------------------------- Line 387The mount_devfs() function creates the /dev mount-related structures. We need to mount /dev because we use it to refer to the root device name. Lines 391396This code block sets the global variable ROOT_DEV to the indicated root device as passed in through kernel boot-time parameters. Line 398A simple comparison of major numbers indicates whether the root device is a floppy. Lines 400401The call to initrd_load() mounts the RAM disk if a RAM disk has been indicated as the kernel's root filesystem. If this is the case, it returns a 1 and executes the jump to the out label, which undoes all we've done in preparation of a root filesystem from a device. Line 406The call to mount_root does the majority of the root-filesystem mounting. Let's closely look at this function: ---------------------------------------------------------------------- init/do_mounts.c 353 void __init mount_root(void) 354 { 355 #ifdef CONFIG_ROOT_NFS 356 if (MAJOR(ROOT_DEV) == UNNAMED_MAJOR) { 357 if (mount_nfs_root()) 358 return; 359 360 printk(KERN_ERR "VFS: Unable to mount root fs via NFS, trying floppy.\n"); 361 ROOT_DEV = Root_FD0; 362 } 363 #endif 364 #ifdef CONFIG_BLK_DEV_FD 365 if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) { ... 367 if (rd_doload==2) { 368 if (rd_load_disk(1)) { 369 ROOT_DEV = Root_RAM1; 370 root_device_name = NULL; 371 } 372 } else 373 change_floppy("root floppy"); 374 } 375 #endif 376 create_dev("/dev/root", ROOT_DEV, root_device_name); 377 mount_block_root("/dev/root", root_mountflags); 378 } ---------------------------------------------------------------------- Lines 355358If the kernel has been configured to mount an NFS filesystem, we execute mount_nfs_root(). If the NFS mount fails, the kernel prints out the appropriate message and then proceeds to try to mount the floppy as the root filesystem. Lines 364375In this code block, the kernel tries to mount the root floppy.[12]
Line 377This function performs the bulk of the root device mounting. We now return to init(). Line 645The call to free_initmem() frees all memory segments that the routines used up with the __init precursor. This marks our exit from pure kernel space and we begin to set up user mode data. Lines 649650Open up the initial console. Lines 662668The execute_command variable is set in init_setup() and holds the value of a boot-time parameter that contains the name of the init program to call if we do not want the default /sbin/init to be called. If an init program name is passed, it takes priority over the usual /sbin/init. Note that the call to run_init_process() (init/main.c) does not return because it ends with a call to execve(). Thus, the first init function call to run successfully is the only one run. In the case that an init program is not found, we can use the bash shell to start up. Line 670This panic statement should be reached only if all of our tries to execute various init program fails. This concludes kernel initialization. From here on out, the init process involves itself with system initialization and starting all the necessary processes and daemon support required for user login and support. |