kernel 启动流程之 【设备驱动加载】 学习笔记

先上总流程图,一图胜千言!


head.S 初始化完成后跳转到 start_kernel 入口:node

kernel-3.18/init/main.c:505:asmlinkage __visible void __init start_kernel(void)

START => 源码分析:linux

这里要干的事情很是多而复杂,若是须要所有理解整个过程的话须要强大的知识背景作支撑以及对内核有着深刻的理解才行,基于学习的按部就班的考量,这里咱们重点关注device driver的启动加载流程机制.

asmlinkage __visible void __init start_kernel(void)
{
	char *command_line;
	char *after_dashes;

	/*
	 * Need to run as early as possible, to initialize the
	 * lockdep hash:
	 */
	 
/* 有些体系结构有本身的start_kernel入口,这里保证只初始化一次
   系统哈希表chainhash_table */
	lockdep_init();

	set_task_stack_end_magic(&init_task);
	
/* 获取当前执行cpu的id */
	smp_setup_processor_id();
	
/* 对象调试支持初始化 */
	debug_objects_early_init();

	/*
	 * Set up the the initial canary ASAP:
	 */
	 
/* 初始化栈canary值,canary值用于防止栈溢出攻击,这里不太明白详细...*/
	boot_init_stack_canary();

/* cgrop :将一组任务在一个或多个子系统中与一组参数关联,机制有点复杂... */
	cgroup_init_early();
	
/* 关闭中断,由于不少初始化的工做不能被中断 */
	local_irq_disable();
	early_boot_irqs_disabled = true;

/*
 * Interrupts are still disabled. Do necessary setups, then
 * enable them
 */
 
 /* 获取当前cpu id,激活之 */
	boot_cpu_init();
	
 /* 高端内存相关,创建内核映射所需的散列表 */
	page_address_init();
	pr_notice("%s", linux_banner);
	
 /* 体系结构相关初始化,没种体系结构都有特有的初始化入口 */
	setup_arch(&command_line);
	
 /* cpu屏蔽位清零 */
	mm_init_cpumask(&init_mm);
	
 /* 将命令行参数保存到 static_command_line 中 */
	setup_command_line(command_line);
	
	setup_nr_cpu_ids();
	setup_per_cpu_areas();
	smp_prepare_boot_cpu();	/* arch-specific boot-cpu hooks */

/* 创建系统内存页区(zone)链表 */
	build_all_zonelists(NULL, NULL);
	
/* cpu热拔插相关, 这名字取得... */
	page_alloc_init();

	pr_notice("Kernel command line: %s\n", boot_command_line);
	
 /* 解析启动命令行参数 */
	parse_early_param();
	after_dashes = parse_args("Booting kernel",
				  static_command_line, __start___param,
				  __stop___param - __start___param,
				  -1, -1, &unknown_bootoption);
	if (!IS_ERR_OR_NULL(after_dashes))
		parse_args("Setting init args", after_dashes, NULL, 0, -1, -1,
			   set_init_arg);

/* 执行跳转指令? */
	jump_label_init();

	/*
	 * These use large bootmem allocations and must precede
	 * kmem_cache_init()
	 */
	 
/* 设置 log输出缓冲buf */
	setup_log_buf(0);
	
/* 初始化和分配pid散列表 */
	pidhash_init();

/* 建立虚拟文件系统(vfs)须要各类数据结构的缓存 */
	vfs_caches_init_early();
	
/* 内核异常表排序 */
	sort_main_extable();
	
/* 异常捕获设置初始化,跟体系结构相关,arm架构的实现是空函数 */
	trap_init();
	
/* 内核内存分配器初始化,初始化slab机制分配器和vmalloc机制 */
	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 (WARN(!irqs_disabled(),
		 "Interrupts were enabled *very* early, fixing it\n"))
		local_irq_disable();
		
/* 为 idr(一种将一个整数ID号和一个指针关联在一块儿的机制) 机制建立cache */
	idr_init_cache();
	
/* rcu(read-copy-update, 内核锁机制一类)机制初始化 */
	rcu_init();
	
/* 上下文tracking机制 ? 该机制被 CONFIG_CONTEXT_TRACKING_FORCE 包住了 */
	context_tracking_init();
	
/* 为内核基数树算法分配内存,运用于内存页查找 */
	radix_tree_init();
	/* init some links before init_ISA_irqs() */
	
/* 初始化体系结构相关irq,建立irq描述符,插入到基数属链表 irq_desc_tree 中管理*/
	early_irq_init();
	init_IRQ();
	
/* 时钟相关初始化*/
	tick_init();
	rcu_init_nohz();
	init_timers();
	hrtimers_init();
	softirq_init();
	timekeeping_init();
	time_init();
	
/* 进程调度时钟初始化 */
	sched_clock_postinit();

/* cpu 性能相关monitor */
	perf_event_init();
	
/* gdb等debug工具设置相关 */
	profile_init();

/* smp下跨cpu的函数传递初始化 */
	call_function_init();
	WARN(!irqs_disabled(), "Interrupts were enabled early\n");
	early_boot_irqs_disabled = false;
	
/* 使能中断 */
	local_irq_enable();

/* slab 分配器后期初始化 */
	kmem_cache_init_late();

	/*
	 * HACK ALERT! This is early. We're enabling the console before
	 * we'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();
	
/* 检查异常记录信息,若是存在异常,走内核panic流程 */
	if (panic_later)
		panic("Too many boot %s vars at `%s'", panic_later,
		      panic_param);

/* printk 输出相关信息 */
	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();

/* 容器组的页面内存分配 ? */
	page_cgroup_init();
	page_ext_init();
	
/* debug 相关*/
	debug_objects_mem_init();
	
/* 内存leak监视 */
	kmemleak_init();
	setup_per_cpu_pageset();
	numa_policy_init();
	if (late_time_init)
		late_time_init();
	sched_clock_init();
	calibrate_delay();
	
/* 进程pid 映射表初始化 */
	pidmap_init();
	anon_vma_init();
	acpi_early_init();

/* 建立内核进程分配的cache */
	thread_info_cache_init();
	cred_init();
	
/* fork 机制初始化 */
	fork_init(totalram_pages);
	
/* 建立进程须要的slab缓存 */
	proc_caches_init();
	buffer_init();
	
/* 内核安全架构初始化 */
	key_init();
	security_init();
	
/* kgdb 在线调试相关支持*/
	dbg_late_init();
	
/* vfs所须要的slab缓存 */
	vfs_caches_init(totalram_pages);
	
/* 为 sigqueue_cachep 建立slab缓存 */
	signals_init();
	/* rootfs populating might need page-writeback */
	
/* 内存页写回机制初始化 */
	page_writeback_init();
	
/* proc 文件系统 */
	proc_root_init();
	
	
	cgroup_init();
	cpuset_init();
	taskstats_init_early();
	delayacct_init();

	check_bugs();

	acpi_subsystem_init();
	sfi_init_late();

	if (efi_enabled(EFI_RUNTIME_SERVICES)) {
		efi_late_init();
		efi_free_boot_services();
	}

/* trace 初始化 */
	ftrace_init();

/* 建立2号内核线程kthreadd,初始化device driver,拉起1号进程init等 */
	rest_init();
}

