JJJ:rcS脚本是如何被执行到的

init/main.c

start_kernel->arch_call_rest_init->rest_init

负责完成系统初始化的最后几个重要步骤,主要包括初始化用户态进程(init进程)、创建内核线程管理器(kthreadd)以及启动CPU的空闲循环。

c 复制代码
 683 noinline void __ref rest_init(void)
 684 {
 685     struct task_struct *tsk;
 686     int pid;
 687
 688     rcu_scheduler_starting();
 689     /*
 690      * We need to spawn init first so that it obtains pid 1, however
 691      * the init task will end up wanting to create kthreads, which, if
 692      * we schedule it before we create kthreadd, will OOPS.
 693      */
		 // 创建用户模式线程,即系统初始化进程(通常称为init进程),使用kernel_init作为入口点,并设置了CLONE_FS标志来共享文件系统命名空间。这一步确保init进程获得PID 1
 694     pid = user_mode_thread(kernel_init, NULL, CLONE_FS);
 695     /*
 696      * Pin init on the boot CPU. Task migration is not properly working
 697      * until sched_init_smp() has been run. It will set the allowed
 698      * CPUs for init to the non isolated CPUs.
 699      */
		 // 695至704:这部分代码将init进程固定在启动CPU上。由于此时任务调度尚未完全初始化,需要手动设置其CPU亲和性,避免调度问题。
 700     rcu_read_lock();
 701     tsk = find_task_by_pid_ns(pid, &init_pid_ns);
 702     tsk->flags |= PF_NO_SETAFFINITY;
 703     set_cpus_allowed_ptr(tsk, cpumask_of(smp_processor_id()));
 704     rcu_read_unlock();
 705
 706     numa_default_policy();
		// 创建内核线程kthreadd,它是所有内核线程的父进程,负责管理和调度内核线程。同样使用了CLONE_FS和CLONE_FILES标志。
 707     pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
 708     rcu_read_lock();
		// 找到并记录刚创建的kthreadd线程的task_struct,以便后续使用.
 709     kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
 710     rcu_read_unlock();
 711
 712     /*
 713      * Enable might_sleep() and smp_processor_id() checks.
 714      * They cannot be enabled earlier because with CONFIG_PREEMPTION=y
 715      * kernel_thread() would trigger might_sleep() splats. With
 716      * CONFIG_PREEMPT_VOLUNTARY=y the init task might have scheduled
 717      * already, but it's stuck on the kthreadd_done completion.
 718      */
		// 更新系统状态,表明系统已准备好进行进程调度。
 719     system_state = SYSTEM_SCHEDULING;
 720	// 表示kthreadd初始化完成,可以唤醒等待此事件的其他部分代码继续执行。比如上面创建的kernel_init线程,就是所谓的init进程
 721     complete(&kthreadd_done);
 722
 723     /*
 724      * The boot idle thread must execute schedule()
 725      * at least once to get things moving:
 726      */
 727     schedule_preempt_disabled();
 728     /* Call into cpu_idle with preempt disabled */
		// 调用cpu_startup_entry启动CPU的空闲循环处理程序,这是CPU进入正常工作状态的最后一环,确保系统能够处理后续的任务调度。
 729     cpu_startup_entry(CPUHP_ONLINE);
 730 }

init负责所有用户空间进程创建,kthreadd是所有内核线程的祖先。

下面看kernel_init就是init进程:

