本文介绍的内容是基于Linux3.1源码,并参考了很多网上找来的资料

Linux内核的启动的流程如下:

start_kernel->rest_init->kernel_init->do_basic_setup->driver_init

由driver_init函数完成设备驱动子系统的初始化,这里重点分析driver_init函数,该函数通过调用一系列的初始化函数主要完成了设备驱动子系统的一个整体框架;

 /**
* driver_init - initialize driver model.
*
* Call the driver model init functions to initialize their
* subsystems. Called early from init/main.c.
*/
void __init driver_init(void)
{
/* These are the core pieces */
devtmpfs_init();
devices_init();
buses_init();
classes_init();
firmware_init();
hypervisor_init(); /* These are also core pieces, but must come after the
* core core pieces.
*/
platform_bus_init();
system_bus_init();
cpu_dev_init();
memory_dev_init();
}

这个函数完成驱动子系统的构建,实现了Linux设备驱动的一个整体框架,接下来就可以真正的添加设备了,接下来逐个介绍这些函数的作用。

1、devtmpfs_int函数:

该函数注册一个名为devtmpfs的文件系统dev_fs_type,这部分和设备驱动子系统的建立关系并不大,这里不多做介绍
 /*
* Create devtmpfs instance, driver-core devices will add their device
* nodes here.
*/
int __init devtmpfs_init(void)
{
int err = register_filesystem(&dev_fs_type);
if (err) {
printk(KERN_ERR "devtmpfs: unable to register devtmpfs "
"type %i\n", err);
return err;
} thread = kthread_run(devtmpfsd, &err, "kdevtmpfs");
if (!IS_ERR(thread)) {
wait_for_completion(&setup_done);
} else {
err = PTR_ERR(thread);
thread = NULL;
} if (err) {
printk(KERN_ERR "devtmpfs: unable to create devtmpfs %i\n", err);
unregister_filesystem(&dev_fs_type);
return err;
} printk(KERN_INFO "devtmpfs: initialized\n");
return ;
}

2、devices_init函数:创建devices相关的设备模型

 int __init devices_init(void)
{
devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
if (!devices_kset)
return -ENOMEM;
dev_kobj = kobject_create_and_add("dev", NULL);
if (!dev_kobj)
goto dev_kobj_err;
sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
if (!sysfs_dev_block_kobj)
goto block_kobj_err;
sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
if (!sysfs_dev_char_kobj)
goto char_kobj_err; return ; char_kobj_err:
kobject_put(sysfs_dev_block_kobj);
block_kobj_err:
kobject_put(dev_kobj);
dev_kobj_err:
kset_unregister(devices_kset);
return -ENOMEM;
}

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设备模型总线部分的顶级节点

 int __init buses_init(void)
{
bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
if (!bus_kset)
return -ENOMEM;
return ;
}

buses_init函数建立了/sys/bus这个顶级容器节点,该节点是Linux内核中所有总线类型的父节点,

bus_kset是drivers/base/bus.c中定义的static变量。

4、classes_init函数:建立Linux设备模型类部分的顶级容器节点

 int __init classes_init(void)
{
class_kset = kset_create_and_add("class", NULL, NULL);
if (!class_kset)
return -ENOMEM;
return ;
}

建立了/sys/class这个顶级容器节点,该节点是Linux内核中所有class类型的父节点,class_kset是drivers/base/class.c中定义的static变量

5、firmware_init函数:建立Linux设备模型中firmware部分的顶级节点

 int __init firmware_init(void)
{
firmware_kobj = kobject_create_and_add("firmware", NULL);
if (!firmware_kobj)
return -ENOMEM;
return ;
}

建立了/sys/firmware这个顶级kobj节点,firmware_kobj是在drivers/base/firmware.c定义的全局变量

6、hypervisor_init函数:建立hypervisor_kobj的顶级容器节点

 int __init hypervisor_init(void)
{
hypervisor_kobj = kobject_create_and_add("hypervisor", NULL);
if (!hypervisor_kobj)
return -ENOMEM;
return ;
}