小结:
从上面注释能够看到 start_kernel 函数干的事情很是多(这里不一一罗列了),并且几乎每个子函数包含的内容都很复杂须要很是深厚的操做系统、计算机结构和数据结构等知识积累才能分析清楚,而这里咱们感兴趣和重点关注跟咱们实际工做关系最密切的设备驱动的加载过程。

rest_init 分析.算法

static noinline void __init_refok rest_init(void) {
	int pid;
...
	 
	// 由0号进程fork出1号进程进入kernel_init,拉起第一个用户空间程序init.
	kernel_thread(kernel_init, NULL, CLONE_FS);
	numa_default_policy();
	
	// 建立2号内核线程.
	pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
	rcu_read_lock();
	kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
	rcu_read_unlock();
	
	// 等待kthreadd初始化完成。
	complete(&kthreadd_done);

	init_idle_bootup_task(current);
	schedule_preempt_disabled();

    // 0 号进程最终成为idle运行。
	cpu_startup_entry(CPUHP_ONLINE);
}
 
 
以下图:
内核0号进程fork出1号跟2号进程,1号进程演变成init进程,2号进程就是kthread进程,后面内核空间全部新进程都由2号进程fork出来。
 
 
用户空间全部新进程都是1号进程的子孙:
 
 
小结:
一、首先0号进程fork出1号init进程,执行设备驱动初始化和拉起第一个用户空间程序init;
二、接着0号进程fork出2号内核线程(kthreadd)做为内核的守护进程;
三、而后0号进程进入idle循环.
static int __ref kernel_init(void *unused) {
	int ret;

// 该函数内包含了初始化各类设备驱动程序等操做.
	kernel_init_freeable();
...
	if (execute_command) {
		ret = run_init_process(execute_command);
		if (!ret)
			return 0;
		pr_err("Failed to execute %s (error %d). Attempting defaults...\n",
			execute_command, ret);
	}
	
// 依次找而后执行init程序.
	if (!try_to_run_init_process("/sbin/init") ||
	    !try_to_run_init_process("/etc/init") ||
	    !try_to_run_init_process("/bin/init") ||
	    !try_to_run_init_process("/bin/sh"))
		return 0;

// 若是从上面路径都没有找到init程序就直接kernel panic了.
	panic("No working init found. Try passing init= option to kernel. "
	      "See Linux Documentation/init.txt for guidance.");
}
static noinline void __init kernel_init_freeable(void) {
	/* * 这里须要等kthreadd初始化完成才能够继续干活,不然会报内核 Oops. */
	wait_for_completion(&kthreadd_done);

...
    // 进入各类基础初始化
	do_basic_setup();

...
}
static void __init do_basic_setup(void)
{
	cpuset_init_smp();
	usermodehelper_init();
	shmem_init();
// 驱动数据结构初始化申请内存,注册内核对象
	driver_init();
	init_irq_proc();
	do_ctors();
	usermodehelper_enable();
// 执行设备驱动入口回调
	do_initcalls();
	random_int_secret_init();
}
// 依次遍历各个levnel的驱动程序,level越小越先执行。
static void __init do_initcalls(void) {
	int level;

	for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
		do_initcall_level(level);
}

