linux内核驱动常用代码讲解

class_create为该设备创建一个class,再为每个设备调用 class_device_create创建对应的设备。

IS_ERR()来判断内核函数的返回值是不是一个有效的指针。

Linux中request_firmware 的用法

读者加锁/解锁操作实现分析

linux内核cdev_init系列函数(字符设备的注册)

cdev_init(&my_cdev, &fops);初始化 cdev 后,需要把它添加到系统中去。为此可以调用 cdev_add() 函数。传入 cdev 结构的指针,起始设备编号,以及设备编号范围。

int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
    
    
   p->dev = dev;
   p->count = count;
   return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
}

kobj_map() 函数就是用来把字符设备编号和 cdev 结构变量一起保存到 cdev_map 这个散列表里。当后续要打开一个字符设备文件时,通过调用 kobj_lookup() 函数,根据设备编号就可以找到 cdev 结构变量,从而取出其中的 ops 字段。

设备驱动程序通过调用cdev_add把它所管理的设备对象的指针嵌入到一个类型为struct probe的节点之中,然后再把该节点加入到cdev_map所实现的哈希链表中。
当一个字符设备驱动不再需要的时候(比如模块卸载),就可以用 cdev_del() 函数来释放 cdev 占用的内存。

register_chrdev_region()内核中所有已分配的字符设备编号都记录在一个名为 chrdevs 散列表里。该散列表中的每一个元素是一个 char_device_struct 结构,它的定义如下:

static struct char_device_struct {
    
    
struct char_device_struct *next; // 指向散列冲突链表中的下一个元素的指针
unsigned int major;           // 主设备号
unsigned int baseminor;       // 起始次设备号
int minorct;                 // 设备编号的范围大小
char name[64];        // 处理该设备编号范围内的设备驱动的名称
struct file_operations *fops;     
struct cdev *cdev;        // 指向字符设备驱动程序描述符的指针
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];

内核提供了三个函数来注册一组字符设备编号,这三个函数分别是 register_chrdev_region()alloc_chrdev_region()register_chrdev()

my_debugfs_root = debugfs_create_dir("mydebug", NULL);
第一个参数是目录的名称
第二个参数用来指定这个目录的上级目录,如果是NULL,则表示是放在debugfs的根目录里 。

create_singlethread_workqueue创建的工作队列只在一个cpu上创建worker_thread内核线程,即使该cpu一直被printk占用也还有其他的cpu可以继续调用其他的进程

kzalloc_node根据内存例程分配内存

class是设备类,完全是抽象出来的概念,没有对应的实体。所谓设备类,是指提供的用户接口相似的一类设备的集合,常见的设备类的有block、tty、input、usb等等。

pci_set_master 设定设备工作在总线主设备模式

生产者消费者模式的优点

1、解耦
如果让生产者直接调用消费者,那么生产者对于消费者就会产生依赖(也就是耦合)。将来如果消费者的代码发生变化, 可能会影响到生产者。而如果两者都依赖于某个缓冲区,两者之间不直接依赖,耦合也就相应降低了。
2、支持并发
生产者与消费者是两个独立的并发体,他们之间是用缓冲区作为桥梁连接,生产者只需要往缓冲区里丢数据,就可以继续生产下一个数据,而消费者只需要从缓冲区了拿数据即可,这样就不会因为彼此的处理速度而发生阻塞。
3、支持忙闲不均
缓冲区还有另一个好处。如果制造数据的速度时快时慢,缓冲区的好处就体现出来 了。当数据制造快的时候,消费者来不及处理,未处理的数据可以暂时存在缓冲区中。 等生产者的制造速度慢下来,消费者再慢慢处理掉。

在线程方式下,生产者和消费者各自是一个线程。生产者把数据写入队列头(以下简称push),消费者从队列尾部读出数据(以下简称pop)。当队列为空,消费者就稍息(稍事休息);当队列满(达到最大长度),生产者就稍息。整个流程并不复杂。

猜你喜欢

转载自blog.csdn.net/qq_44710568/article/details/131897007