首页 运维 linux运维 linux内核有main函数吗

linux内核有main函数吗

Mar 28, 2023 am 10:45 AM
linux main

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

linux内核有main函数吗

本教程操作环境: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视频教程

以上是linux内核有main函数吗的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
4 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

无法以 root 身份登录 mysql 无法以 root 身份登录 mysql Apr 08, 2025 pm 04:54 PM

无法以 root 身份登录 MySQL 的原因主要在于权限问题、配置文件错误、密码不符、socket 文件问题或防火墙拦截。解决方法包括:检查配置文件中 bind-address 参数是否正确配置。查看 root 用户权限是否被修改或删除,并进行重置。验证密码是否准确无误,包括大小写和特殊字符。检查 socket 文件权限设置和路径。检查防火墙是否阻止了 MySQL 服务器的连接。

C语言条件编译:新手入门到实战应用的详尽指南 C语言条件编译:新手入门到实战应用的详尽指南 Apr 04, 2025 am 10:48 AM

C语言条件编译是一种根据编译时条件选择性编译代码块的机制,入门方法有:使用#if和#else指令根据条件选择代码块。常用条件表达式包括STDC、_WIN32和linux。实战案例:根据操作系统打印不同消息。根据系统位数使用不同的数据类型。根据编译器支持不同的头文件。条件编译增强了代码的可移植性和灵活性,使其适应编译器、操作系统和CPU架构变化。

Linux的5个基本组件是什么? Linux的5个基本组件是什么? Apr 06, 2025 am 12:05 AM

Linux的五个基本组件是:1.内核,管理硬件资源;2.系统库,提供函数和服务;3.Shell,用户与系统交互的接口;4.文件系统,存储和组织数据;5.应用程序,利用系统资源实现功能。

mysql 无法启动怎么解决 mysql 无法启动怎么解决 Apr 08, 2025 pm 02:21 PM

MySQL启动失败的原因有多种,可以通过检查错误日志进行诊断。常见原因包括端口冲突(检查端口占用情况并修改配置)、权限问题(检查服务运行用户权限)、配置文件错误(检查参数设置)、数据目录损坏(恢复数据或重建表空间)、InnoDB表空间问题(检查ibdata1文件)、插件加载失败(检查错误日志)。解决问题时应根据错误日志进行分析,找到问题的根源,并养成定期备份数据的习惯,以预防和解决问题。

mysql 可以在 android 上运行吗 mysql 可以在 android 上运行吗 Apr 08, 2025 pm 05:03 PM

MySQL无法直接在Android上运行,但可以通过以下方法间接实现:使用轻量级数据库SQLite,由Android系统自带,无需单独服务器,资源占用小,非常适合移动设备应用。远程连接MySQL服务器,通过网络连接到远程服务器上的MySQL数据库进行数据读写,但存在网络依赖性强、安全性问题和服务器成本等缺点。

MySQL安装在特定系统版本上报错的解决途径 MySQL安装在特定系统版本上报错的解决途径 Apr 08, 2025 am 11:54 AM

MySQL安装报错的解决方法是:1.仔细检查系统环境,确保满足MySQL的依赖库要求,不同操作系统和版本需求不同;2.认真阅读报错信息,根据提示(例如缺少库文件或权限不足)采取对应措施,例如安装依赖或使用sudo命令;3.必要时,可尝试源码安装并仔细检查编译日志,但这需要一定的Linux知识和经验。最终解决问题的关键在于仔细检查系统环境和报错信息,并参考官方文档。

mySQL下载完安装不了 mySQL下载完安装不了 Apr 08, 2025 am 11:24 AM

MySQL安装失败的原因主要有:1.权限问题,需以管理员身份运行或使用sudo命令;2.依赖项缺失,需安装相关开发包;3.端口冲突,需关闭占用3306端口的程序或修改配置文件;4.安装包损坏,需重新下载并验证完整性;5.环境变量配置错误,需根据操作系统正确配置环境变量。解决这些问题,仔细检查每个步骤,就能顺利安装MySQL。

无法从终端访问 mysql 无法从终端访问 mysql Apr 08, 2025 pm 04:57 PM

无法从终端访问 MySQL 可能是由于:MySQL 服务未运行;连接命令错误;权限不足;防火墙阻止连接;MySQL 配置文件错误。

See all articles