下面的 do_inicall_level 函数颇有趣也比较关键, 咱们知道,设备驱动入口都是相似这样写的:module_init(fn),而这个module_init宏 其实就是这里的level 6, 以此为例,内核机制和 编译系统 机制提供实现将这个函数指针fn加到kernel内存布局的 ".init.data" section(段)地址空间中并依次增加,直到添加完全部同level的的设备入口fn到该内存段中,而后再下一个 level的操做...这里实现机制涉及到kernel内存布局相关, 须要参考vmlinux.ld.S文件分析. 因此下面代码中的initcall_levels是一个指针数组, 数组成员也是一个指针数组,数组内容是对应level的函数指针, 因此这里要干的事情就是依次取出各个level的函数指针fn执行(*fn)回调, 也就是实现了依次遍历各个level的驱动程序。

static void __init do_initcall_level(int level) {
	initcall_t *fn;
...
	/* 依次遍历各个level的驱动程序 */
	for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++) 
	{
		pr_err("xxx## %s, %d ,%d: %pf \n", __func__,__LINE__,level, *fn); // 能够验证打印出来看看是什么.
		do_one_initcall(*fn);
	}
}

// 每一个数组成员也是一个指针数组,而成员的数组成员是函数指针,__initdata代表了所在的内存段位置.
static initcall_t *initcall_levels[] __initdata = {
	__initcall0_start, 
	__initcall1_start,
	__initcall2_start,
	__initcall3_start,
	__initcall4_start,
	__initcall5_start,
	__initcall6_start,
	__initcall7_start,
	__initcall_end,
};
int __init_or_module do_one_initcall(initcall_t fn) { int count = preempt_count();
	int ret;
	char msgbuf[64];
...
    // 这个还用的着注释么?.
	ret = fn();
...
	return ret;
}

验证推测的最好办法就是加log测试,下面是kernel log输出结果,包括fn的函数名和level:数组

 
 