建立了/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改写了

 int __init platform_bus_init(void)
{
int error; early_platform_cleanup(); error = device_register(&platform_bus);
if (error)
return error;
error = bus_register(&platform_bus_type);
if (error)
device_unregister(&platform_bus);
return error;
}

platform_bus_init函数中引入两个变量platform_bus,platform_bus_type,均为在drivers/base/platform.c中定义的全局变量,如下:

 struct device platform_bus = {
.init_name = "platform",
}; struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};

  该函数先调用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容器节点

 int __init system_bus_init(void)
{
system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj);
if (!system_kset)
return -ENOMEM;
return ;
}

通过kset_create_and_add函数建立了/sys/devices/system这个容器节点,system_kset是drivers/base/sys.c中定义的static变量。

这个节点中主要是一些和cpu、中断控制器、时钟之类的设备

9、cpu_dev_init函数:建立一个名为”cpu”的类

 int __init cpu_dev_init(void)
{
int err; err = sysdev_class_register(&cpu_sysdev_class);
#if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT)
if (!err)
err = sched_create_sysfs_power_savings_entries(&cpu_sysdev_class);
#endif return err;
}

调用sysdev_class_register函数注册cpu_sysdev_class这个类,cpu_sysdev_class是在drivers/base/cpu.c中定义的全局变量,如下:

 struct sysdev_class cpu_sysdev_class = {
.name = "cpu",
.attrs = cpu_sysdev_class_attrs,
};

  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中的接口

 /*
* Initialize the sysfs support for memory devices...
*/
int __init memory_dev_init(void)
{
unsigned int i;
int ret;
int err;
unsigned long block_sz; memory_sysdev_class.kset.uevent_ops = &memory_uevent_ops;
ret = sysdev_class_register(&memory_sysdev_class);
if (ret)
goto out; block_sz = get_memory_block_size();
sections_per_block = block_sz / MIN_MEMORY_BLOCK_SIZE; /*
* Create entries for memory sections that were found
* during boot and have been initialized
*/
for (i = ; i < NR_MEM_SECTIONS; i++) {
if (!present_section_nr(i))
continue;
err = add_memory_section(, __nr_to_section(i), MEM_ONLINE,
BOOT);
if (!ret)
ret = err;
} err = memory_probe_init();
if (!ret)
ret = err;
err = memory_fail_init();
if (!ret)
ret = err;
err = block_size_init();
if (!ret)
ret = err;
out:
if (ret)
printk(KERN_ERR "%s() failed: %d\n", __func__, ret);
return ret;
}

这边与cpu_dev_init函数类似,先调用sysdev_class_register函数注册memory_sysdev_class这个类,cpu_sysdev_class是在drivers/base/memory.c中定义的全局变量,如下:

 #define MEMORY_CLASS_NAME       "memory"

 static struct sysdev_class memory_sysdev_class = {
.name = MEMORY_CLASS_NAME,
};

因此与cpu_dev_init函数类似,将memory_sysdev_class的父节点设置为前面system_bus_init时创建的变量system_kset,因此会在/sys/devices/system/节点下建立一个名为”memory”的子节点/sys/devices/system/memory/;该节点包含了内存相关的属性,如块大小等

 

