Linux设备子系统初始化
本文介绍的内容是基于Linux3.1源码,并参考了很多网上找来的资料
Linux内核的启动的流程如下:
start_kernel->rest_init->kernel_init->do_basic_setup->driver_init
由driver_init函数完成设备驱动子系统的初始化,这里重点分析driver_init函数,该函数通过调用一系列的初始化函数主要完成了设备驱动子系统的一个整体框架;
1 /** 2 * driver_init - initialize driver model. 3 * 4 * Call the driver model init functions to initialize their 5 * subsystems. Called early from init/main.c. 6 */ 7 void __init driver_init(void) 8 { 9 /* These are the core pieces */ 10 devtmpfs_init(); 11 devices_init(); 12 buses_init(); 13 classes_init(); 14 firmware_init(); 15 hypervisor_init(); 16 17 /* These are also core pieces, but must come after the 18 * core core pieces. 19 */ 20 platform_bus_init(); 21 system_bus_init(); 22 cpu_dev_init(); 23 memory_dev_init(); 24 }
这个函数完成驱动子系统的构建,实现了Linux设备驱动的一个整体框架,接下来就可以真正的添加设备了,接下来逐个介绍这些函数的作用。
1、devtmpfs_int函数:
该函数注册一个名为devtmpfs的文件系统dev_fs_type,这部分和设备驱动子系统的建立关系并不大,这里不多做介绍
1 /* 2 * Create devtmpfs instance, driver-core devices will add their device 3 * nodes here. 4 */ 5 int __init devtmpfs_init(void) 6 { 7 int err = register_filesystem(&dev_fs_type); 8 if (err) { 9 printk(KERN_ERR "devtmpfs: unable to register devtmpfs " 10 "type %i\n", err); 11 return err; 12 } 13 14 thread = kthread_run(devtmpfsd, &err, "kdevtmpfs"); 15 if (!IS_ERR(thread)) { 16 wait_for_completion(&setup_done); 17 } else { 18 err = PTR_ERR(thread); 19 thread = NULL; 20 } 21 22 if (err) { 23 printk(KERN_ERR "devtmpfs: unable to create devtmpfs %i\n", err); 24 unregister_filesystem(&dev_fs_type); 25 return err; 26 } 27 28 printk(KERN_INFO "devtmpfs: initialized\n"); 29 return 0; 30 }
2、devices_init函数:创建devices相关的设备模型
1 int __init devices_init(void) 2 { 3 devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL); 4 if (!devices_kset) 5 return -ENOMEM; 6 dev_kobj = kobject_create_and_add("dev", NULL); 7 if (!dev_kobj) 8 goto dev_kobj_err; 9 sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj); 10 if (!sysfs_dev_block_kobj) 11 goto block_kobj_err; 12 sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj); 13 if (!sysfs_dev_char_kobj) 14 goto char_kobj_err; 15 16 return 0; 17 18 char_kobj_err: 19 kobject_put(sysfs_dev_block_kobj); 20 block_kobj_err: 21 kobject_put(dev_kobj); 22 dev_kobj_err: 23 kset_unregister(devices_kset); 24 return -ENOMEM; 25 }
devices_init函数建立/sys/devices、/sys/dev这两个顶级容器节点和/sys/dev/block、/sys/dev/char这两个二级节点
devices_kset是在/driver/base/core.c中定义的全局变量,对应于sysfs的/sys/devices节点
dev_kobj是在/driver/base/core.c中定义的static变量, 对应于sysfs的/sys/dev节点
sysfs_dev_block_kobj是在/driver/base/core.c中定义的全局变量,是dev_kobj的子节点,对应于sysfs的/sys/dev/block,该节点是所有block设备的父节点
sysfs_dev_char_kobj是在/driver/base/core.c中定义的全局变量,是dev_kobj的子节点,对应于sysfs的/sys/dev/char,该节点是所有char设备的父节点
3、buses_init函数:建立Linux设备模型总线部分的顶级节点
1 int __init buses_init(void) 2 { 3 bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL); 4 if (!bus_kset) 5 return -ENOMEM; 6 return 0; 7 }
buses_init函数建立了/sys/bus这个顶级容器节点,该节点是Linux内核中所有总线类型的父节点,
bus_kset是drivers/base/bus.c中定义的static变量。
4、classes_init函数:建立Linux设备模型类部分的顶级容器节点
1 int __init classes_init(void) 2 { 3 class_kset = kset_create_and_add("class", NULL, NULL); 4 if (!class_kset) 5 return -ENOMEM; 6 return 0; 7 }
建立了/sys/class这个顶级容器节点,该节点是Linux内核中所有class类型的父节点,class_kset是drivers/base/class.c中定义的static变量
5、firmware_init函数:建立Linux设备模型中firmware部分的顶级节点
1 int __init firmware_init(void) 2 { 3 firmware_kobj = kobject_create_and_add("firmware", NULL); 4 if (!firmware_kobj) 5 return -ENOMEM; 6 return 0; 7 }
建立了/sys/firmware这个顶级kobj节点,firmware_kobj是在drivers/base/firmware.c定义的全局变量
6、hypervisor_init函数:建立hypervisor_kobj的顶级容器节点
1 int __init hypervisor_init(void) 2 { 3 hypervisor_kobj = kobject_create_and_add("hypervisor", NULL); 4 if (!hypervisor_kobj) 5 return -ENOMEM; 6 return 0; 7 }
建立了/sys/hypervisor这个顶级节点,hypervisor_kobj是在 drivers/base/hypervisor.c中定义的全局变量
--------------------------------------------------------------------我是分割线-----------------------------------------------------------------------
前面几个函数执行完成后基本已经建立起Linux设备模型的框架了,接下来几个函数都是在前面框架中的扩展
7、platform_bus_init函数:
初始化Linux平台总线,平台总线(platform_bus_type)是在2.6 kernel中引入的一种虚拟总线,主要用来管理CPU的片上资源,具有较好的可移植性能,因此在2.6及以后的kernel中,很多驱动都已经用platform改写了
1 int __init platform_bus_init(void) 2 { 3 int error; 4 5 early_platform_cleanup(); 6 7 error = device_register(&platform_bus); 8 if (error) 9 return error; 10 error = bus_register(&platform_bus_type); 11 if (error) 12 device_unregister(&platform_bus); 13 return error; 14 }
platform_bus_init函数中引入两个变量platform_bus,platform_bus_type,均为在drivers/base/platform.c中定义的全局变量,如下:
1 struct device platform_bus = { 2 .init_name = "platform", 3 }; 4 5 struct bus_type platform_bus_type = { 6 .name = "platform", 7 .dev_attrs = platform_dev_attrs, 8 .match = platform_match, 9 .uevent = platform_uevent, 10 .pm = &platform_dev_pm_ops, 11 };
该函数先调用device_register函数注册platform_bus这个设备,这会在/sys/devices/节点下创建platform节点/sys/devices/platform,此设备节点是所有platform设备的父节点,即所有platform_device设备都会在/sys/devices/platform下创建子节点
然后调用bus_register函数注册platform_bus_type这个总线类型,这会在/sys/bus目录下创建一个platform节点,这个节点是所有platform设备和驱动的总线类型,即所有platform设备和驱动都会挂载到这个总线上;
8、system_bus_init函数:在/sys/devices/下建立system容器节点
1 int __init system_bus_init(void) 2 { 3 system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj); 4 if (!system_kset) 5 return -ENOMEM; 6 return 0; 7 }
通过kset_create_and_add函数建立了/sys/devices/system这个容器节点,system_kset是drivers/base/sys.c中定义的static变量。
这个节点中主要是一些和cpu、中断控制器、时钟之类的设备
9、cpu_dev_init函数:建立一个名为”cpu”的类
1 int __init cpu_dev_init(void) 2 { 3 int err; 4 5 err = sysdev_class_register(&cpu_sysdev_class); 6 #if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT) 7 if (!err) 8 err = sched_create_sysfs_power_savings_entries(&cpu_sysdev_class); 9 #endif 10 11 return err; 12 }
调用sysdev_class_register函数注册cpu_sysdev_class这个类,cpu_sysdev_class是在drivers/base/cpu.c中定义的全局变量,如下:
1 struct sysdev_class cpu_sysdev_class = { 2 .name = "cpu", 3 .attrs = cpu_sysdev_class_attrs, 4 };
sysdev_class_register函数中将父节点设置为前面system_bus_init时创建的变量system_kset,即/sys/devices/system/节点;因此cpu_dev_init函数会在/sys/devices/system/节点下建立一个名为cpu的子节点/sys/devices/system/cpu/,该节点包含CPU相关的属性
10、memory_dev_init函数:建立memory设备在sysfs中的接口
1 /* 2 * Initialize the sysfs support for memory devices... 3 */ 4 int __init memory_dev_init(void) 5 { 6 unsigned int i; 7 int ret; 8 int err; 9 unsigned long block_sz; 10 11 memory_sysdev_class.kset.uevent_ops = &memory_uevent_ops; 12 ret = sysdev_class_register(&memory_sysdev_class); 13 if (ret) 14 goto out; 15 16 block_sz = get_memory_block_size(); 17 sections_per_block = block_sz / MIN_MEMORY_BLOCK_SIZE; 18 19 /* 20 * Create entries for memory sections that were found 21 * during boot and have been initialized 22 */ 23 for (i = 0; i < NR_MEM_SECTIONS; i++) { 24 if (!present_section_nr(i)) 25 continue; 26 err = add_memory_section(0, __nr_to_section(i), MEM_ONLINE, 27 BOOT); 28 if (!ret) 29 ret = err; 30 } 31 32 err = memory_probe_init(); 33 if (!ret) 34 ret = err; 35 err = memory_fail_init(); 36 if (!ret) 37 ret = err; 38 err = block_size_init(); 39 if (!ret) 40 ret = err; 41 out: 42 if (ret) 43 printk(KERN_ERR "%s() failed: %d\n", __func__, ret); 44 return ret; 45 }
这边与cpu_dev_init函数类似,先调用sysdev_class_register函数注册memory_sysdev_class这个类,cpu_sysdev_class是在drivers/base/memory.c中定义的全局变量,如下:
1 #define MEMORY_CLASS_NAME "memory" 2 3 static struct sysdev_class memory_sysdev_class = { 4 .name = MEMORY_CLASS_NAME, 5 };
因此与cpu_dev_init函数类似,将memory_sysdev_class的父节点设置为前面system_bus_init时创建的变量system_kset,因此会在/sys/devices/system/节点下建立一个名为”memory”的子节点/sys/devices/system/memory/;该节点包含了内存相关的属性,如块大小等