• 分析Linux内核的启动过程


    第一章 环境

    Ubuntu 14.10

    Linux Kernel 3.18.6

    第二章 代码及调试过程

    环境搭建与内核准备:

    cd ~/LinuxKernel/
    wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.18.6.tar.xz
    xz -d linux-3.18.6.tar.xz
    tar -xvf linux-3.18.6.tar
    cd linux-3.18.6
    make i386_defconfig
    make 
    
    cd ~/LinuxKernel/
    mkdir rootfs
    git clone  https://github.com/mengning/menu.git
    cd menu
    gcc -o init linktable.c menu.c test.c -m32 -static –lpthread
    cd ../rootfs
    cp ../menu/init ./
    find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img
    
    cd ~/LinuxKernel/
    qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img
    又一次编译内核:
    make menuconfig
    kernel hacking—>
    compile-time checks and compile options
    [*] compile the kernel with debug info
    開始调试:
    qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S 
    同一时候另开一个窗体。执行GDB:
    gdb
    (gdb)file linux-3.18.6/vmlinux 
    (gdb)target remote:1234 
    (gdb)break start_kernel
    (gdb)list

    这一次代码就不贴出来了,在init/mian.c下,主要是代码太长。


    http://codelab.shiyanlou.com/xref/linux-3.18.6/init/main.c

    单击对应的链接能够找到引用的变量位置。我下载了几个。

    这里打算分析:

    cpu_startup_entry

    page_address_init()

    rest_init()

    page_alloc_init()

    trap_init()

    tick_init()

    profile_init()

    key_init()

    security_init()

    buffer_init()

    init_task 0号进程

    run_init_process(const char *init_filename) 1号进程

    0号进程init_task的结构:

    #define INIT_TASK(tsk)	
    {									
    	.state		= 0,						
    	.stack		= &init_thread_info,				
    	.usage		= ATOMIC_INIT(2),				
    	.flags		= PF_KTHREAD,					
    	.prio		= MAX_PRIO-20,					
    	.static_prio	= MAX_PRIO-20,					
    	.normal_prio	= MAX_PRIO-20,					
    	.policy		= SCHED_NORMAL,					
    	.cpus_allowed	= CPU_MASK_ALL,					
    	.nr_cpus_allowed= NR_CPUS,					
    	.mm		= NULL,						
    	.active_mm	= &init_mm,					
    	.se		= {						
    		.group_node 	= LIST_HEAD_INIT(tsk.se.group_node),	
    	},								
    	.rt		= {						
    		.run_list	= LIST_HEAD_INIT(tsk.rt.run_list),	
    		.time_slice	= RR_TIMESLICE,				
    	},								
    	.tasks		= LIST_HEAD_INIT(tsk.tasks),			
    	INIT_PUSHABLE_TASKS(tsk)					
    	INIT_CGROUP_SCHED(tsk)						
    	.ptraced	= LIST_HEAD_INIT(tsk.ptraced),			
    	.ptrace_entry	= LIST_HEAD_INIT(tsk.ptrace_entry),		
    	.real_parent	= &tsk,						
    	.parent		= &tsk,						
    	.children	= LIST_HEAD_INIT(tsk.children),			
    	.sibling	= LIST_HEAD_INIT(tsk.sibling),			
    	.group_leader	= &tsk,						
    	RCU_POINTER_INITIALIZER(real_cred, &init_cred),			
    	RCU_POINTER_INITIALIZER(cred, &init_cred),			
    	.comm		= INIT_TASK_COMM,				
    	.thread		= INIT_THREAD,					
    	.fs		= &init_fs,					
    	.files		= &init_files,					
    	.signal		= &init_signals,				
    	.sighand	= &init_sighand,				
    	.nsproxy	= &init_nsproxy,				
    	.pending	= {						
    		.list = LIST_HEAD_INIT(tsk.pending.list),		
    		.signal = {{0}}},					
    	.blocked	= {{0}},					
    	.alloc_lock	= __SPIN_LOCK_UNLOCKED(tsk.alloc_lock),		
    	.journal_info	= NULL,						
    	.cpu_timers	= INIT_CPU_TIMERS(tsk.cpu_timers),		
    	.pi_lock	= __RAW_SPIN_LOCK_UNLOCKED(tsk.pi_lock),	
    	.timer_slack_ns = 50000, /* 50 usec default slack */		
    	.pids = {							
    		[PIDTYPE_PID]  = INIT_PID_LINK(PIDTYPE_PID),		
    		[PIDTYPE_PGID] = INIT_PID_LINK(PIDTYPE_PGID),		
    		[PIDTYPE_SID]  = INIT_PID_LINK(PIDTYPE_SID),		
    	},								
    	.thread_group	= LIST_HEAD_INIT(tsk.thread_group),		
    	.thread_node	= LIST_HEAD_INIT(init_signals.thread_head),	
    	INIT_IDS							
    	INIT_PERF_EVENTS(tsk)						
    	INIT_TRACE_IRQFLAGS						
    	INIT_LOCKDEP							
    	INIT_FTRACE_GRAPH						
    	INIT_TRACE_RECURSION						
    	INIT_TASK_RCU_PREEMPT(tsk)					
    	INIT_TASK_RCU_TASKS(tsk)					
    	INIT_CPUSET_SEQ(tsk)						
    	INIT_RT_MUTEXES(tsk)						
    	INIT_VTIME(tsk)							
    }

    第三章 调试

    设置断点:一共13个。

    命令为:

    (gdb)break <function name>
    接着输入c进行调试。

    流程例如以下:


    来几张有代表性的调试图片:

    start_kernel:


    page_address_init:


    buffer_init:


    security_init:


    cpu_startup_entry:



    run_init_process:


    第四章 总结

    通过这个方式我们知道了。Linux内核通过调用那些函数来启动,看起来在启动的时候仅仅有些跳动的字符,但是在内部是非常忙碌的。希望以后能了解到每个函数的详细作用。只是,这就非常困难的了。

    set_task_stack_end_magic确立一个init_task。这个便是后来的0号进程,也就是idle。

    通过參考资料:http://blog.chinaunix.net/uid-27767798-id-3577069.html

    通过检測时钟中断来提醒idle进程来复制自己的进程信息,来创建一个进程。

    就好像之前的myKernel一样。

    然后idle进程就会检測,假设有新的任务便会中断执行。生成并释放系统资源让进程使用。当进程结束,系统便将资源收回返回给idle进程。

    当执行到run_init_process时。1号进程便開始执行。

    附录


    卢晅 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
  • 相关阅读:
    Visual Studio使用技巧笔记(引用程序集自动复制dll到引用项目目录)
    图解-Excel的csv格式特殊字符处理方式尝试笔记(个人拙笔)
    Nuget.config格式错误,请检查nuget.config配置文件
    securecrt切换会话(session)的显示方式
    javascript将分,秒,毫秒转换为xx天xx小时xx秒(任何语言通用,最通俗易懂)
    Http状态码枚举(摘自 Microsoft 程序集 System.dll)
    Visual Studio 提示某个dll文件(已在Microsoft Visual Studio 外对该文件进行了修改,是否重新加载它)
    IIS Express mime type 列表。
    为什么要 MySQL 迁移到 Maria DB
    降维技术---PCA
  • 原文地址:https://www.cnblogs.com/yangykaifa/p/6890616.html
Copyright © 2020-2023  润新知