c 复制代码
1510 static int __ref kernel_init(void *unused)
1511 {
1512     int ret;
1513
1514     /*
1515      * Wait until kthreadd is all set-up.
1516      */
1517     wait_for_completion(&kthreadd_done);
1518
1519     kernel_init_freeable();
1520     /* need to finish all async __init code before freeing the memory */
1521     async_synchronize_full();
1522
1523     system_state = SYSTEM_FREEING_INITMEM;
1524     kprobe_free_init_mem();
1525     ftrace_free_init_mem();
1526     kgdb_free_init_mem();
1527     exit_boot_config();
1528     free_initmem();
1529     mark_readonly();
1530
1531     /*
1532      * Kernel mappings are now finalized - update the userspace page-table
1533      * to finalize PTI.
1534      */
1535     pti_finalize();
1536
1537     system_state = SYSTEM_RUNNING;
1538     numa_default_policy();
1539
1540     rcu_end_inkernel_boot();
1541
1542     do_sysctl_args();
1543
1544     pr_err("x5_test_init:%s:%d:ramdisk_execute_command=%s\n", __func__, __LINE__, ramdisk_execute_command);
1545     if (ramdisk_execute_command) {
1546         ret = run_init_process(ramdisk_execute_command);
1547         if (!ret)
1548             return 0;
1549         pr_err("Failed to execute %s (error %d)\n",
1550                ramdisk_execute_command, ret);
1551     }
1552
1553     /*
1554      * We try each of these until one succeeds.
1555      *
1556      * The Bourne shell can be used instead of init if we are
1557      * trying to recover a really broken machine.
1558      */
1559     if (execute_command) {
1560         ret = run_init_process(execute_command);
1561         if (!ret)
1562             return 0;
1563         panic("Requested init %s failed (error %d).",
1564               execute_command, ret);
1565     }
1566
1567     if (CONFIG_DEFAULT_INIT[0] != '\0') {
1568         ret = run_init_process(CONFIG_DEFAULT_INIT);
1569         if (ret)
1570             pr_err("Default init %s failed (error %d)\n",
1571                    CONFIG_DEFAULT_INIT, ret);
1572         else
1573             return 0;
1574     }
1575
1576     if (!try_to_run_init_process("/sbin/init") || 
1577         !try_to_run_init_process("/etc/init") ||
1578         !try_to_run_init_process("/bin/init") ||
1579         !try_to_run_init_process("/bin/sh"))
1580         return 0;
1581
1582     panic("No working init found.  Try passing init= option to kernel. "
1583           "See Linux Documentation/admin-guide/init.rst for guidance.");
1584 }

init上承kernel,下起用户空间进程,配置了整个用户空间工作环境。

kernel_init执行"/sbin/init",在此程序中,会执行到busybox的init进程入口,init_main.

在init_main()函数中会调用parse_inittab(void)函数,parse_inittab(void)函数可以使用一些默认的配置,当/etc/inittab没有配置时。

parse_inittab 里面会调用 new_init_action(SYSINIT, INIT_SCRIPT, ""),决定了接下去初始化的脚本是INIT_SCRIPT所定义的值。这个宏的默认值是"/etc/init.d/rcS"。

如此就执行到了rcS初始化脚本

相关推荐
爱吃生蚝的于勒36 分钟前
C语言内存函数
c语言·开发语言·数据结构·c++·学习·算法
码上一元1 小时前
SpringBoot自动装配原理解析
java·spring boot·后端
计算机-秋大田2 小时前
基于微信小程序的养老院管理系统的设计与实现,LW+源码+讲解
java·spring boot·微信小程序·小程序·vue
小白学大数据3 小时前
Python爬虫开发中的分析与方案制定
开发语言·c++·爬虫·python
魔道不误砍柴功3 小时前
简单叙述 Spring Boot 启动过程
java·数据库·spring boot
冰芒猓3 小时前
SpringMVC数据校验、数据格式化处理、国际化设置
开发语言·maven
失落的香蕉3 小时前
C语言串讲-2之指针和结构体
java·c语言·开发语言
枫叶_v4 小时前
【SpringBoot】22 Txt、Csv文件的读取和写入
java·spring boot·后端
wclass-zhengge4 小时前
SpringCloud篇(配置中心 - Nacos)
java·spring·spring cloud
路在脚下@4 小时前
Springboot 的Servlet Web 应用、响应式 Web 应用(Reactive)以及非 Web 应用(None)的特点和适用场景
java·spring boot·servlet