Line 2376: [    0.199826] <2>.(2)[1:swapper/0]xxx## do_initcall_level, 883 ,0: init_mmap_min_addr 
	Line 2378: [    0.200871] <2>.(2)[1:swapper/0]xxx## do_initcall_level, 883 ,0: mtk_arch_reset_init 
	Line 2386: [    0.204162] <2>.(2)[1:swapper/0]xxx## do_initcall_level, 883 ,0: init_cpufreq_transition_notifier_list 
	Line 2388: [    0.205416] <2>.(2)[1:swapper/0]xxx## do_initcall_level, 883 ,0: net_ns_init 
	Line 2390: [    0.206843] <2>.(2)[1:swapper/0]xxx## do_initcall_level, 883 ,1: vfp_init 
	Line 2398: [    0.209921] <2>.(2)[1:swapper/0]xxx## do_initcall_level, 883 ,1: ptrace_break_init 
...
	Line 2468: [    0.264062] <2>.(2)[1:swapper/0]xxx## do_initcall_level, 883 ,2: atomic_pool_init 
	Line 2472: [    0.266331] <2>.(2)[1:swapper/0]xxx## do_initcall_level, 883 ,2: bdi_class_init 
...
	Line 2524: [    0.319510] <1>.(1)[1:swapper/0]xxx## do_initcall_level, 883 ,3: gate_vma_init 
	Line 2526: [    0.320495] <1>.(1)[1:swapper/0]xxx## do_initcall_level, 883 ,3: customize_machine 
...
	Line 3236: [    0.829585] <0>.(0)[1:swapper/0]xxx## do_initcall_level, 883 ,4: proc_schedstat_init 
	Line 3238: [    0.830644] <0>.(0)[1:swapper/0]xxx## do_initcall_level, 883 ,4: pm_sysrq_init 
...
	Line 3522: [    1.005405] <1>.(2)[1:swapper/0]xxx## do_initcall_level, 883 ,4: wireless_nlevent_init 
	Line 3524: [    1.008609] <2>.(2)[1:swapper/0]xxx## do_initcall_level, 883 ,4: activity_stats_init 
...
	Line 4136: [    1.542339] <3>.(3)[1:swapper/0]xxx## do_initcall_level, 883 ,6: register_pmu_driver 
	Line 4142: [    1.569623] <1>.(1)[1:swapper/0]xxx## do_initcall_level, 883 ,6: kallsyms_init 
	Line 4144: [    1.570621] <1>.(1)[1:swapper/0]xxx## do_initcall_level, 883 ,6: audit_init 
	...
	Line 4474: [    1.763312] <1>.(1)[1:swapper/0]xxx## do_initcall_level, 883 ,6: ltr553_init 
	Line 4482: [    1.767457] <1>.(1)[1:swapper/0]xxx## do_initcall_level, 883 ,6: epl_sensor_init 
	Line 4492: [    1.771665] <1>.(1)[1:swapper/0]xxx## do_initcall_level, 883 ,6: bma222_init 
	Line 4498: [    1.774798] <1>.(1)[1:swapper/0]xxx## do_initcall_level, 883 ,6: akm09911_init 
	Line 4500: [    1.776511] <1>.(1)[1:swapper/0]xxx## do_initcall_level, 883 ,6: hall_init 
...
	Line 6436: [    2.805940] <2>.(3)[1:swapper/0]xxx## do_initcall_level, 883 ,7: init_machine_late 
...

如今咱们已经实现了从start_kernel到回调驱动入口代码的流程分析,还有比较关心的就是从kernel_init到具体某一个驱动的probe函数的整个代码流程是怎么样的(深刻了解这个流程对于debug有实际意义), 一般作法有两种方式,一种就是继续跟代码,一步一步的看逻辑找答案,另一种比较简单而实用的方式就是使用调试手段加dump_stack(),把call stack打印出来就清楚明了了, 咱们的作法是二者结合,这样是最清楚不过的了。
先来看 简单实用 的方式,下面以 battery_meter_init 代码为例,看从模块入口到probe的代码路径是怎么样的:

