Home  >  Article  >  Operation and Maintenance  >  Does the linux kernel have a main function?

Does the linux kernel have a main function?

藏色散人
藏色散人Original
2023-03-28 10:45:512016browse

linux内核有main函数;main函数是程序的入口,main是应用程序和操作系统之间约定好的一个接口名,所以linux中每个应用程序的第一个函数必须是main。

Does the linux kernel have a main function?

本教程操作环境:linux5.9.8系统、Dell G3电脑。

linux内核有main函数吗?

linux内核源码之main函数解析

这几天一直在纠结:

 

main函数是程序的入口,一个程序启动后,经过bootloader的初始化就该经main函数进入C语言的世界,但是linux中每个应用程序的开始都是从main函数开始的。linux下有多个应用程序,岂不是有很多个main。那bootloader会知道跳到哪个main?多个main编译怎么不冲突?

 

 

 

在网上搜索了很久,渐渐的有些明白了:

 

1、main函数是C语言的入口,这句话没错;但是这句话仅仅是一个约定,而非一个亘古不变的铁律!从程序的更为本质的汇编代码来看,只是大家约定汇编初始化完了后,跳到一个名字叫"main"的标号处;言外之意就是这个标号也是可以改名的,比如linux的C语言入口就是start_kernel();从这个标号地址后就是C语言的天下了。用main这个名字仅仅是因为大家的约定而已,不遵守约定能玩的转也行啊,就像苹果充电线啥的都和别人不一样。

 

2、在编译时是不存多个main函数的!每个应用程序虽说都有一个main函数(从应用程序来看应用程序的入口是main函数哦);但是应用程序都是独立编译的,不会一起编译,操作系统内核就更不可能和应用程序一起编译了!所以根本不存在多个main冲突的!!可能是统一操作系统与应用程序之间的接口,亦或是侧面影响下main是程序入口的说法,main是应用程序和操作系统之间约定好的一个接口名!所以linux中每个应用程序的第一个函数必须是main。除非你改掉了内核调度的接口地方。

 

3、linux的应用程序的安装启动也可以类比下我们每天都在用的Windows。Windows应用程序的安装其实也是把一些执行文件拷贝到指定的文件夹里(从绿色软件看),点击就可以运行。linux下也是这样。编译好的bin文件放到指定的文件夹目录下,然后用命令启动执行。

 

/*
 *  linux/init/main.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 *
 *  GK 2/5/95  -  Changed to support mounting root fs via NFS
 *  Added initrd & change_root: Werner Almesberger & Hans Lermen, Feb '96
 *  Moan early if gcc is old, avoiding bogus kernels - Paul Gortmaker, May '96
 *  Simplified starting of init:  Michael A. Griffith <grif@acm.org> 
 * start_kernel->rest_init->kernel_init创建用户init  pid=1
                          ->kthreadd管理内核线程     pid=x
                          ->pid=0,是idle线程
    在rest_init中,会创建kernel_init线程,它负责创建用户init进程,完成工作后,自己
    化身为idle线程
 */
 
#include <linux/types.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/kernel.h>
#include <linux/syscalls.h>
#include <linux/stackprotector.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/initrd.h>
#include <linux/bootmem.h>
#include <linux/acpi.h>
#include <linux/tty.h>
#include <linux/percpu.h>
#include <linux/kmod.h>
#include <linux/vmalloc.h>
#include <linux/kernel_stat.h>
#include <linux/start_kernel.h>
#include <linux/security.h>
#include <linux/smp.h>
#include <linux/profile.h>
#include <linux/rcupdate.h>
#include <linux/moduleparam.h>
#include <linux/kallsyms.h>
#include <linux/writeback.h>
#include <linux/cpu.h>
#include <linux/cpuset.h>
#include <linux/cgroup.h>
#include <linux/efi.h>
#include <linux/tick.h>
#include <linux/interrupt.h>
#include <linux/taskstats_kern.h>
#include <linux/delayacct.h>
#include <linux/unistd.h>
#include <linux/rmap.h>
#include <linux/mempolicy.h>
#include <linux/key.h>
#include <linux/buffer_head.h>
#include <linux/page_cgroup.h>
#include <linux/debug_locks.h>
#include <linux/debugobjects.h>
#include <linux/lockdep.h>
#include <linux/kmemleak.h>
#include <linux/pid_namespace.h>
#include <linux/device.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/idr.h>
#include <linux/kgdb.h>
#include <linux/ftrace.h>
#include <linux/async.h>
#include <linux/kmemcheck.h>
#include <linux/sfi.h>
#include <linux/shmem_fs.h>
#include <linux/slab.h>
#include <linux/perf_event.h>
 
