Linux驱动都需要有一个设备文件,否则应用程序将无法与驱动程序交互,下面良许教程网为大家详细讲解一下Linux Driver。
设备分类
-
字符设备
-
块设备
-
网络设备
驱动在kernel中的位置
linux 系统设计将设备驱动纳入了文件系统,
更为具体的分层调用结构如下所示:
Linux Device Model
usb 设备的驱动设备模型:
设备文件的节点记录的信息:
-
文件类型
-
主设备号
-
次设备号 主设备号和类型决定了驱动的类型及其界面,而次设备号则说明是同类设备中的第几个设备。
devfs 文件系统
在linux 2.4 引入,主要优点:
可以通过程序在设备初始化时在/dev目录下创建设备文件,卸载设备时将它 删除。
-
设备驱动程序可以指定设备名、所有者和权限位,用户空间程序仍可以修改 所有者和权限位。
-
不再需要为设备驱动程序分配主设备号以及处理次设备号,在程序中可以直 接给 register_chrdev()传递 0 主设备号以动态获得可用的主设备号,并在 devfs_register()中指定次设备号。
udev 设备文件系统
linux 2.6开始抛弃了devfs,引入了udev 文件系统,基于以下几点:
devfs所做的工作被确信可以在用户态来完成。
一些bug相当长的时间内未被修复。
devfs的维护者和作者停止了对代码的维护工作。
udev 与devfs的区别在于:
devfs 在设备打开时自动加载驱动程序 udev在发现设备时自动加载驱动程序
sysfs 文件系统
udev工作在用户态,利用设备加入或移除时内核所发送的热插拔事件来进行工作,同时hotplug事件,设备的详细信息会由内核输出到位于/sys 的sysfs文件系统。
udev 利用sysfs中的信息来创建设备文件节点等工作
sysfs被看成是与proc、devfs和devpty同 类别的文件系统,该文件系统是一个虚拟的文件系统,它可以产生一个包括所有系统 硬件的层级视图,与提供进程和状态信息的 proc 文件系统十分类似
tree /sys/dev/ ├── block │ ├── 7:0 -> ../../devices/virtual/block/loop0 │ ├── 7:1 -> ../../devices/virtual/block/loop1 │ ├── 7:2 -> ../../devices/virtual/block/loop2 │ ├── 7:3 -> ../../devices/virtual/block/loop3 │ ├── 7:4 -> ../../devices/virtual/block/loop4 │ ├── 7:5 -> ../../devices/virtual/block/loop5 │ ├── 7:6 -> ../../devices/virtual/block/loop6 │ ├── 7:7 -> ../../devices/virtual/block/loop7 │ ├── 8:0 -> ../../devices/pci0000:00/0000:00:17.0/ata1/host0/target0:0:0/0:0:0:0/block/sda │ ├── 8:1 -> ../../devices/pci0000:00/0000:00:17.0/ata1/host0/target0:0:0/0:0:0:0/block/sda/sda1 │ ├── 8:16 -> ../../devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8.2/1-8.2:1.0/host4/target4:0:0/4:0:0:0/block/sdb │ ├── 8:2 -> ../../devices/pci0000:00/0000:00:17.0/ata1/host0/target0:0:0/0:0:0:0/block/sda/sda2 │ ├── 8:32 -> ../../devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8.2/1-8.2:1.0/host4/target4:0:0/4:0:0:1/block/sdc │ ├── 8:48 -> ../../devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8.2/1-8.2:1.0/host4/target4:0:0/4:0:0:2/block/sdd │ ├── 8:5 -> ../../devices/pci0000:00/0000:00:17.0/ata1/host0/target0:0:0/0:0:0:0/block/sda/sda5 │ └── 8:6 -> ../../devices/pci0000:00/0000:00:17.0/ata1/host0/target0:0:0/0:0:0:0/block/sda/sda6 └── char ├── 10:1 -> ../../devices/virtual/misc/psaux ├── 10:183 -> ../../devices/virtual/misc/hw_random ├── 10:184 -> ../../devices/virtual/misc/microcode ├── 10:200 -> ../../devices/virtual/misc/tun ├── 10:223 -> ../../devices/virtual/misc/uinput ├── 10:227 -> ../../devices/virtual/misc/mcelog ├── 10:228 -> ../../devices/virtual/misc/hpet ├── 10:229 -> ../../devices/virtual/misc/fuse ├── 10:231 -> ../../devices/virtual/misc/snapshot ├── 10:232 -> ../../devices/virtual/misc/kvm 12345678910111213141516171819202122232425262728293031
/sys 目录下展示了所有系统中的设备层级目录,其关系大致如下
kobject
linux3.4
struct kobject { const char *name; struct list_head entry; //used for mount to kset list struct kobject *parent; struct kset *kset; struct kobj_type *ktype; //指向对象类型描述附指针 struct sysfs_dirent *sd; // struct kref kref; // 引用计数 unsigned int state_initialized:1; unsigned int state_in_sysfs:1; unsigned int state_add_uevent_sent:1; unsigned int state_remove_uevent_sent:1; unsigned int uevent_suppress:1; }; 123456789101112131415
其中 sysfs_dirent,
/* * sysfs_dirent - the building block of sysfs hierarchy. Each and * every sysfs node is represented by single sysfs_dirent. * * As long as s_count reference is held, the sysfs_dirent itself is * accessible. Dereferencing s_elem or any other outer entity * requires s_active reference. */ struct sysfs_dirent { atomic_t s_count; atomic_t s_active; #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map; #endif struct sysfs_dirent *s_parent; const char *s_name; struct rb_node s_rb; union { struct completion *completion; struct sysfs_dirent *removed_list; } u; const void *s_ns; /* namespace tag */ unsigned int s_hash; /* ns + name hash */ union { struct sysfs_elem_dir s_dir; struct sysfs_elem_symlink s_symlink; struct sysfs_elem_attr s_attr; struct sysfs_elem_bin_attr s_bin_attr; }; unsigned short s_flags; umode_t s_mode; unsigned int s_ino; struct sysfs_inode_attrs *s_iattr; }; 123456789101112131415161718192021222324252627282930313233343536373839
kset
struct kset { struct list_head list; //double list head spinlock_t list_lock; struct kobject kobj; //嵌入的kobject const struct kset_uevent_ops *uevent_ops; //事件操作集 }; 123456
其中 kset_uevent_ops
定义了kset的对所包含的kobject 可以执行的操作,包括事件过滤和导出环境变量操作。
struct kset_uevent_ops { int (* const filter)(struct kset *kset, struct kobject *kobj); //事件过滤 const char *(* const name)(struct kset *kset, struct kobject *kobj); int (* const uevent)(struct kset *kset, struct kobject *kobj, struct kobj_uevent_env *env); // 环境变量导出 }; 1234567
device
device 数据结构用于描述设备相关的信息和设备之间的层次关系,设备与总线与驱动之间的关系。
struct device { struct device *parent; struct device_private *p; struct kobject kobj; const char *init_name; /* initial name of the device */ const struct device_type *type; struct mutex mutex; /* mutex to synchronize calls to * its driver. */ struct bus_type *bus; /* type of bus device is on */ struct device_driver *driver; /* which driver has allocated this device */ void *platform_data; /* Platform specific data, device core doesn't touch it */ struct dev_pm_info power; struct dev_pm_domain *pm_domain; #ifdef CONFIG_NUMA int numa_node; /* NUMA node this device is close to */ #endif u64 *dma_mask; /* dma mask (if dma'able device) */ u64 coherent_dma_mask;/* Like dma_mask, but for alloc_coherent mappings as not all hardware supports 64 bit addresses for consistent allocations such descriptors. */ struct device_dma_parameters *dma_parms; struct list_head dma_pools; /* dma pools (if dma'ble) */ struct dma_coherent_mem *dma_mem; /* internal for coherent mem override */ #ifdef CONFIG_CMA struct cma *cma_area; /* contiguous memory area for dma allocations */ #endif /* arch specific additions */ struct dev_archdata archdata; struct device_node *of_node; /* associated device tree node */ dev_t devt; /* dev_t, creates the sysfs "dev" */ u32 id; /* device instance */ spinlock_t devres_lock; struct list_head devres_head; struct klist_node knode_class; struct class *class; const struct attribute_group **groups; /* optional groups */ void (*release)(struct device *dev); }; 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
操作device的相关函数:
device_register(): /sys/devices 下创建相关目录 device_unregister(): 移除相关目录 get_device: 增加引用计数 put_device: 减小引用计数 1234
device_driver
struct device_driver { const char *name; struct bus_type *bus; struct module *owner; const char *mod_name; /* used for built-in modules */ bool suppress_bind_attrs; /* disables bind/unbind via sysfs */ const struct of_device_id *of_match_table; int (*probe) (struct device *dev); int (*remove) (struct device *dev); void (*shutdown) (struct device *dev); int (*suspend) (struct device *dev, pm_message_t state); int (*resume) (struct device *dev); const struct attribute_group **groups; const struct dev_pm_ops *pm; struct driver_private *p; }; 12345678910111213141516171819202122
其中
struct bus_type { const char *name; const char *dev_name; struct device *dev_root; struct bus_attribute *bus_attrs; struct device_attribute *dev_attrs; struct driver_attribute *drv_attrs; int (*match)(struct device *dev, struct device_driver *drv); int (*uevent)(struct device *dev, struct kobj_uevent_env *env); int (*probe)(struct device *dev); int (*remove)(struct device *dev); void (*shutdown)(struct device *dev); int (*suspend)(struct device *dev, pm_message_t state); int (*resume)(struct device *dev); const struct dev_pm_ops *pm; struct iommu_ops *iommu_ops; struct subsys_private *p; }; 1234567891011121314151617181920212223
Kernel modules
模块安装与卸载
并不是所有的module都是在编译内核时加入的,主要有以下考虑:
-
所有模块默认编译进内核,将使得内核臃肿
-
使得内核不易进行扩展
linux提供可动态安装模块的机制,可将模块编译成ko文件,推迟链接的时间:
-
在需要时通过
insmod
完成链接过程,链接系统内核导出符号,完成模块的可插拔功能。 -
相对应的,移除模块通过
rmmod
完成 -
考虑到模块之间可相互依赖,进行调用,因此在载入模块的同时需要检查依赖的模块是否已经载入,
modprobe
可以自动加载依赖模块,其根据依赖信息工作:depmod -A
可生成新模块的依赖信息 推荐使用modprobe 进行模块安装与卸载:
modprobe module [parameter] modprobe -r modules ; 如果依赖的模块有被其他模块调用,则不会移除,反之,一并移除 12
导出符号表
模块之间可通过到处符号表实现依赖关系
EXPORT_SYMBOL(function_name)
导出函数符号 EXPORT_SYMBOL_GPL(function_name)
导出给符合GPL 协议的模块使用
The minor number is used by the kernel to determine exactly which device is being referred to. Depending on how your driver is written (as we will see below), you can either get a direct pointer to your device from the kernel, or you can use the minor number yourself as an index into a local array of devices. Either way, the kernel itself knows almost nothing about minor numbers beyond the fact that they refer to devices implemented by your driver.
Module Stack
驱动常见错误debug
-
invalid format
# insmod hello.ko Error inserting './hello.ko': -1 Invalid module format 12
cat /var/log/messages
以上就是
为各位朋友分享的 相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多 等着你!