static int battery_meter_probe(struct platform_device *dev) {
    int ret_device_file = 0;
    char *temp_strptr;

    battery_log(BAT_LOG_CRTI, "------- battery_meter_probe!! -------\n");
    dump_stack();
...

编译-烧机-抓开机log:缓存

[ 2.322439] <1>.(1)[1:swapper/0]------- battery_meter_probe!! ------- [ 2.323246] <1>-(1)[1:swapper/0]CPU: 1 PID: 1 Comm: swapper/0 Tainted: G W 3.18.35+ #4 [ 2.324357] Backtrace: [ 2.324676] <1>-(1)[1:swapper/0][<c010badc>] (dump_backtrace) from [<c010bc7c>] (show_stack+0x18/0x1c) [ 2.325832] r6:c103d790 r5:ffffffff r4:00000000 r3:00000000 [ 2.326547] <1>-(1)[1:swapper/0][<c010bc64>] (show_stack) from [<c0a92d58>] (dump_stack+0x90/0xa4) [ 2.327669] <1>-(1)[1:swapper/0][<c0a92cc8>] (dump_stack) from [<c07e1f2c>] (battery_meter_probe+0x40/0x22c) [ 2.328889] r8:c1080b7c r7:c1080b7c r6:c044fcfc r5:c12f5318 r4:c1080a38 r3:00000000 [ 2.329866] <1>-(1)[1:swapper/0][<c07e1eec>] (battery_meter_probe) from [<c03c4354>] (platform_drv_probe+0x38/0x90) [ 2.331162] r7:c1080b7c r6:fffffdfb r5:c1080a48 r4:ffffffed [ 2.331877] <1>-(1)[1:swapper/0][<c03c431c>] (platform_drv_probe) from [<c03c29e4>] (driver_probe_device+0x1d8/0x43c) [ 2.333195] r7:c115017c r6:c10a7c38 r5:c1080a48 r4:c1150170 [ 2.333908] <1>-(1)[1:swapper/0][<c03c280c>] (driver_probe_device) from [<c03c2c90>] (__device_attach+0x48/0x4c) [ 2.335171] r10:00000000 r9:00000000 r8:00000000 r7:00000000 r6:c03c2c48 r5:c1080a48 [ 2.336147] r4:c1080b7c [ 2.336470] <1>-(1)[1:swapper/0][<c03c2c48>] (__device_attach) from [<c03c129c>] (bus_for_each_drv+0x60/0x94) [ 2.337701] r5:c1080a48 r4:00000000 [ 2.338154] <1>-(1)[1:swapper/0][<c03c123c>] (bus_for_each_drv) from [<c03c2dac>] (device_attach+0x80/0x88) [ 2.339363] r6:c1040f40 r5:c1080a7c r4:c1080a48 [ 2.339945] <1>-(1)[1:swapper/0][<c03c2d2c>] (device_attach) from [<c03c1524>] (bus_probe_device+0x8c/0xb0) [ 2.341154] r6:c1040f40 r5:c1080a48 r4:c1080a50 r3:df166000 [ 2.341870] <1>-(1)[1:swapper/0][<c03c1498>] (bus_probe_device) from [<c03bf2a4>] (device_add+0x43c/0x558) [ 2.343068] r6:c1080a48 r5:c1040e40 r4:c1080a50 r3:00000001 [ 2.343784] <1>-(1)[1:swapper/0][<c03bee68>] (device_add) from [<c03c4a04>] (platform_device_add+0xd0/0x264) [ 2.345004] r9:de089bc0 r8:c1080bd4 r7:c1080bd4 r6:c1080a48 r5:c1080a38 r4:00000000 [ 2.345979] <1>-(1)[1:swapper/0][<c03c4934>] (platform_devdev) from [<c03c2dd8>] (driver_attach+0x24/0x28) [ 2.359715] r6:c1040f40 r5:de087b80 r4:c1080bd4 [ 2.360298] <1>-(1)[1:swapper/0][<c03c2db4>] (driver_attach) from [<c03c17f0>] (bus_add_driver+0x15c/0x218) [ 2.361516] <1>-(1)[1:swapper/0][<c03c1694>] (bus_add_driver) from [<c03c3750>] (driver_register+0x80/0x100) [ 2.362735] r7:df04a038 r6:c0f39e6c r5:00000000 r4:c1080bd4 [ 2.363450] <1>-(1)[1:swapper/0][<c03c36d0>] (driver_register) from [<c03c4e54>] (__platform_driver_register+0x5c/0x64) [ 2.364789] r5:00000000 r4:c12f5318 [ 2.365244] <1>-(1)[1:swapper/0][<c03c4df8>] (__platform_driver_register) from [<c0f39ee0>] (battery_meter_init+0x74/0xc4) [ 2.366627] <1>-(1)[1:swapper/0][<c0f39e6c>] (battery_meter_init) from [<c0f00e74>] (do_one_initcall+0x140/0x200) [ 2.367901] r5:c0f62538 r4:c0f62538 [ 2.368354] <1>-(1)[1:swapper/0][<c0f00d34>] (do_one_initcall) from [<c0f010a0>] (kernel_init_freeable+0x16c/0x20c) [ 2.369649] r10:c0f62d6c r9:00000141 r8:c0f00600 r7:c10efb80 r6:c0f62d60 r5:c0f6c278 [ 2.370625] r4:00000006 [ 2.370951] <1>-(1)[1:swapper/0][<c0f00f34>] (kernel_init_freeable) from [<c0a8bd78>] (kernel_init+0x10/0x100)

从上面 backtrace看整个代码调用流程就很清晰了,因此这种方式实际上是很是实用而重要的debug手段!安全

下面进入源码分析,验证上面的过程:数据结构

static int __init battery_meter_init(void)
{
	int ret;

// 注册到平台设备
	ret = platform_device_register(&battery_meter_device);
	if (ret) {
		return ret;
	}
	
// 注册到平台驱动
	ret = platform_driver_register(&battery_meter_driver);
	if (ret) {
		return ret;
	}

	return 0;
}

继续看平台驱动注册:架构

// 发现这个实际上是一个宏定义
#define platform_driver_register(drv) \
	__platform_driver_register(drv, THIS_MODULE)
	
	
int __platform_driver_register(struct platform_driver *drv,
				struct module *owner)
{
// 标记为:THIS_MODULE
	drv->driver.owner = owner;
// 总线类型
	drv->driver.bus = &platform_bus_type;

// drv->driver 赋值,下面的 platform_drv_probe 其实就是最终须要call的接口!
	if (drv->probe)
		drv->driver.probe = platform_drv_probe;
	if (drv->remove)
		drv->driver.remove = platform_drv_remove;
	if (drv->shutdown)
		drv->driver.shutdown = platform_drv_shutdown;

	return driver_register(&drv->driver);
}
	
int driver_register(struct device_driver *drv)
{
	int ret;
	struct device_driver *other;

// 异常判断,若是依次直接kernel panic.
	BUG_ON(!drv->bus->p);

	if ((drv->bus->probe && drv->probe) ||
	    (drv->bus->remove && drv->remove) ||
	    (drv->bus->shutdown && drv->shutdown))
		printk(KERN_WARNING "Driver '%s' needs updating - please use "
			"bus_type methods\n", drv->name);

/* 首先去drivers_kset的对象list中查是否存在相同名字的内核对象,若是存在就 输出错误提示信息。 */
	other = driver_find(drv->name, drv->bus);
	if (other) {
	// 这个log信息能够用于驱动debug检查。
		printk(KERN_ERR "Error: Driver '%s' is already registered, "
			"aborting...\n", drv->name);
		return -EBUSY;
	}

// 若是是新的driver则加入到总线中.
	ret = bus_add_driver(drv);
...
}
int bus_add_driver(struct device_driver *drv)
{
	struct bus_type *bus;
	struct driver_private *priv;
	int error = 0;

	bus = bus_get(drv->bus);
	if (!bus)
		return -EINVAL;

// debug辅助信息
	pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);

// 使用kmalloc申请内存,初始化为0,属于线性映射内存
	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
	if (!priv) {
		error = -ENOMEM;
		goto out_put_bus;
	}
	
// 注册到driver内核对象链表.
	klist_init(&priv->klist_devices, NULL, NULL);
	priv->driver = drv;
	drv->p = priv;
	priv->kobj.kset = bus->p->drivers_kset;
	error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
				     "%s", drv->name);
