• Linux内核设计第三周——构造一个简单的Linux系统


    Linux内核设计第三周

    ——构造一个简单的Linux系统

    一、知识点总结

    计算机三个法宝:

    • 存储程序计算机
    • 函数调用堆栈
    • 中断

    操作系统两把宝剑:

    • 中断上下文的切换
    • 进程上下文的切换

    linux内核源代码分析

    arch/目录保存支持多种CPU类型的源代码

    • 其中的关键目录包括:Documentation、drivers、firewall、fs(文件系统)、include

    init目录:含有main.c,内核启动相关的代码基本都在init目录下

    • start_kernal()函数为启动函数,初始化内核的起点。

    ipc目录:进程间的通信 
    kernel目录:有Linux内核的核心代码。

    • pid.c、kthread.c

    lib目录:公用库文件 
    mm目录:内存管理代码 
    net目录:与网络相关代码 
    scripts目录:与脚本相关 
    security目录:与安全相关 
    sound目录:与声音相关 
    tools目录:与工具相关

    README文件:

    • 清理中间文件: 
      cd linux
      make mrproper
    • 安装一些模块,最终执行make 
      sudo make O = /home/name/build/kernal modules 
      install install // O= output/dir
      将所选项关闭,以提高安装速度 
      make allnoconfig

    二、实验过程

    图1 构建Menu系统的过程

    cd LinuxKernel/ 
    qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img

    其中rootfs.img 为根文件系统,目前只支持help、version、quit功能。

    图2 使用gdb进行调试

    qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S

    关于-s和-S选项的说明:

    -S 在启动前冻结cpu(使用 ’c’ 以开始执行) 
    -s 在端口1234上创建一个tcp连接 
    若不想使用1234端口,则可以使用-gdb tcp:xxxx来取代-s选项

    图3 将断点设置到start_kernal

    gdb 
    (gdb)file linux-3.18.6/vmlinux

    • 在gdb界面中targe remote之前加载符号表

    (gdb)target remote:1234

    • 建立gdb和gdbserver之间的连接,按c 让qemu上的Linux继续运行

    (gdb)break start_kernel

    • 断点的设置可以在target remote之前,也可以在之后

    (gdb)c

    • 开始执行

    (gdb)list

    • 列出断点位置的源代码

    图4 将断点设置到rest_init

    (gdb)break rest_init

    • 断点的设置可以在target remote之前,也可以在之后

    (gdb)c

    • 开始执行

    (gdb)list

    • 列出断点位置的源代码

    三、分析start_kernel函数的执行过程

    在init目录下的main.c有函数 ,其中包含start_kernel函数

    基本所有模块,都需要start_kernel来进行初始化。

    asmlinkage __visible void __init start_kernel(void)
    

    其中,有init_ task,set_ task_ stack_ end_ magic(&init_task);这个是手工创建的PCB,0号进程,即最终的idle进程。

    trap_init();//中断向量初始化
    mm_init();//内存管理模块初始化
    sched_init();//调度模块初始化   
    console_init();//控制模块初始化
    rest_init(); //其他模块初始化  
    

    其中rest_ init()调用了 
    -->kernel_ thread(kernel_ init, NULL, CLONE_ FS); 调用了
    -->run_ init_ process(ramdisk_ execute_ command); 
    //init是第一个用户态进程,是1号进程

    pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
    //创建了线程
    

    在rest_init中,各部分启动完毕后,

       /* Call into cpu_idle with preempt disabled */

    cpu_startup_entry(CPUHP_ONLINE); 
    
    调用static void cpu_idle_loop(void);
    里面有个while(1)
    
    也就是在系统没有进程需要执行时就调度idle进程
    

    总结下来:在start_ kernel启动后,rest_ init的中0号进程会一直存在。

    如图所示,第500行代码有start_kernel函数

    四、总结

    阐明对“Linux系统启动过程”的理解。

    ①在start_ kernel启动后,rest_ init的中0号进程会一直存在。 
    ②道生一(startkernel....cpuidle),一生二(kernel_init和kthreadd),二生三(即前面0、1和2三个进程),三生万物(1号进程是所有用户态进程的祖先,2号进程是所有内核线程的祖先)

    五、实验中遇到的问题

    1、在gdb调试的过程中,出现了如下图所示的问题,即输入“fil e linux-3.18.6/vmlinux”后,提示“没有那个文件或目录”。

    解决:需要先进入到linux-3.18.6目录下,然后再进入gdb中。

    六、附录:

    start_kernel函数的源代码如下:

      500asmlinkage __visible void __init start_kernel(void)
        501{
        502 char *command_line;
        503 char *after_dashes;
        504
        505 /*
        506  * Need to run as early as possible, to initialize the
        507  * lockdep hash:
        508  */
        509 lockdep_init();
        510 set_task_stack_end_magic(&init_task);
        511 smp_setup_processor_id();
        512 debug_objects_early_init();
        513
        514 /*
        515  * Set up the the initial canary ASAP:
        516  */
        517 boot_init_stack_canary();
        518
        519 cgroup_init_early();
        520
        521 local_irq_disable();
        522 early_boot_irqs_disabled = true;
        523
        524/*
        525 * Interrupts are still disabled. Do necessary setups, then
        526 * enable them
        527 */
        528 boot_cpu_init();
        529 page_address_init();
        530 pr_notice("%s", linux_banner);
        531 setup_arch(&command_line);
        532 mm_init_cpumask(&init_mm);
        533 setup_command_line(command_line);
        534 setup_nr_cpu_ids();
        535 setup_per_cpu_areas();
        536 smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
        537
        538 build_all_zonelists(NULL, NULL);
        539 page_alloc_init();
        540
        541 pr_notice("Kernel command line: %s
    ", boot_command_line);
        542 parse_early_param();
        543 after_dashes = parse_args("Booting kernel",
        544               static_command_line, __start___param,
        545               __stop___param - __start___param,
        546               -1, -1, &unknown_bootoption);
        547 if (!IS_ERR_OR_NULL(after_dashes))
        548     parse_args("Setting init args", after_dashes, NULL, 0, -1, -1,
        549            set_init_arg);
        550
        551 jump_label_init();
        552
        553 /*
        554  * These use large bootmem allocations and must precede
        555  * kmem_cache_init()
        556  */
        557 setup_log_buf(0);
        558 pidhash_init();
        559 vfs_caches_init_early();
        560 sort_main_extable();
        561 trap_init();  //中断向量初始化
        562 mm_init();  //内存管理模块初始化
        563
        564 /*
        565  * Set up the scheduler prior starting any interrupts (such as the
        566  * timer interrupt). Full topology setup happens at smp_init()
        567  * time - but meanwhile we still have a functioning scheduler.
        568  */
        569 sched_init();  //调度模块初始化
        570 /*
        571  * Disable preemption - early bootup scheduling is extremely
        572  * fragile until we cpu_idle() for the first time.
        573  */
        574 preempt_disable();
        575 if (WARN(!irqs_disabled(),
        576      "Interrupts were enabled *very* early, fixing it
    "))
        577     local_irq_disable();
        578 idr_init_cache();
        579 rcu_init();
        580 context_tracking_init();
        581 radix_tree_init();
        582 /* init some links before init_ISA_irqs() */
        583 early_irq_init();
        584 init_IRQ();
        585 tick_init();
        586 rcu_init_nohz();
        587 init_timers();
        588 hrtimers_init();
        589 softirq_init();
        590 timekeeping_init();
        591 time_init();
        592 sched_clock_postinit();
        593 perf_event_init();
        594 profile_init();
        595 call_function_init();
        596 WARN(!irqs_disabled(), "Interrupts were enabled early
    ");
        597 early_boot_irqs_disabled = false;
        598 local_irq_enable();
        599
        600 kmem_cache_init_late();
        601
        602 /*
        603  * HACK ALERT! This is early. We're enabling the console before
        604  * we've done PCI setups etc, and console_init() must be aware of
        605  * this. But we do want output early, in case something goes wrong.
        606  */
        607 console_init();
        608 if (panic_later)
        609     panic("Too many boot %s vars at `%s'", panic_later,
        610           panic_param);
        611
        612 lockdep_info();
        613
        614 /*
        615  * Need to run this when irqs are enabled, because it wants
        616  * to self-test [hard/soft]-irqs on/off lock inversion bugs
        617  * too:
        618  */
        619 locking_selftest();
        620
        621#ifdef CONFIG_BLK_DEV_INITRD
        622 if (initrd_start && !initrd_below_start_ok &&
        623     page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {
        624     pr_crit("initrd overwritten (0x%08lx < 0x%08lx) - disabling it.
    ",
        625         page_to_pfn(virt_to_page((void *)initrd_start)),
        626         min_low_pfn);
        627     initrd_start = 0;
        628 }
        629#endif
        630 page_cgroup_init();
        631 debug_objects_mem_init();
        632 kmemleak_init();
        633 setup_per_cpu_pageset();
        634 numa_policy_init();
        635 if (late_time_init)
        636     late_time_init();
        637 sched_clock_init();
        638 calibrate_delay();
        639 pidmap_init();
        640 anon_vma_init();
        641 acpi_early_init();
        642#ifdef CONFIG_X86
        643 if (efi_enabled(EFI_RUNTIME_SERVICES))
        644     efi_enter_virtual_mode();
        645#endif
        646#ifdef CONFIG_X86_ESPFIX64
        647 /* Should be run before the first non-init thread is created */
        648 init_espfix_bsp();
        649#endif
        650 thread_info_cache_init();
        651 cred_init();
        652 fork_init(totalram_pages);
        653 proc_caches_init();
        654 buffer_init();
        655 key_init();
        656 security_init();
        657 dbg_late_init();
        658 vfs_caches_init(totalram_pages);
        659 signals_init();
        660 /* rootfs populating might need page-writeback */
        661 page_writeback_init();
        662 proc_root_init();
        663 cgroup_init();
        664 cpuset_init();
        665 taskstats_init_early();
        666 delayacct_init();
        667
        668 check_bugs();
        669
        670 sfi_init_late();
        671
        672 if (efi_enabled(EFI_RUNTIME_SERVICES)) {
        673     efi_late_init();
        674     efi_free_boot_services();
        675 }
        676
        677 ftrace_init();
        678
        679 /* Do the rest non-__init'ed, we're now alive */
        680 rest_init();  //其他模块初始化
        681}
  • 相关阅读:
    windows下使用curl命令,以及常用curl命令
    使用 C# 下载文件的十八般武艺
    初闻不知曲中意,再听已是曲中人
    从线性回归走进机器学习
    自定义Vue&Element组件,实现用户选择和显示
    ARP协议原理——地址解析协议, 用于实现从 IP 地址到 MAC 地址的映射,即询问目标IP对应的MAC地址,ARP整个完整交互过程仅需要两个包,一问一答即可搞定
    SSH加密隧道流量攻击与检测技术——这玩意和思科加密流量检测没有本质区别啊,可借鉴CNN图像
    bt2——基于telegram的C2
    通过gmail进行C2控制——看了下源码,本质上和dropbox c2架构一样,都是去轮训邮件,将c2攻击的东西以邮件形式发送,结果也发到邮箱里
    DropboxC2 工具原理总结——就是通过dropbox文件来间接做c2控制和交互。
  • 原文地址:https://www.cnblogs.com/java-stx/p/5260128.html
Copyright © 2020-2023  润新知