#include <asm/io.h>
#include <asm/bugs.h>
#include <asm/setup.h>
#include <asm/sections.h>
#include <asm/cacheflush.h>
 
#ifdef CONFIG_X86_LOCAL_APIC
#include <asm/smp.h>
#endif
 
static int kernel_init(void *);
 
extern void init_IRQ(void);
extern void fork_init(unsigned long);
extern void mca_init(void);
extern void sbus_init(void);
extern void prio_tree_init(void);
extern void radix_tree_init(void);
#ifndef CONFIG_DEBUG_RODATA
static inline void mark_rodata_ro(void) { }
#endif
 
#ifdef CONFIG_TC
extern void tc_init(void);
#endif
 
/*
 * Debug helper: via this flag we know that we are in &#39;early bootup code&#39;
 * where only the boot processor is running with IRQ disabled.  This means
 * two things - IRQ must not be enabled before the flag is cleared and some
 * operations which are not allowed with IRQ disabled are allowed while the
 * flag is set.
 */
bool early_boot_irqs_disabled __read_mostly;
 
enum system_states system_state __read_mostly;
EXPORT_SYMBOL(system_state);
 
/*
 * Boot command-line arguments
 */
#define MAX_INIT_ARGS CONFIG_INIT_ENV_ARG_LIMIT
#define MAX_INIT_ENVS CONFIG_INIT_ENV_ARG_LIMIT
 
extern void time_init(void);
/* Default late time init is NULL. archs can override this later. */
void (*__initdata late_time_init)(void);
extern void softirq_init(void);
 
/* Untouched command line saved by arch-specific code. */
char __initdata boot_command_line[COMMAND_LINE_SIZE];
/* Untouched saved command line (eg. for /proc) */
char *saved_command_line;
/* Command line for parameter parsing */
static char *static_command_line;
 
static char *execute_command;
static char *ramdisk_execute_command;
 
/*
 * If set, this is an indication to the drivers that reset the underlying
 * device before going ahead with the initialization otherwise driver might
 * rely on the BIOS and skip the reset operation.
 *
 * This is useful if kernel is booting in an unreliable environment.
 * For ex. kdump situaiton where previous kernel has crashed, BIOS has been
 * skipped and devices will be in unknown state.
 */
unsigned int reset_devices;
EXPORT_SYMBOL(reset_devices);
 
static int __init set_reset_devices(char *str)
{
    reset_devices = 1;
    return 1;
}
 
__setup("reset_devices", set_reset_devices);
 
static const char * argv_init[MAX_INIT_ARGS+2] = { "init", NULL, };
const char * envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, };
static const char *panic_later, *panic_param;
 
extern const struct obs_kernel_param __setup_start[], __setup_end[];
 