// 若是注册失败,直接退出.
	if (error)
		goto out_unregister;
		
// 添加到klist_drivers list 中管理.
	klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
	if (drv->bus->p->drivers_autoprobe) {
	// attach.
		error = driver_attach(drv);
		if (error)
			goto out_unregister;
	}
...
}
int driver_attach(struct device_driver *drv) {
/* 经过drv找到dev而后传入__driver_attach执行,这里使用了内核中最 广泛的获取容器数据结构实例的指针操做. */
	return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
static int __driver_attach(struct device *dev, void *data)
{
	struct device_driver *drv = data;

// 若是dev跟drv不匹配,返回.
	if (!driver_match_device(drv, dev))
		return 0;

// 若是存在父设备,则须要拿到两把锁
	if (dev->parent)	/* Needed for USB */
		device_lock(dev->parent);
	device_lock(dev);
	
// 匹配到,执行driver的probe
	if (!dev->driver)
		driver_probe_device(drv, dev);
		
// 执行完释放锁.
	device_unlock(dev);
	if (dev->parent)
		device_unlock(dev->parent);

	return 0;
}

这里就是关键地方了!app

int driver_probe_device(struct device_driver *drv, struct device *dev)
{
	int ret = 0;

// 若是没注册dev返回错误
	if (!device_is_registered(dev))
		return -ENODEV;

// 有用的debug信息
	pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
		 drv->bus->name, __func__, dev_name(dev), drv->name);

// pm runtime须要保证不挂起.
	pm_runtime_barrier(dev);
	
// 看名字就知道这里就是要干最终的call驱动的probe了?
	ret = really_probe(dev, drv);
	pm_request_idle(dev);

	return ret;
}
static int really_probe(struct device *dev, struct device_driver *drv)
{
	int ret = 0;
	int local_trigger_count = atomic_read(&deferred_trigger_count);

// 原子计数器,保证原子操做
	atomic_inc(&probe_count);
	
// 有用的debug信息
	pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
		 drv->bus->name, __func__, drv->name, dev_name(dev));
	WARN_ON(!list_empty(&dev->devres_head));

	dev->driver = drv;