Linux设备子系统初始化的更多相关文章

  1. linux文件系统 - 初始化(二)

    加载initrd(上) 一.目的 本文主要讲述linux3.10文件系统初始化过程的第二阶段:加载initrd. initrd是一个临时文件系统,由bootload负责加载到内存中,里面包含了基本的可 ...

  2. linux文件系统 - 初始化(一)

    术语表: struct task:进程 struct mnt_namespace:命名空间 struct mount:挂载点 struct vfsmount:挂载项 struct file:文件 st ...

  3. linux文件系统初始化过程(5)---加载initrd(下)

    一.目的 linux把文件分为常规文件.目录文件.软链接文件.硬链接文件.特殊文件(设备文件.管道文件.socket文件等)几种类型,分别对应不同的新建函数sys_open().sys_mkdir() ...

  4. linux文件系统初始化过程(2)---挂载rootfs文件系统

    一.目的 本文主要讲述linux3.10文件系统初始化过程的第一阶段:挂载rootfs文件系统. rootfs是基于内存的文件系统,所有操作都在内存中完成:也没有实际的存储设备,所以不需要设备驱动程序 ...

  5. linux文件系统初始化过程(1)---概述

    术语表: struct task:进程 struct mnt_namespace:命名空间 struct mount:挂载点 struct vfsmount:挂载项 struct file:文件 st ...

  6. linux系统初始化——文件系统初始化步骤

    linux文件系统初始化步骤 System V init启动过程 概括地讲,Linux/Unix系统一般有两种不同的初始化启动方式. 1) BSD system init 2) System V in ...

  7. Linux系统启动初始化

    文章目录 一.BIOS 加载启动引导程序 二.MBR 主引导扇区 三.GRUB引导内核 3.1运行 boot.img 3.2加载 core.img 3.3切换到保护模式 3.4kernel.img 引 ...

  8. Linux启动初始化配置文件

    Linux启动初始化配置文件(1)/etc/profile 登录时,会执行. 全局(公有)配置,不管是哪个用户,登录时都会读取该文件. (2)/ect/bashrc Ubuntu没有此文件,与之对应的 ...

  9. linux环境初始化 用户问题

    linux 初始化系统配置(centos6) (2013-04-03 13:19:15) 转载▼   分类: linux 这篇博文是从别处转来的,原文地址http://zhoualine.iteye. ...

随机推荐

  1. CLDAPReflectionDDoS(CLDAP反射放大攻击)

    CLDAP Reflection DDoS 0x01 LDAP: 全称为Lightweight Directory Access Protocol,即轻量目录访问协议,基于X.500标准: 目录服务就 ...

  2. Jmeter系列(2)- Jmeter工具介绍、Jmeter安装目录介绍、Jmeter面板介绍

    如果你想从头学习Jmeter,可以看看这个系列的文章哦 https://www.cnblogs.com/poloyy/category/1746599.html Jmeter支持哪些测试场景? Jme ...

  3. TensorFlow keras读取图片

    from tensorflow.python.keras.preprocessing.image import load_img,img_to_array def main(): #tagert_si ...

  4. 基于layui,Jquery 表格动态编辑 设置 编辑值为 int 或者 double 类型及默认值

    首先先推荐大家在看这篇笔记时,阅读过我写的这篇 Layui表格编辑[不依赖Layui的动态table加载] 阅读过上面那篇笔记之后呢,才能更好的理解我现在所要说的这个东西 接下来废话不多说,上代码. ...

  5. deepin15.11小毛病解决

    目录 边缘花屏问题 QQ`Tim头像问题 ssh卡死问题 看直播卡 边缘花屏问题 sudo apt install systemsettings 打开kde系统设置 打开显示与设置,修改如图下,基本上 ...

  6. PHP把PNG图片转化为JPG时透明背景变黑色

    $type = exif_imagetype($srcimg); switch($type) { case 1: $simg = imagecreatefromgif($srcimg); break; ...

  7. SQLI-LABS学习笔记(四)

    第十六关   和之前的关卡一样,修改闭合,无意义的关卡   ")闭合即可   第十七关   这题从源码上看发现     这里进行了两次查询   先查询了用户名是否存在   再查询密码是否匹配 ...

  8. 定期清理nohup.out

    事件背景 服务应用weblogic通过nohup启动. nohup的使用全部都在weblogic域中的bin目录下 但是没有做定期nohup.out的清理 导致核心服务的日志过大,在出现问题时候难以打 ...

  9. Python 如何写 Ubuntu syslog

    address='/dev/log' 是关键 import logging from logging.handlers import SysLogHandler logger = logging.ge ...

  10. 对于WebP格式入门解读

    因为项目中需要用到大量动画效果,前期尝试过几种方案,比如GIF.帧动画.lottie.SVGA等格式的动画渲染方案,发现都存在各式各样的问题.比如: 1,GIF格式.5秒的动画,一张图大小可能就会达到 ...