static int __init obsolete_checksetup(char *line)
{
    const struct obs_kernel_param *p;
    int had_early_param = 0;
 
    p = __setup_start;
    do {
        int n = strlen(p->str);
        if (parameqn(line, p->str, n)) {
            if (p->early) {
                /* Already done in parse_early_param?
                 * (Needs exact match on param part).
                 * Keep iterating, as we can have early
                 * params and __setups of same names 8( */
                if (line[n] == &#39;\0&#39; || line[n] == &#39;=&#39;)
                    had_early_param = 1;
            } else if (!p->setup_func) {
                printk(KERN_WARNING "Parameter %s is obsolete,"
                       " ignored\n", p->str);
                return 1;
            } else if (p->setup_func(line + n))
                return 1;
        }
        p++;
    } while (p < __setup_end);
 
    return had_early_param;
}
 
/*
 * This should be approx 2 Bo*oMips to start (note initial shift), and will
 * still work even if initially too large, it will just take slightly longer
 */
unsigned long loops_per_jiffy = (1<<12);
EXPORT_SYMBOL(loops_per_jiffy);
static int __init debug_kernel(char *str)
{
    console_loglevel = 10;
    return 0;
}
static int __init quiet_kernel(char *str)
{
    console_loglevel = 4;
    return 0;
}
early_param("debug", debug_kernel);
early_param("quiet", quiet_kernel);
static int __init loglevel(char *str)
{
    int newlevel;
    /*
     * Only update loglevel value when a correct setting was passed,
     * to prevent blind crashes (when loglevel being set to 0) that
     * are quite hard to debug
     */
    if (get_option(&str, &newlevel)) {
        console_loglevel = newlevel;
        return 0;
    }
    return -EINVAL;
}
early_param("loglevel", loglevel);
/* Change NUL term back to "=", to make "param" the whole string. */
static int __init repair_env_string(char *param, char *val)
{
    if (val) {
        /* param=val or param="val"? */
        if (val == param+strlen(param)+1)
            val[-1] = &#39;=&#39;;
        else if (val == param+strlen(param)+2) {
            val[-2] = &#39;=&#39;;
            memmove(val-1, val, strlen(val)+1);
            val--;
        } else
            BUG();
    }
    return 0;
}
/*
 * Unknown boot options get handed to init, unless they look like
 * unused parameters (modprobe will find them in /proc/cmdline).
 */
static int __init unknown_bootoption(char *param, char *val)
{
    repair_env_string(param, val);
    /* Handle obsolete-style parameters */
    if (obsolete_checksetup(param))
        return 0;
    /* Unused module parameter. */
    if (strchr(param, &#39;.&#39;) && (!val || strchr(param, &#39;.&#39;) < val))
        return 0;
    if (panic_later)
        return 0;
    if (val) {
        /* Environment option */
        unsigned int i;
        for (i = 0; envp_init[i]; i++) {
            if (i == MAX_INIT_ENVS) {
                panic_later = "Too many boot env vars at `%s&#39;";
                panic_param = param;
            }
            if (!strncmp(param, envp_init[i], val - param))
                break;
        }
        envp_init[i] = param;
    } else {
        /* Command line option */
        unsigned int i;
        for (i = 0; argv_init[i]; i++) {
            if (i == MAX_INIT_ARGS) {
                panic_later = "Too many boot init vars at `%s&#39;";
                panic_param = param;
            }
        }
        argv_init[i] = param;
    }
    return 0;
}
static int __init init_setup(char *str)
{
    unsigned int i;
    execute_command = str;
    /*
     * In case LILO is going to boot us with default command line,
     * it prepends "auto" before the whole cmdline which makes
     * the shell think it should execute a script with such name.
     * So we ignore all arguments entered _before_ init=... [MJ]
     */
    for (i = 1; i < MAX_INIT_ARGS; i++)
        argv_init[i] = NULL;
    return 1;
}
__setup("init=", init_setup);
static int __init rdinit_setup(char *str)
{
    unsigned int i;
    ramdisk_execute_command = str;
    /* See "auto" comment in init_setup */
    for (i = 1; i < MAX_INIT_ARGS; i++)
        argv_init[i] = NULL;
    return 1;
}
__setup("rdinit=", rdinit_setup);
#ifndef CONFIG_SMP
static const unsigned int setup_max_cpus = NR_CPUS;
#ifdef CONFIG_X86_LOCAL_APIC
static void __init smp_init(void)
{
    APIC_init_uniprocessor();
}
#else
#define smp_init()  do { } while (0)
#endif
static inline void setup_nr_cpu_ids(void) { }
static inline void smp_prepare_cpus(unsigned int maxcpus) { }
#endif
/*
 * We need to store the untouched command line for future reference.
 * We also need to store the touched command line since the parameter
 * parsing is performed in place, and we should allow a component to
 * store reference of name/value for future reference.
 */
static void __init setup_command_line(char *command_line)
{
    saved_command_line = alloc_bootmem(strlen (boot_command_line)+1);
    static_command_line = alloc_bootmem(strlen (command_line)+1);
    strcpy (saved_command_line, boot_command_line);
    strcpy (static_command_line, command_line);
}
/*
 * We need to finalize in a non-__init function or else race conditions
 * between the root thread and the init thread may cause start_kernel to
 * be reaped by free_initmem before the root thread has proceeded to
 * cpu_idle.
 *
 * gcc-3.4 accidentally inlines this function, so use noinline.
 */
static __initdata DECLARE_COMPLETION(kthreadd_done);
static noinline void __init_refok rest_init(void)
{
    int pid;
    rcu_scheduler_starting();//READ-COPY UPDATE启动
    /*
     * We need to spawn init first so that it obtains pid 1, however
     * the init task will end up wanting to create kthreads, which, if
     * we schedule it before we create kthreadd, will OOPS.
     * 创建一个内核线程,它的线程函数是kernel_init,pid=1,内核进程
     */
    kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
    //numa策略设置
    numa_default_policy();
    //全局链表kthread_create_list中的kthread内核线程都被运行
    //kthreadd线程管理和调度其它内核线程
    pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
    rcu_read_lock();
    //通过pid,ini_pid_ns取得kthreadd地址
    kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
    rcu_read_unlock();
    //通知在kthreadd_done条件的kernel_init线程
    complete(&kthreadd_done);
    /*
     * The boot idle thread must execute schedule()
     * at least once to get things moving:
     * idle 线程初始化
     */
    init_idle_bootup_task(current);
    //抢占禁用
    schedule_preempt_disabled();
    /* Call into cpu_idle with preempt disabled */
    cpu_idle();
}
/* Check for early params. */
static int __init do_early_param(char *param, char *val)
{
    const struct obs_kernel_param *p;
    for (p = __setup_start; p < __setup_end; p++) {
        if ((p->early && parameq(param, p->str)) ||
            (strcmp(param, "console") == 0 &&
             strcmp(p->str, "earlycon") == 0)
        ) {
            if (p->setup_func(val) != 0)
                printk(KERN_WARNING
                       "Malformed early option &#39;%s&#39;\n", param);
        }
    }
    /* We accept everything at this stage. */
    return 0;
}
void __init parse_early_options(char *cmdline)
{
    parse_args("early options", cmdline, NULL, 0, 0, 0, do_early_param);
}
/* Arch code calls this early on, or if not, just before other parsing. */
void __init parse_early_param(void)
{
    static __initdata int done = 0;
    static __initdata char tmp_cmdline[COMMAND_LINE_SIZE];
    if (done)
        return;
    /* All fall through to do_early_param. */
    strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);
    parse_early_options(tmp_cmdline);
    done = 1;
}
/*
 *  Activate the first processor.
 */
static void __init boot_cpu_init(void)
{
    int cpu = smp_processor_id();
    /* Mark the boot cpu "present", "online" etc for SMP and UP case */
    set_cpu_online(cpu, true);
    set_cpu_active(cpu, true);
    set_cpu_present(cpu, true);
    set_cpu_possible(cpu, true);
}
void __init __weak smp_setup_processor_id(void)
{
}
void __init __weak thread_info_cache_init(void)
{
}
/*
 * Set up kernel memory allocators
 */
static void __init mm_init(void)
{
    /*
     * page_cgroup requires contiguous pages,
     * bigger than MAX_ORDER unless SPARSEMEM.
     */
    page_cgroup_init_flatmem();
    mem_init();
    kmem_cache_init();
    percpu_init_late();
    pgtable_cache_init();
    vmalloc_init();
}
asmlinkage void __init start_kernel(void)
{
    char * command_line;
    extern const struct kernel_param __start___param[], __stop___param[];
    /*
     * Need to run as early as possible, to initialize the
     * lockdep hash:
     */
    //初始化2个hash表-Lock Dependency Validator(内核依赖的关系表)
    lockdep_init();
    smp_setup_processor_id(); //空函数
    debug_objects_early_init();//初始化内核调试相关
    /*
     * Set up the the initial canary ASAP:
     */
    boot_init_stack_canary();//栈溢出保护初始化
    //控制组初始化-cgroup-资源任务分组管理
    cgroup_init_early();
    local_irq_disable();//关中断
    early_boot_irqs_disabled = true;
/*
 * Interrupts are still disabled. Do necessary setups, then
 * enable them
 */
    tick_init();//时钟初始化
    boot_cpu_init();//启动cpu初始化
    page_address_init();//页面初始化
    printk(KERN_NOTICE "%s", linux_banner);
    setup_arch(&command_line);//架构相关初始化
    mm_init_owner(&init_mm, &init_task);//内存管理初始化
    mm_init_cpumask(&init_mm);//内存管理初始化
    setup_command_line(command_line);//处理命令行(保存2份)
    setup_nr_cpu_ids();//cpuid相关
    setup_per_cpu_areas();//每cpu变量申请空间(包括gdt)
    //smp中用来启动的cpu
    smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
     //建立系统内存页区链表
    build_all_zonelists(NULL);
    //内存页相关初始化
    page_alloc_init();
    printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line);
    //命令行boot_command_line
    parse_early_param();
    //解析参数
    parse_args("Booting kernel", static_command_line, __start___param,
           __stop___param - __start___param,
           -1, -1, &unknown_bootoption);
    //
    jump_label_init();
    /*
     * These use large bootmem allocations and must precede
     * kmem_cache_init()
     * 内存初始化相关
     */
    setup_log_buf(0);
    pidhash_init();
    vfs_caches_init_early();
    sort_main_extable();
    trap_init();
    mm_init();
    /*
     * Set up the scheduler prior starting any interrupts (such as the
     * timer interrupt). Full topology setup happens at smp_init()
     * time - but meanwhile we still have a functioning scheduler.
     * 调度初始化
     */
    sched_init();
    /*
     * Disable preemption - early bootup scheduling is extremely
     * fragile until we cpu_idle() for the first time.
     * 抢占禁用
     */
    preempt_disable();
    if (!irqs_disabled()) {
        printk(KERN_WARNING "start_kernel(): bug: interrupts were "
                "enabled *very* early, fixing it\n");
        local_irq_disable();
    }
    idr_init_cache();//idr
    perf_event_init();//performance event
    rcu_init();//read-copy-update 机制
    radix_tree_init();//radix树机制
    /* init some links before init_ISA_irqs() */
    early_irq_init();//中断请求
    init_IRQ();//中断请求
    prio_tree_init();//优先查找树
    init_timers();//时钟
    hrtimers_init();//High-resolution kernel timers高精度内核时钟
    softirq_init();//软中断
    timekeeping_init();//时间相关
    time_init();//时间
    profile_init();//分配内核性能统计保存的内存
    call_function_init();//smp中每cpu的call_single_queue初始化
    if (!irqs_disabled())
        printk(KERN_CRIT "start_kernel(): bug: interrupts were "
                 "enabled early\n");
    early_boot_irqs_disabled = false;//中断请求开
    local_irq_enable();//本地中断开
    kmem_cache_init_late();//kmem后期初始化
    /*
     * HACK ALERT! This is early. We&#39;re enabling the console before
     * we&#39;ve done PCI setups etc, and console_init() must be aware of
     * this. But we do want output early, in case something goes wrong.
     */
    console_init();//初始化系统控制台结构
    if (panic_later)
        panic(panic_later, panic_param);
    //锁依赖信息
    lockdep_info();
    /*
     * Need to run this when irqs are enabled, because it wants
     * to self-test [hard/soft]-irqs on/off lock inversion bugs
     * too:
     */
    locking_selftest();
#ifdef CONFIG_BLK_DEV_INITRD
    if (initrd_start && !initrd_below_start_ok &&
        page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {
        printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "
            "disabling it.\n",
            page_to_pfn(virt_to_page((void *)initrd_start)),
            min_low_pfn);
        initrd_start = 0;
    }
#endif
    page_cgroup_init();//control groups初始化
    debug_objects_mem_init();//对象调试
    kmemleak_init();//检测内核内存泄漏的功能
    setup_per_cpu_pageset();//申请并初始化每cpu页set
    numa_policy_init();//numa相关
    if (late_time_init)
        late_time_init();
    //初始化每cpusched_clock_data=ktime_now
    sched_clock_init();
    calibrate_delay();//计算cpuMIPS百万条指令/s
    pidmap_init();//pid进程id表初始化
    anon_vma_init();//虚拟地址
#ifdef CONFIG_X86
    if (efi_enabled)//efi bois
        efi_enter_virtual_mode();
#endif
    thread_info_cache_init();//申请thread_info的内存
    cred_init();//credential健在分配 
    //根据物理内存大小,计算可创建进/线程数量
    fork_init(totalram_pages); 
    proc_caches_init();//进程内存初始化
    buffer_init();//页高速缓存
    key_init();//红黑树内存,存keys
    security_init();//安全相关
    dbg_late_init();//调试相关
    vfs_caches_init(totalram_pages);//虚拟文件系统初始化
    signals_init();//sigqueue申请内存,信号系统
    /* rootfs populating might need page-writeback */
    page_writeback_init();//页回写
#ifdef CONFIG_PROC_FS
    proc_root_init();//proc文件系统初始化
#endif
    cgroup_init();//cgroup相关
    cpuset_init();//cpuset相关
    taskstats_init_early();//进程计数器
    delayacct_init();//进程延时审计
    check_bugs();//系统bug相关测试
    //acpi总线
    acpi_early_init(); /* before LAPIC and SMP init */
    sfi_init_late();//Simple Firmware Interface
    //功能追踪初始化,一种调试工具
    ftrace_init();
    /* Do the rest non-__init&#39;ed, we&#39;re now alive */
    rest_init();
}
/* Call all constructor functions linked into the kernel. */
static void __init do_ctors(void)
{
#ifdef CONFIG_CONSTRUCTORS
    ctor_fn_t *fn = (ctor_fn_t *) __ctors_start;
    for (; fn < (ctor_fn_t *) __ctors_end; fn++)
        (*fn)();
#endif
}
bool initcall_debug;
core_param(initcall_debug, initcall_debug, bool, 0644);
static char msgbuf[64];
static int __init_or_module do_one_initcall_debug(initcall_t fn)
{
    ktime_t calltime, delta, rettime;
    unsigned long long duration;
    int ret;
    printk(KERN_DEBUG "calling  %pF @ %i\n", fn, task_pid_nr(current));
    calltime = ktime_get();
    ret = fn();
    rettime = ktime_get();
    delta = ktime_sub(rettime, calltime);
    duration = (unsigned long long) ktime_to_ns(delta) >> 10;
    printk(KERN_DEBUG "initcall %pF returned %d after %lld usecs\n", fn,
        ret, duration);
    return ret;
}
int __init_or_module do_one_initcall(initcall_t fn)
{
    int count = preempt_count();
    int ret;
    if (initcall_debug)
        ret = do_one_initcall_debug(fn);
    else
        ret = fn();
    msgbuf[0] = 0;
    if (ret && ret != -ENODEV && initcall_debug)
        sprintf(msgbuf, "error code %d ", ret);
    if (preempt_count() != count) {
        strlcat(msgbuf, "preemption imbalance ", sizeof(msgbuf));
        preempt_count() = count;
    }
    if (irqs_disabled()) {
        strlcat(msgbuf, "disabled interrupts ", sizeof(msgbuf));
        local_irq_enable();
    }
    if (msgbuf[0]) {
        printk("initcall %pF returned with %s\n", fn, msgbuf);
    }
    return ret;
}
extern initcall_t __initcall_start[];
extern initcall_t __initcall0_start[];
extern initcall_t __initcall1_start[];
extern initcall_t __initcall2_start[];
extern initcall_t __initcall3_start[];
extern initcall_t __initcall4_start[];
extern initcall_t __initcall5_start[];
extern initcall_t __initcall6_start[];
extern initcall_t __initcall7_start[];
extern initcall_t __initcall_end[];
static initcall_t *initcall_levels[] __initdata = {
    __initcall0_start,
    __initcall1_start,
    __initcall2_start,
    __initcall3_start,
    __initcall4_start,
    __initcall5_start,
    __initcall6_start,
    __initcall7_start,
    __initcall_end,
};
static char *initcall_level_names[] __initdata = {
    "early parameters",
    "core parameters",
    "postcore parameters",
    "arch parameters",
    "subsys parameters",
    "fs parameters",
    "device parameters",
    "late parameters",
};
static void __init do_initcall_level(int level)
{
    extern const struct kernel_param __start___param[], __stop___param[];
    initcall_t *fn;
    strcpy(static_command_line, saved_command_line);
    parse_args(initcall_level_names[level],
           static_command_line, __start___param,
           __stop___param - __start___param,
           level, level,
           repair_env_string);
    for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
        do_one_initcall(*fn);
}
static void __init do_initcalls(void)
{
    int level;
    for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
        do_initcall_level(level);
}
/*
 * Ok, the machine is now initialized. None of the devices
 * have been touched yet, but the CPU subsystem is up and
 * running, and memory and process management works.
 *
 * Now we can finally start doing some real work..
 */
static void __init do_basic_setup(void)
{
    cpuset_init_smp();//smp cpuset相关
    usermodehelper_init();//khelper单线程工作队列
    shmem_init();//sheme机制
    driver_init();//驱动各子系统
    init_irq_proc();//proc中创建irq目录
    do_ctors();//内核中所有构造函数,介于.ctors段中的函数
    usermodehelper_enable();
    //所有编译进内核的驱动模块初始化函数
    do_initcalls();
}
static void __init do_pre_smp_initcalls(void)
{
    initcall_t *fn;
    for (fn = __initcall_start; fn < __initcall0_start; fn++)
        do_one_initcall(*fn);
}
static void run_init_process(const char *init_filename)
{
    argv_init[0] = init_filename;
    kernel_execve(init_filename, argv_init, envp_init);
}
/* This is a non __init function. Force it to be noinline otherwise gcc
 * makes it inline to init() and it becomes part of init.text section
 * 这是个非Init函数,防止gcc让它内联到init(),并成为Init.text段的一部分
 */
static noinline int init_post(void)
{
    /* need to finish all async __init code before freeing the memory 
     * 在释放init内存前,必须完成所有__init代码执行
     */
    async_synchronize_full();
    free_initmem();//释放init.*段中的内存
    //修改页表,保证只读数据段为只读属性read only
    mark_rodata_ro();
    //系统运行状态标志
    system_state = SYSTEM_RUNNING;
    //numa默认策略
    numa_default_policy();
    //当前进程不能被杀掉,只为它是init
    current->signal->flags |= SIGNAL_UNKILLABLE;
    //如果ramdisk_execute_command变量指定了init程序,执行它
    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.
     * 又一个程序,看能不能执行,如果不能,则执行下面4个之一
     */
    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");
    //两个变量和4个init都不能成功执行,报错
    panic("No init found.  Try passing init= option to kernel. "
          "See Linux Documentation/init.txt for guidance.");
}
static int __init kernel_init(void * unused)
{
    /*
     * Wait until kthreadd is all set-up.等待kthreadd的启动完成
     */
    wait_for_completion(&kthreadd_done);
    /* Now the scheduler is fully set up and can do blocking allocations 
     * 
     */
    gfp_allowed_mask = __GFP_BITS_MASK;
    /*
     * init can allocate pages on any node
     */
    set_mems_allowed(node_states[N_HIGH_MEMORY]);
    /*
     * init can run on any cpu.
     */
    set_cpus_allowed_ptr(current, cpu_all_mask);
    //cad_pid为接收Ctrl-alt-del操作的INT信号的进程ID,设置成了init的pid
    //说明init可接受这3个键
    cad_pid = task_pid(current);
    //smp系统准备、激活所有cpu
    smp_prepare_cpus(setup_max_cpus);
    do_pre_smp_initcalls();
    lockup_detector_init();
    smp_init();
    sched_init_smp();
    //初始化设备驱动、内核模块
    do_basic_setup();
    /* Open the /dev/console on the rootfs, this should never fail 
     * 打开/dev/console设备
     */
    if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
        printk(KERN_WARNING "Warning: unable to open an initial console.\n");
    /*
     * 复制两次标准输入0,一个是标准输入1,一个是标准错误2
     */
    (void) sys_dup(0);
    (void) sys_dup(0);
    /*
     * check if there is an early userspace init.  If yes, let it do all
     * the work
     * 是否有早期用户空间init进程,有的话,让其执行
     */
    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();
    }
    /*
     * Ok, we have completed the initial bootup, and
     * we&#39;re essentially up and running. Get rid of the
     * initmem segments and start the user-mode stuff..
     */
    //启动用户空间的init进程
    init_post();
    return 0;
}

推荐学习:《linux视频教程

The above is the detailed content of Does the linux kernel have a main function?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Previous article:Why is Linux stable?Next article:Why is Linux stable?