...

// 执行probe:
	if (dev->bus->probe) {
		TIME_LOG_START();
		ret = dev->bus->probe(dev);
		TIME_LOG_END();
		bootprof_probe(ts, dev, drv, (unsigned long)dev->bus->probe);
		if (ret)
			goto probe_failed;
	} else if (drv->probe) {
		TIME_LOG_START();
		
	/* 这里执行最新的probe,这个drv->probe是否是好像哪里见过??? 没错,这里执行的就是开头驱动注册时候传入的 platform_drv_probe ! 兜兜转转一大圈又绕回去了! */
		ret = drv->probe(dev);
        ...
	}
...

// 若是执行失败,移除dev,释放资源.
probe_failed:
	devres_release_all(dev);
	driver_sysfs_remove(dev);
	dev->driver = NULL;
	dev_set_drvdata(dev, NULL);

....
}

继续分析:
static int platform_drv_probe(struct device *_dev)
{
// 又见 container_of 机制!内核中链表和指针的运用很是之巧妙而高效.
	struct platform_driver *drv = to_platform_driver(_dev->driver);
	struct platform_device *dev = to_platform_device(_dev);
	int ret;

...
	ret = dev_pm_domain_attach(_dev, true);
	if (ret != -EPROBE_DEFER) {
	
	    // 真正执行驱动的probe入口!!!
		ret = drv->probe(dev);
		if (ret)
			dev_pm_domain_detach(_dev, true);
	}

...
}

驱动注册基本流程:
1、 经过平台驱动通用接口注册platform_drv_xxcallback
2、 使用driver_find查询当前drv是否已添加到总线集合drivers_kset中, 若是已存在则返回退出;
3、 建立驱动内核对象, 并添加到内核对象层级中, 将knode_bus连接总线链表bus->p->klist_drivers .
4
、 建立驱动属性组;
5、 注册完成, kobject_uevent发送KOBJ_ADD通知.

less