内核启动第一个程序

本文详细解析了Linux内核启动过程中的init初始化流程,包括如何打开初始终端、执行init进程及解析inittab配置文件等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

从kernel_init()函数我们知道,init_post是最后执行的一个函数,我们来分析这个函数:

static int noinline init_post(void)
{
    free_initmem();
    unlock_kernel();
    mark_rodata_ro();
    system_state = SYSTEM_RUNNING;
    numa_default_policy();

    /* 首先打开终端设备 */
    if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
        printk(KERN_WARNING "Warning: unable to open an initial console.\n");

    /* 然后复制两个,共三个,分别是stdin,stdout,stderr */
    (void) sys_dup(0);
    (void) sys_dup(0);

    if (ramdisk_execute_command) {
        run_init_process(ramdisk_execute_command);
        printk(KERN_WARNING "Failed to execute %s\n",
                ramdisk_execute_command);
    }

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

    /* 执行命令行传入的int=xxxx程序,
       如果没有就接着向下执行,如果成功则会一直运行不会向下执行。
     */
    if (execute_command) {
        run_init_process(execute_command);
        printk(KERN_WARNING "Failed to execute %s.  Attempting "
                    "defaults...\n", execute_command);
    }
    
    /* 如果上面没有成功,则向下执行 */
    run_init_process("/sbin/init");
    run_init_process("/etc/init");
    run_init_process("/bin/init");
    run_init_process("/bin/sh");

    panic("No init found.  Try passing init= option to kernel.");
}
从上面可以看出如果执行命令里没有 init=xxx 的参数,那么就会依次向下执行

run_init_process("/sbin/init");
......

因为我们的执行命令里没有init=xxx参数,所以 init 就是我们的第一个进程

但是如果我们执行下面的命令:

ls -l /sbin/init
lrwxrwxrwx    1 messageb messageb       14 Dec 21  2012 /sbin/init -> ../bin/busybox

我们发现第一个程序是链接到busybox的,也就是说启动是busybox程序,所以我们要分析busybox源码里的init.c源码。

打开 busybox 源码中的 init.c文件,分析int.c 是怎么解析 /etc/inittab 文件以及执行程序的:

init_main
    parse_inittab
        file = fopen(INITTAB, "r");  //打开配置文件
        new_init_action              //创建init_action结构,并加入init_action_list链表
        
    run_actions(SYSINIT);    
        waitfor(a, 0);               //执行应用程序,等待它执行完毕
                run(a);              //创建process子进程
                waitpid              //等待它结束
        delete_init_action(a);       //从init_action_list链表中删掉
                        
    run_actions(WAIT);
        waitfor(a, 0);               //执行应用程序,等待它执行完毕
                run(a);              //创建process子进程
                waitpid              //等待它结束
        delete_init_action(a);       //从init_action_list链表中删掉
                    
    run_actions(ONCE);
        run(a);                      //不等待了,运行完后直接结束
        delete_init_action(a);
                    
    while (1) {
        run_actions(RESPAWN);
                if (a->pid == 0) {
                    a->pid = run(a);
                }
                    
        run_actions(ASKFIRST);
                if (a->pid == 0) {
                    a->pid = run(a);
                                        打印: "\nPlease press Enter to activate this console. ";
                                        等待回车
                }
                    
        wpid = wait(NULL);           //等待子进程退出
        while (wpid > 0) {
                a->pid = 0;
        }
    }

当然在busybox/example下,我们可以查到 inittab的格式说明:

# Format for each entry: <id>:<runlevels>:<action>:<process>
id --> /dev/it  用于终端:stdin, stdout,stderr
runlevels       : 忽略
action             :执行时机
process         : 应用程序或脚本

这样第一个进程 init 就运行起来了


