最新要闻
- 动漫修心与“动漫之都 亚运之城”的美丽邂逅
- 塞尔达传说王国之泪雷鸣之岛怎么去-雷鸣之岛前往方法
- 诸葛科技:房企拿地格局有所改变 民营房企进,城投退
- 防弹少年团the wings tour_防弹少年团官方商城
- 当前头条:新华文轩(601811)6月26日主力资金净卖出2624.22万元
- 1 2米沙发床多少钱_1 2米
- 天天头条:上海国企在印度被强行收购、丧失控制权?刚刚,紧急回应!
- 中国天楹:与怀来县人民政府签署100MWh重力储能项目合作协议
- 世界播报:场下接着竞争!罗马诺:曼城和阿森纳都打算在本周报价赖斯
- 世界资讯:锁阳的功效与作用及食用方法 锁阳的功效以及食用方法介绍
- 全球微头条丨山西事业单位改革最新消息(山西事业单位改革方案最新)
- 只需真诚,便已相遇足够
- 刘强东“改革”京东管理层200天:老将回归,多业务线一把手调整
- 每日快播:官方回应!西城一片区有腾退计划但尚未实施!还有一片区腾退计划在加紧中!
- 今日视点:资产减值背后的真相
- 智城发展控股(08268)公布年度业绩 公司拥有人应占亏损1097.5万港元 同比盈转亏|世界最资讯
手机
太精明!皇马坐等1月出手,0价签下姆巴佩,省2.5亿欧,操盘大师
近半受访者:美联储至少再加息两次!白银期货反攻?动力有限
- 太精明!皇马坐等1月出手,0价签下姆巴佩,省2.5亿欧,操盘大师
- 近半受访者:美联储至少再加息两次!白银期货反攻?动力有限
- 山东多措并举提升城市“青和力” “筑巢引凤”聚人才 焦点热门
- 山东泰山VS长春亚泰,人员不整崔康熙仍争胜,射手回避陈洋难复仇
- 环球讯息:宋霞
- 售价33.5万,坦克500 Hi4-T和燃油版同价,越野性能不减!
家电
Linux内核中的设计模式-天天热闻
在软件开发中,设计模式是一种被广泛应用的解决问题的方法。设计模式可以帮助开发人员有效地解决软件设计中的问题,提高软件的可维护性和可扩展性,同时也能提高代码的可读性和可重用性。
而在Linux内核的开发中,设计模式也扮演了重要的角色。Linux内核作为一个开源操作系统内核,其内部架构复杂,代码庞大,设计模式在其中的应用也十分广泛。
(资料图片)
本文将介绍一些常见的设计模式在Linux内核中的应用,以及它们在内核中的具体实现方式。通过本文的介绍,读者可以更加深入地了解Linux内核的设计和实现,并学习如何应用设计模式来提高自己的软件开发能力。
01单例模式
在Linux内核中,单例模式(Singleton Pattern)被广泛使用,主要用于管理内核的全局数据结构,确保它们只被创建一次,避免资源浪费和不必要的竞争。
1.1使用场景
在Linux内核中,全局数据结构经常用于表示系统的各种状态和配置,如进程表、文件系统等。这些数据结构需要在整个系统中被访问和修改,因此需要被全局共享。但是,如果这些数据结构不是单例模式,就可能会被多次创建,导致浪费系统资源和引入不必要的竞争。因此,单例模式是管理全局数据结构的一种常用方式。
在Linux内核中,为了保证资源的安全性和一致性,单例模式被广泛应用于管理各种资源,例如:
1. 驱动程序管理设备资源:在Linux内核中,驱动程序是管理设备资源的重要组成部分。每个设备驱动程序都需要管理特定设备的资源,例如设备寄存器、内存和I/O端口等。为了避免重复的资源分配,每个设备驱动程序只需要创建一个实例即可,这就可以使用单例模式来实现。
2. 内存分配器管理系统内存:内存分配器是Linux内核中另一个重要的资源管理器。为了保证系统内存的安全和一致性,内存分配器也需要使用单例模式来保证只有一个实例来管理内存分配。在Linux内核中,内存分配器实现通常使用slab分配器,slab分配器是一种高效的内存管理机制。它使用单例模式来保证系统中只有一个实例来管理内存分配和释放。每个slab分配器实例包含多个slab缓存,每个缓存用于管理一类大小相同的内存块。
1.2实现方式
在Linux内核中,实现单例模式的方式有以下几种:
1. 全局变量:全局变量是实现单例模式最常用的方法之一。在Linux内核中,可以定义一个全局变量来存储单例实例。该变量可以使用static修饰符来限制其作用域,避免在其他文件中访问。然后在需要使用单例模式的地方,可以使用该变量来获取单例实例。
2. 宏定义:宏定义是另一种常用的实现单例模式的方法。在Linux内核中,可以定义一个宏来获取单例实例。该宏可以使用static变量来存储单例实例,以避免在其他文件中访问。然后在需要使用单例模式的地方,可以使用该宏来获取单例实例。
3. 函数封装:函数封装是实现单例模式的一种灵活方式。在Linux内核中,可以定义一个函数来获取单例实例,并使用static变量来存储单例实例。该函数可以使用锁来保证线程安全性,避免多个线程同时访问单例实例。
以上是Linux内核中实现单例模式的常用方式。这些方式都可以保证只有一个实例存在,并在需要使用实例时提供访问接口。在实际应用中,需要根据具体情况选择合适的方式来实现单例模式。
1.3进程管理中的init进程
在Linux内核中,init进程是所有用户进程的祖先进程,它是系统启动时创建的第一个进程,也是进程管理中的重要组成部分。在进程管理中,Linux内核使用了单例模式来确保init进程的唯一性。
在Linux内核源码中,init进程的唯一性通过task_struct结构体中的静态指针init_task来实现。在进程管理子系统中,init_task是一个全局变量,它被用来保存init进程的进程描述符(task_struct)的指针。当Linux内核启动时,init_task被初始化为一个新的进程描述符,并在init进程被创建时,将init_task指针设置为该进程的进程描述符。
由于init_task是一个全局变量,因此在系统运行期间,只能有一个init进程存在,从而实现了单例模式的效果。
下面是Linux内核源码中关于init_task的定义和使用示例:
struct task_struct init_task = INIT_TASK(init_task); // 定义全局变量init_task// 进程管理子系统中的初始化函数void __init fork_init(void){ ... pid = pid_nr(__task_pid(current)); // 获取当前进程的pid task = alloc_task_struct(); // 分配新的进程描述符 ... if (pid == 1) { /* * This will be cleaned up by init when it calls * delete_module. But we still clean it up on * normal exit as well. */ init_task = *task; // 将init_task指针设置为该进程的进程描述符 kthread_create_on_node(init, 0, NULL, 0, NULL, 0); // 创建init进程 } ...}
在上面的代码中,alloc_task_struct()函数用于分配新的进程描述符,kthread_create_on_node()函数用于创建新的内核线程。当当前进程的pid为1时,说明当前进程为init进程,此时将init_task指针设置为该进程的进程描述符,并调用kthread_create_on_node()函数来创建init进程。
通过这样的方式,Linux内核确保了系统中只有一个init进程存在,从而实现了进程管理中的单例模式。
02工厂模式
工厂模式是一种创建型设计模式,其目的是创建对象而不是直接通过new关键字来实例化对象。
2.1使用场景
在Linux内核中,工厂模式通常用于以下场景:
1. 设备驱动:在Linux内核中,设备驱动程序通常需要使用设备对象来与硬件设备进行交互。使用工厂模式可以在内核启动时动态地创建设备对象,而不是预先实例化所有可能的设备对象。这可以大大减少内存使用,并提高系统性能。
2. 系统调用:Linux内核中的系统调用通常需要返回一个特定的数据结构,如file或socket。使用工厂模式可以在系统调用被调用时创建这些数据结构,从而使系统更加灵活。
3. 内存管理:Linux内核中的内存管理子系统负责对物理内存进行分配和管理。使用工厂模式可以动态地创建和管理不同类型的内存分配器,从而使内存管理更加高效。
2.2实现方式
在Linux内核中,实现工厂模式的方式主要有两种:函数指针和宏定义。下面分别介绍这两种方式的具体实现方法。
1. 函数指针
在使用函数指针实现工厂模式时,需要定义一个函数指针类型,用于指向实际创建对象的函数。然后,可以定义一个工厂函数,该函数接受一个函数指针作为参数,并在需要时调用该函数指针来创建对象。下面是一个简单的示例代码:
typedef struct { int type; void *data;} Object;typedef Object *(*CreateObjectFunc)(void);Object *create_object(CreateObjectFunc create_func){ Object *obj = NULL; if (create_func != NULL) { obj = create_func(); } return obj;}Object *create_object_type1(void){ Object *obj = NULL; obj = kmalloc(sizeof(Object), GFP_KERNEL); if (obj != NULL) { obj- >type = 1; obj- >data = NULL; } return obj;}Object *create_object_type2(void){ Object *obj = NULL; obj = kmalloc(sizeof(Object), GFP_KERNEL); if (obj != NULL) { obj- >type = 2; obj- >data = NULL; } return obj;}// 使用示例Object *obj1 = create_object(create_object_type1);Object *obj2 = create_object(create_object_type2);
在上面的代码中,我们定义了一个Object结构体,表示要创建的对象。然后,定义了一个函数指针类型CreateObjectFunc,用于指向实际创建对象的函数。接着,定义了一个create_object函数,该函数接受一个CreateObjectFunc类型的函数指针作为参数,并在需要时调用该函数指针来创建对象。最后,我们定义了两个实际创建对象的函数create_object_type1和create_object_type2,并使用create_object函数来创建对象。
2. 宏定义
在使用宏定义实现工厂模式时,可以定义一组宏,每个宏都代表一个特定类型的对象。然后,可以使用这些宏来创建对象。下面是一个简单的示例代码:
#define CREATE_OBJECT_TYPE1() ({ \\ Object *obj = kmalloc(sizeof(Object), GFP_KERNEL); \\ if (obj != NULL) { \\ obj- >type = 1; \\ obj- >data = NULL; \\ } \\ obj; \\})#define CREATE_OBJECT_TYPE2() ({ \\ Object *obj = kmalloc(sizeof(Object), GFP_KERNEL); \\ if (obj != NULL) { \\ obj- >type = 2; \\ obj- >data = NULL; \\ } \\ obj; \\})// 使用示例Object *obj1 = CREATE_OBJECT_TYPE1();Object *obj2 = CREATE_OBJECT_TYPE2();
在上面的代码中,我们定义了两个宏CREATE_OBJECT_TYPE1和CREATE_OBJECT_TYPE2,分别代表创建类型1和类型2的对象。这些宏使用了C语言的语法扩展,包括大括号表达式和逗号表达式。
2.3字符设备驱动中的file_operations结构体
在Linux内核中,字符设备驱动中的file_operations结构体是一个非常重要的结构体,用于定义字符设备的操作函数。在驱动程序加载时,内核会为每个打开的设备文件分配一个file结构体,并将其与相应的file_operations结构体关联起来,从而实现对设备文件的操作。因此,在字符设备驱动中,通常会使用工厂模式来创建file_operations结构体。下面结合代码来介绍这个过程。
首先,我们需要定义一个工厂函数,用于创建file_operations结构体。下面是一个简单的示例代码:
static struct file_operations *create_file_ops(void){ struct file_operations *ops = kmalloc(sizeof(struct file_operations), GFP_KERNEL); if (ops == NULL) { return NULL; } ops- >owner = THIS_MODULE; ops- >open = my_open; ops- >read = my_read; ops- >write = my_write; ops- >release = my_release; return ops;}
在上面的代码中,我们定义了一个名为create_file_ops的函数,用于创建一个file_operations结构体。该函数使用kmalloc函数来分配内存,并将结构体的各个成员设置为相应的操作函数。这里我们只设置了几个常见的成员,实际上还有很多其他的成员可以设置,具体可以参考Linux内核源码中的定义。
接着,我们可以在驱动程序的init函数中调用create_file_ops函数来创建file_operations结构体,并将其与设备文件关联起来。下面是一个示例代码:
static int __init my_init(void){ int ret; // 创建file_operations结构体 struct file_operations *ops = create_file_ops(); if (ops == NULL) { printk(KERN_ERR "Failedto allocate file operations\\n"); return -ENOMEM; } // 注册字符设备 ret = alloc_chrdev_region(&my_dev, 0, 1, "my_dev"); if (ret < 0) { printk(KERN_ERR "Failed to allocate device number\\n"); kfree(ops); return ret; } // 初始化字符设备 cdev_init(&my_cdev, ops); my_cdev.owner = THIS_MODULE; // 添加字符设备 ret = cdev_add(&my_cdev, my_dev, 1); if (ret < 0) { printk(KERN_ERR "Failed to add character device\\n"); unregister_chrdev_region(my_dev, 1); kfree(ops); return ret; } return 0;}
在上面的代码中,我们先调用create_file_ops函数来创建file_operations结构体,然后在注册字符设备和初始化字符设备时将其与设备文件关联起来。如果创建file_operations结构体失败,我们需要释放已分配的内存,并返回错误。如果注册字符设备或初始化字符设备失败,我们同样需要释放已分配的内存,并返回错误。
注意,在卸载驱动程序时,我们需要释放file_operations结构体的内存,以避免内存泄漏。下面是一个示例代码:
static void __exit my_exit(void){ // 删除字符设备 cdev_del(&my_cdev); // 释放设备号 unregister_chrdev_region(my_dev, 1); // 释放file_operations结构体 kfree(my_cdev.ops);}module_init(my_init);module_exit(my_exit);
在上面的代码中,我们在卸载驱动程序时,先删除字符设备,然后释放设备号和file_operations结构体的内存。注意,我们需要使用my_cdev.ops来访问file_operations结构体,因为它是存储在my_cdev中的。
总的来说,使用工厂模式来创建file_operations结构体可以使代码更加模块化和可维护,而且可以方便地定制设备文件的操作函数。Linux内核源码中的许多字符设备驱动都采用了这种设计模式,例如drivers/tty/tty_io.c中的tty_fops和drivers/char/misc.c中的misc_fops。
2.4块设备驱动中的request_queue结构体
在Linux块设备驱动中,request_queue结构体是负责管理和调度块设备请求的核心数据结构之一。它负责将请求添加到队列中,然后按照某种算法进行调度,以便将它们传递给设备驱动程序处理。
request_queue结构体的创建和初始化通常是由块设备驱动程序负责的。在这个过程中,常常会使用工厂模式来创建和初始化request_queue结构体。
首先,我们需要在驱动程序中定义一个结构体,用于存储request_queue结构体的相关信息,例如:
struct my_device { struct request_queue *queue; // 其他成员变量};
接下来,我们需要编写一个工厂函数,用于创建request_queue结构体并将其与我们的设备关联起来。这个函数通常被命名为my_init_queue,代码示例如下:
static int my_init_queue(struct my_device *dev){ struct request_queue *q; // 分配request_queue结构体的内存 q = blk_alloc_queue(GFP_KERNEL); if (!q) return -ENOMEM; // 设置request_queue的一些属性 blk_queue_logical_block_size(q, 512); blk_queue_physical_block_size(q, 512); blk_queue_max_segments(q, 128); // 其他设置... // 将request_queue与我们的设备关联起来 dev- >queue = q; return 0;}
在上面的代码中,我们使用blk_alloc_queue函数来分配request_queue结构体的内存,并设置一些request_queue的属性。然后,我们将request_queue与我们的设备关联起来,以便在以后的操作中可以方便地访问它。
最后,我们需要在设备驱动程序的初始化函数中调用my_init_queue函数来创建和初始化request_queue结构体。代码示例如下:
static int __init my_init(void){ int ret; struct my_device *dev; // 分配和注册一个块设备 ret = register_blkdev(my_major, "my_device"); if (ret < 0) return ret; // 分配my_device结构体的内存 dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) { unregister_blkdev(my_major, "my_device"); return -ENOMEM; } // 初始化request_queue ret = my_init_queue(dev); if (ret < 0) { kfree(dev); unregister_blkdev(my_major, "my_device"); return ret; } // 其他初始化操作... return 0;}static void __exit my_exit(void){ struct my_device *dev; // 获取my_device结构体 dev = container_of(my_disk- >queue, struct my_device, queue); // 释放request_queue结构体的内存 blk_cleanup_queue(dev- >queue); // 其他清理操作...}module_init(my_init);module_exit(my_exit);
在上面的代码中,我们在my_init函数中调用my_init_queue函数来创建和初始化request_queue结构体,并将其与我们的设备关联起来。在my_exit函数中,我们使用blk_cleanup_queue函数释放request_queue结构体的内存,以及执行其他的清理操作。
总的来说,工厂模式在Linux块设备驱动中的应用比较广泛,它可以帮助我们方便地创建和初始化request_queue结构体,并将其与我们的设备关联起来。这样,我们就可以在以后的操作中方便地访问request_queue,从而更好地管理和调度块设备请求。
03享元模式
在计算机科学中,享元模式是一种结构型设计模式,它的主要目的是通过共享尽可能多的数据来减少系统中的内存使用。这种模式通常适用于需要大量对象的场景,尤其是对象数量超过了系统内存容量的情况。
3.1使用场景
在Linux内核中,享元模式通常用于优化内存使用。在内核中,有许多对象(如进程、文件)需要占用内存,如果每个对象都占用独立的内存,将会导致内存的浪费。而享元模式通过共享相同的内存对象来避免这种浪费。
具体来说,Linux内核中常用的享元模式应用场景有:
1. 内存页(page)的管理:在Linux内核中,内存页(page)是内存管理的最小单位。为了有效地管理内存页,Linux内核使用了一个被称为"伙伴系统"的算法,它通过将内存页划分成一系列大小相等的块,并且以2的幂次方来对其进行分组,然后在每个组中,使用享元模式共享相同大小的块。这种方式可以避免内存碎片的产生,并且提高内存使用效率。
2. 文件系统缓存:在Linux内核中,文件系统缓存通常使用了一种被称为"页面高速缓存"(Page Cache)的机制来管理文件系统的缓存。Page Cache使用了享元模式,它将相同的文件页面映射到同一个物理内存块中,并且使用引用计数来跟踪页面的使用情况。这种方式可以避免同一个文件页面被多次缓存,从而节省了内存空间。
3. 进程管理:在Linux内核中,进程管理也使用了享元模式。具体来说,Linux将所有的进程控制块(PCB)放在一个表格中,并且使用一个唯一的进程ID来标识每个进程。这样,当有新的进程创建时,Linux内核可以快速地查找一个空闲的PCB,并且将其初始化,从而避免了为每个进程分配独立的内存空间。
总的来说,Linux内核中的享元模式主要用于优化内存使用,通过共享相同的内存对象来避免内存浪费,并且提高内核的运行效率。
3.2内存管理中的slab分配器
在 Linux 内存管理中,SLAB 分配器是一种常用的内存分配方式。它使用了享元模式来实现内存的高效管理。在这种模式下,内核会预先创建一定数量的对象,这些对象可以被多个进程或线程共享。这种方式可以减少内存的分配和释放次数,从而提高系统性能和稳定性。
SLAB 分配器由三个重要的数据结构组成:slab、slab_cache 和 kmem_cache。其中,slab 表示一块内存区域,它由若干个对象组成。slab_cache 表示一个对象的缓存池,它由多个 slab 组成。kmem_cache 是一个全局的对象缓存池,它管理了所有 slab_cache。
在 SLAB 分配器中,对象的创建、销毁和管理都是由 kmem_cache 来完成的。它负责创建 slab_cache 和 slab,将对象放入 slab 中,管理 slab 的状态以及实现对象的高效分配和回收等操作。
SLAB 分配器的实现方式非常复杂,涉及到多线程同步、内存管理、缓存管理等方面的知识。下面我们以 kmem_cache_create 函数为例,简单介绍一下 SLAB 分配器的实现原理:
struct kmem_cache *kmem_cache_create(const char *name, size_t size, size_t align, unsigned long flags, void (*ctor)(void *)){ struct kmem_cache *cachep; int err; cachep = kmem_cache_alloc(kmem_cache, flags); if (!cachep) return NULL; err = kmem_cache_init(cachep, name, size, align, flags, ctor); if (err) { kmem_cache_free(kmem_cache, cachep); cachep = NULL; } return cachep;}
kmem_cache_create 函数的作用是创建一个新的缓存池,它首先调用 kmem_cache_alloc 函数从 kmem_cache 中分配一块内存,然后调用 kmem_cache_init 函数对缓存池进行初始化。其中,kmem_cache_alloc 函数的作用是从 kmem_cache 中获取一块内存,如果 kmem_cache 中没有可用的内存,则会重新申请一块新的内存。
kmem_cache_init 函数的作用是对缓存池进行初始化,包括设置对象的大小、对齐方式、缓存池的名称等属性,并且为缓存池创建一个 slab_cache。slab_cache 的作用是管理缓存池中的 slab,它保存了所有 slab 的状态信息,并且可以根据需要自动分配或回收 slab。
3.3进程管理中的进程描述符(task_struct)
在 Linux 内核中,每个进程都有一个进程描述符(task_struct),其中包含了进程的各种信息,如进程的状态、进程 ID、父进程 ID、调度信息、文件描述符表等。由于每个进程都需要一个进程描述符,因此在 Linux 内核中使用了享元模式来实现进程描述符的管理。
具体来说,Linux 内核中维护了一个全局的进程描述符池,其中包含了所有的进程描述符。当需要创建新的进程时,内核会从这个进程描述符池中获取一个可用的进程描述符,然后根据需要对该进程描述符进行初始化。
在进程结束后,进程描述符会被释放回进程描述符池,以便下次可以再次使用。
以下是一个简单的示例代码,展示了如何使用享元模式来管理进程描述符:
struct task_struct { // 进程状态 volatile long state; // 进程 ID pid_t pid; // 父进程 ID pid_t ppid; // 文件描述符表 struct file *files[NR_OPEN_DEFAULT]; // ... 其他信息};// 全局的进程描述符池static struct task_struct task[NR_TASKS];// 获取一个可用的进程描述符struct task_struct *get_task_struct(void) { int i; // 遍历进程描述符池,查找可用的进程描述符 for (i = 0; i < NR_TASKS; i++) { if (task[i].state == TASK_DEAD) { // 找到了可用的进程描述符,返回它 return &task[i]; } } // 进程描述符池已满,返回 NULL return NULL;}// 释放一个进程描述符void put_task_struct(struct task_struct *task) { // 将进程描述符的状态设置为 TASK_DEAD,表示它可以被重新使用 task- >state = TASK_DEAD;}
在上面的代码中,我们使用了一个全局的进程描述符池来存储所有的进程描述符。当需要获取一个可用的进程描述符时,我们遍历进程描述符池,查找状态为 TASK_DEAD 的进程描述符。如果找到了可用的进程描述符,则返回它;否则,返回 NULL。当进程结束时,我们将其对应的进程描述符的状态设置为 TASK_DEAD,以便下次可以再次使用。
04适配器模式
适配器模式(Adapter Pattern)是一种常见的设计模式,它用于将一个类的接口转换成另一个类的接口,以满足不同类之间的兼容性需求。适配器模式在软件开发中的应用十分广泛,尤其在不同系统、不同框架、不同组件之间进行接口兼容性的处理时,往往都需要使用适配器模式来实现。
4.1使用场景
Linux内核中适配器模式的使用场景比较广泛,其中最典型的应用场景是针对不同类型的硬件设备(如网络设备、存储设备等)的驱动程序中。由于这些硬件设备的接口和协议可能不同,因此需要将其转换为一种标准的接口协议,以便在Linux系统中进行统一的管理和使用。
例如,在Linux的网络设备驱动程序中,使用适配器模式将不同类型的网络设备(如以太网卡、无线网卡等)转换为标准的网络设备接口协议(如Linux内核网络协议栈所支持的网络协议),以便实现网络数据包的传输和接收。
另外,在Linux的存储设备驱动程序中,也使用了适配器模式将不同类型的存储设备(如硬盘、固态硬盘等)转换为标准的块设备接口协议,以便在Linux系统中进行统一的管理和使用。
在Linux的虚拟文件系统体系中,文件系统适配器主要用于不同文件系统之间的交互。例如,Linux内核中支持多种不同的文件系统类型,例如ext4、NTFS、FAT等,不同类型的文件系统需要通过文件系统适配器来实现彼此之间的交互。
除此之外,适配器模式还可以应用在其他的场景中,例如将不同类型的数据结构转换为统一的接口协议,或者将不同类型的应用程序适配为标准的API接口等。
4.2实现方式
适配器模式的实现方式有以下几种:
1. 结构体嵌套
适配器模式可以通过将适配对象嵌套在适配器中来实现。适配器可以定义一个新的接口,并将适配对象的接口转换成新的接口。下面是一个简单的示例代码:
struct target_interface { int (*read)(void *buf, int len);};struct adaptee_interface { int (*get_data)(void **data);};struct adaptee { void *data;};struct adapter { struct target_interface *target; struct adaptee_interface *adaptee;};int target_read(void *buf, int len){ struct adapter *adapter = (struct adapter *)buf; void *data; int ret; ret = adapter- >adaptee- >get_data(&data); if (ret < 0) return ret; /* Do something with data */ return ret;}
2. 函数指针
适配器模式还可以通过函数指针来实现。适配器可以定义一个新的函数,并将适配对象的函数转换成新的函数。下面是一个简单的示例代码:
typedef int (*target_func_t)(void *buf, int len);typedef int (*adaptee_func_t)(void **data);struct adapter { target_func_t target_func; adaptee_func_t adaptee_func;};int target_read(void *buf, int len){ struct adapter *adapter = (struct adapter *)buf; void *data; int ret; ret = adapter- >adaptee_func(&data); if (ret < 0) return ret; /* Do something with data */ return ret;}
4.3USB驱动中的usb_driver结构体
在Linux中,USB驱动是一种常见的外部设备驱动类型。针对不同的USB设备,驱动需要提供不同的操作函数,比如打开设备、关闭设备、读写数据等。然而,Linux内核本身并不知道如何处理特定类型的USB设备,需要外部的驱动程序来实现这些操作函数。
这时就可以使用适配器模式来实现。适配器模式能够将不同的接口转换为统一的接口,从而使得不同的模块之间可以互相协作。在Linux中,USB驱动需要将自己的操作函数和USB核心层提供的操作函数进行适配,以便USB核心层能够调用驱动的函数来处理USB设备。
Linux中的usb_driver结构体就是一个适配器模式的典型例子。它定义在include/linux/usb.h
头文件中,是USB设备驱动程序的核心结构体,包含了一系列函数指针,这些函数指针对应了USB设备的不同操作。USB核心层会根据设备的VID和PID等信息匹配到对应的usb_driver结构体,并调用其中的函数指针来完成USB设备的操作。
下面是一个简单的示例代码,展示了一个usb_driver结构体的定义及其初始化方式:
#include < linux/usb.h >// 定义一个USB设备驱动程序的结构体static struct usb_driver my_usb_driver = { .name = "my_usb_driver", // 驱动程序的名称 .probe = my_usb_probe, // 设备初始化函数 .disconnect = my_usb_disconnect, // 设备卸载函数 .id_table = my_usb_id_table, // 设备ID表};// 设备ID表,用于匹配设备static const struct usb_device_id my_usb_id_table[] = { { USB_DEVICE(0x1234, 0x5678) }, {},};
上面的代码中,my_usb_driver
是一个usb_driver结构体的实例,其中包含了设备的名称、设备初始化函数、设备卸载函数和设备ID表等信息。通过初始化这个结构体,USB驱动程序就可以向USB核心层注册自己,并响应相应的USB设备事件。
05总结
本文介绍了常见的设计模式在Linux内核中的应用,并通过具体案例分析,讲述了这些设计模式在内核中的实现方式。读者可以从这些案例中学习到如何在实际开发中应用设计模式,提高软件开发效率和代码质量。
但是,Linux内核的代码量非常庞大,新的设计模式不断被引入。因此,需要继续深入学习和研究,探索更多新的设计模式在Linux内核中的应用。同时,设计模式并不是万能的,需要根据具体问题选择合适的解决方案。
设计模式在Linux内核中具有重要作用,能够帮助开发人员更好地解决问题和提高软件的可维护性和可扩展性。我们希望本文能够为读者提供有用的参考和启示,并鼓励读者不断学习和研究,提高自己的软件开发能力。
关键词:
Linux内核中的设计模式-天天热闻
海洋荣耀 驾驭巅峰 宋PLUS 冠军版长春上市
今日热议:八闽“梧桐”繁茂 企业“宾至如归”
全球即时看!亲子游、毕业游等叠加带动旅游复苏
动漫修心与“动漫之都 亚运之城”的美丽邂逅
焦点观察:西安西郊发现唐代宫人墓地 人骨保存较好的皆为20~30岁女性
跳桥救人小哥,记二等功+邀请落户!_全球快播
云海金属:子公司联合开发超大型镁合金汽车压铸件试制成功 今日报
一顿小龙虾下肚,15岁男孩过敏性休克!万幸,倒下时正在医院就诊_世界快看点
塞尔达传说王国之泪雷鸣之岛怎么去-雷鸣之岛前往方法
漯河市生态环境局临颍分局联合县水利局开展双随机联合执法专项行动 全球今热点
2023年棉纺织行业“金蓝领网络大学堂”启动-全球微资讯
全球视讯!如何改善建筑物室内空气质量?
Livent(LTHM.US)北卡罗来纳州氢氧化锂工厂发生火灾
丛麟科技: 截至2023年6月9日,公司股东户数为14,628户|新资讯
太精明!皇马坐等1月出手,0价签下姆巴佩,省2.5亿欧,操盘大师
成立医学院!浙江这所高校即将布局医学学科,网友:乘风破浪
安阳:机器人“上岗”让专家评标“静悄悄”_每日消息
【天天快播报】招联好期待逾期三年怎么办?逾期三年还会被起诉吗?
环球看点!长三角铁路端午假期收官 五天发送旅客近1500万人次
京东多业务线高管调整|环球热议
文件夹无法重命名怎么办(文件夹无法重命名)
IPO动态:鹰峰电子拟在深交所创业板上市募资12.3亿元|全球头条
“买到躺赚2000万”楼盘声明:三期户型解析会是虚假信息
世界互联网大会数字文明尼山对话今日在山东曲阜召开|热门
当前快讯:车祸牙齿掉落算几级伤残|全球关注
环球观点:诉前财产保全的注意事项都有哪些?财产保全有效期多久? 世界通讯
近半受访者:美联储至少再加息两次!白银期货反攻?动力有限
【报资讯】午评:创业板指探底回升跌0.16% 光伏等新能源赛道股集体反弹
山东多措并举提升城市“青和力” “筑巢引凤”聚人才 焦点热门
中证500ETF(159922)成交额超7亿元 第一大重仓股昆仑万维跌超10%丨ETF观察
翔安区马塘村连续六届获“全国文明村”荣誉 成为富美山村典型
雷蛇灵刃蓝屏怎么解决
报了=白读!这些都是野鸡大学 “高大上”的学校水很深!!
祖传静音键真没了!苹果 iPhone 15 Pro Max 手机保护壳曝光
软件系统、硬件配置将大升级!小米14、小米14 Pro双双现身
高考分数线公布,这些信息千万别在朋友圈乱晒!-最新资讯
郑薇神奇调整!中国女篮半场领先11分,李梦一条龙,杨力维强势
meadow 关于meadow的介绍
6月26日西北地区地炼汽柴油报价暂稳
诸葛科技:房企拿地格局有所改变 民营房企进,城投退
天天快讯:重庆市开县发布高温橙色预警
今年计划优化调整10条公交线路,涉及这些区域
哪些人退休后有退休工资领取?来看看_全球热资讯
每日看点!养老金账户由哪些内容构成
快资讯:广州检察机关发布依法惩治毒品犯罪和参与禁毒综合治理白皮书
艳丽七彩丹霞
山东泰山VS长春亚泰,人员不整崔康熙仍争胜,射手回避陈洋难复仇
魔道祖师歌词归类_魔道祖师歌词
河北省易县发布冰雹橙色预警_世界热点评
西南首家GW级光伏组件工厂首件下线背后,成都光伏产业版图再扩张-当前热文
如果城市有“味道”,大庆的“味道”是……(文末有福利)|天天热点
厉害了!110注二等奖
防弹少年团the wings tour_防弹少年团官方商城
今头条!最高法首次公布因涉新型毒品核准被告人死刑案:罪行极其严重
【全球独家】蓝曦臣亲自责罚蓝忘机 蓝曦臣
2021年第一季度工作简报 第一季度宣传工作总结
法院|宁波海事法院成功调解一起涉外船舶碰撞纠纷案-全球独家
女友的妈妈|信息
宋雷 天天速看
安徽一男孩遭家人棍打从5楼跳下,当地妇联回应 焦点速看
环球讯息:宋霞
当前头条:新华文轩(601811)6月26日主力资金净卖出2624.22万元
河南2023年高考分数线公布:文科一本547分,理科一本514分|世界讯息
【世界速看料】鄱阳湖区通江水体面积今年首次突破2000平方公里
实名曝光!山东又有43人被终生禁驾
【全球热闻】香酥鸡翅的正宗做法?
天天头条:国网吉林培训中心:推动监督贯通融合,实现统筹衔接攥指成拳
开阳县紫兴街道大坪子(易安)小区“四抓四强”激活基层治理新动能 每日资讯
浙江交科(002061.SZ):获长城人寿增持1%的股份、持股比例升至6.0289% 世界关注
1 2米沙发床多少钱_1 2米
售价33.5万,坦克500 Hi4-T和燃油版同价,越野性能不减!
看完这群小学生写的诗,整个世界都温柔了!
618战绩出炉!看家居品牌如何在今年消费大环境下脱颖而出?
卖楼抵债 “烘焙第一股”克莉丝汀出售上海两处物业
青海湖水域面积达到近十年来最大值 湟鱼洄游迎来高峰期
“爱上北京的100个理由”短视频大赛在京颁奖 天天滚动
张景宁:三解宁萌意 奋进新征程|观焦点
“克格勃已知晓……,他们欲武力夺权”|热文
南昌中考填报志愿26日开始 市教育考试院发布招录问题解答集锦 每日速讯
21年前的今天:火箭用状元签选中了姚明
千亿房企退市,败在“1元”面值保卫战_天天报道
从逆流而上的新零售巨轮,看林氏家居逆势增长的关键密码|世界短讯
BBC记者:曼联认为对芒特报价已足够合理,拒绝支付“曼联税” 全球微动态
富春染织: 富春染织2023年第一次临时股东大会会议资料 全球热点
316l和316的区别筷子_316l和316的区别|观热点
森林怎么丢背包里面的东西_森林怎么通关 世界今亮点
手工制作钟表大全图片 小学生_手工制作钟表大全图片
word目录点点怎么对齐_word目录点点怎么输入 焦点热议
用数字化破解“慢堵烦”!淮安打造重大产业项目审批新模式
设计时速 350 公里:2 小时左右即达,贵南高铁进入运行试验阶段 速讯
天天头条:上海国企在印度被强行收购、丧失控制权?刚刚,紧急回应!
走进广东历史文化街区探寻城市文脉
百事通!李小婉户外才艺直播_李小婉
一梦江湖华山攻略,技巧分享|今日最新
大企业“发榜”中小企业“揭榜” “百场万企”对接会助力打造融通创新生态_世界视点
环球最新:江苏无锡:以“人兴”促“业旺”,“引智育才”唱响乡村振兴大戏
环球热推荐:甘肃金塔一卤肉店液化气泄漏闪爆,官方:2人轻伤,原因正调查
端午假期铁路累计发送旅客7037.9万人次 较2019年同期增长11.3%
朗威股份:创业板IPO网上发行中签率0.0298724094%|天天快讯