<think>我们首先需要解决用户的问题:如何启动一个内核程序以及不同会话名的区别。根据用户提供的引用和之前的历史对话,我们可以将问题拆解为两部分:第一部分:启动内核程序-内核程序通常指运行在内核态的程序,如内核模块(LKM)或驱动。在Linux中,我们通过insmod或modprobe加载内核模块。-在Windows中,内核程序一般是指驱动程序,通过服务控制管理器(SCM)加载。第二部分:不同会话名的区别(在Windows环境下)-在之前的回答中,我们已经解释了Windows中不同会话名(Services,Console,RDP-Tcp#X,N/A)的区别。但是,用户提到了“内核程序”,我们需要结合引用内容:-引用[1][2][3]提到了内核级线程(Kernel-LevelThread)和用户级线程(User-LevelThread)的区别。-注意:内核级线程和内核程序是有区别的。内核级线程是线程的实现方式,而内核程序是运行在内核空间的程序。因此,回答的结构:1.解释如何启动内核程序(分别以Linux和Windows为例)。2.解释不同会话名的区别(Windows环境,因为之前的问题是在Windows环境下)。3.补充说明内核级线程和内核程序的关联(根据引用内容,内核级线程是由内核管理的,而内核程序运行在内核空间,可能会创建内核线程)。注意:用户要求使用中文回答,并且对数学公式的格式有要求。由于我们的回答中可能没有数学公式,所以主要注意代码块和引用格式。同时,用户要求生成相关问题,放在最后。由于用户上一次的问题中已经涉及不同会话名的区别,我们这次可以简要回顾,重点放在内核程序启动。步骤:一、启动内核程序的方法A.Linux系统:-编写内核模块(.ko文件),然后使用insmod或modprobe命令加载。-示例步骤:1.编写模块代码(例如hello.c)2.编写Makefile3.使用make编译生成.ko文件4.使用sudoinsmodhello.ko加载模块5.使用dmesg查看内核日志(因为printk输出到内核环缓冲区)6.使用rmmod卸载模块B.Windows系统:-内核程序通常是驱动程序(.sys文件),通过服务的方式加载。-步骤:1.编写驱动程序(需要WDK)2.编译生成.sys文件3.创建服务:使用sccreate命令创建服务并指定binPath为.sys文件路径4.启动服务:scstart服务名5.停止服务:scstop服务名6.删除服务:scdelete服务名二、不同会话名的区别(回顾,基于Windows)-Services:服务会话(会话0),无用户界面,以系统账户运行。-Console:本地用户交互会话(通常会话1)。-RDP-Tcp#X:远程桌面会话(会话编号≥1)。-N/A:内核级进程(不属于任何会话)。三、内核程序与会话的关系(Windows)-内核程序(如驱动程序)加载后,通常没有会话信息(即显示为N/A)。它们运行在内核空间,由系统直接管理。-而用户级程序运行在用户空间,属于某个会话(如Console或RDP-Tcp#X)。四、补充:内核级线程(根据引用)-内核级线程是由操作系统内核管理的线程,其线程控制块(TCB)在内核中。内核程序可能会创建内核线程。-优点:可以实现真正的并行(多处理器环境下)。-缺点:线程操作需要系统调用,开销较大。根据用户要求,我们在回答末尾生成相关问题。回答结构:###内核程序启动方法(分Linux和Windows)###不同会话名的区别(Windows环境下)(表格形式回顾)###内核程序与会话的关系###内核级线程与内核程序的关联§§相关问题§§1.如何编写Linux内核模块?2.Windows驱动程序开发需要哪些工具?3.内核程序崩溃会导致什么后果?4.用户级线程和内核级线程有什么具体应用场景?</think> ### 内核程序启动方法 在操作系统中,内核程序(如驱动、内核模块)的启动依赖于系统核心机制,主要有两种方式: 1. **内核模块动态加载(Linux)** 通过 `insmod` 或 `modprobe` 命令加载预编译的 `.ko` 文件(内核对象): ```bash insmod /path/to/module.ko # 加载内核模块 modprobe module_name # 自动处理依赖关系后加载 ``` - **原理**: 内核通过系统调用(如 `sys_init_module()`)将模块代码注入内核空间,执行模块的 `init()` 函数初始化[^2]。 线程控制块(TCB)由内核直接管理,调度由内核完成,模式切换涉及用户态→内核态转换(开销较高)[^1][^3]。 2. **驱动服务启动(Windows)** 通过服务控制管理器(SCM)加载 `.sys` 驱动程序: ```powershell sc create DriverName binPath=C:\driver.sys type=kernel # 注册驱动服务 sc start DriverName # 启动内核级驱动 ``` - **原理**: SCM 以 `SYSTEM` 权限加载驱动,驱动入口函数 `DriverEntry()` 由内核直接调用,线程调度由内核线程池管理[^1]。 --- ### 不同会话名的区别(Windows) | **会话名** | **运行环境** | **权限/资源隔离** | **典型进程** | |------------------|---------------------------|---------------------------------------|----------------------------------| | **`Services`** | 后台服务(会话0) | `SYSTEM` 账户,无界面隔离 | `svchost.exe`, `lsass.exe` | | **`Console`** | 本地交互会话(会话1) | 当前用户权限,隔离其他会话 | `explorer.exe`, `msedge.exe` | | **`RDP-Tcp#X`** | 远程桌面会话(会话≥2) | 远程用户权限,独立内存/句柄隔离 | `mstsc.exe`, 远程打开的应用程序 | | **`N/A`** | 内核级进程(无会话) | 最高内核权限,全局资源无隔离 | `System` (PID 4), 内核驱动程序 | #### 关键区别: 1. **权限层级**: - `Services`/`N/A` 属**内核态**:直接访问硬件资源(如通过 `write()` 系统调用操作设备缓冲区)[^4]。 - `Console`/`RDP-Tcp#X` 属**用户态**:需通过系统调用请求内核服务(如 `printf()` 需经用户级缓冲区→内核级缓冲区→设备)[^4][^3]。 2. **资源隔离机制**: ```mermaid graph LR A[内核空间] --> B(全局资源) A --> C(内核线程TCB) D[用户空间] --> E(会话隔离) E --> F(Services) E --> G(Console) E --> H(RDP-Tcp) ``` - 内核程序(`N/A`)无会话隔离,直接操作全局资源(如中断向量表)[^1][^2]。 - 用户级会话(如 `Services`)通过会话管理器(`smss.exe`)隔离内存和句柄[^1]。 3. **线程调度**: - 内核线程(如驱动创建的线程)由内核直接调度,无用户态参与(`Yiled()` 不可见,实际调用 `Schedule()`)[^3]。 - 用户会话线程依赖内核级线程池,但调度需经会话管理器协调[^2]。 --- ### 内核程序与会话的关系 1. **内核驱动(`N/A` 会话)**: - 无会话绑定,直接由内核加载(如存储驱动 `storport.sys`)。 - 若需与用户会话交互(如弹窗),需手动配置 **会话隔离权限**(例如 SQL Server 服务需授权访问 `Console` 会话)[^1]。 2. **服务进程(`Services` 会话)**: - 虽在用户空间运行,但通过内核级线程执行关键操作(如网络数据包处理)。 - 线程阻塞时,内核切换到其他进程的线程(跨进程调度)[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值