当前位置: 首页 > news >正文

动态加载和异步调用tasklet/workqueue day63 ay64

五:动态加载驱动

1.区别

静态编译进内核:驱动存在于ulmage中,内核启动时加载驱动

动态加载驱动:驱动独立存在(xxx.ko),内核启动后动态加载

​ 动态编译只需要make modules 然后在内核insmod,就不需要reboot重复流程,直接编译就行

2.代码

MODULE_LIENCES("GPL");	//在led4.c最后面加,符合gnu的协议,内核不会“脏”
config LED4tristate "this is led4"		//改成三态default y	---help---this is demo_secondTest driver在.config CONFIG_LED4=m make menuconfig 中是 M 状态,改成 M 
cp drivers/char/led4.ko ~/nfs/rootfs	// make modules -- M全部编译成.ko
cp arch/arm/boot/uImage ~/tftpboot///make uImage
内核中
insmod led4.ko	//动态加载驱动模块
mknod /dev/led4 c 253 0
lsmod		//查看动态加载的驱动模块
rmmod led4	//卸载动态加载的驱动模块(卸载使用模块名)
1.自带寄存器函数
#define GPIO_LED S3C2410_GPB(5)		//引脚GPB5
2.自动创建设备节点次class/device
alloc_chrdev_regionclass_create
device_create	//创造设备节点
#include <asm/io.h>
#include <asm/ioctl.h>
#include <asm/uaccess.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/types.h>
#include <mach/gpio-nrs.h>
#include <mach/gpio.h>
#include <linux/device.h>
#include <linux/cdev.h>#define DEV_NAME "led4_alloc"
#define GPIO_LED S3C2410_GPB(5)
#define LED_ON 0
#define LED_OFF 1static void led2_init(void) {gpio_request(GPIO_LED, "led4"); //申请gpio_direction_output(GPIO_LED, LED_OFF);
}static void led2_on(void) { gpio_set_value(GPIO_LED, LED_ON); }static void led2_off(void) { gpio_set_value(GPIO_LED, LED_OFF); }static void led2_deinit(void) {gpio_set_value(GPIO_LED, LED_OFF);gpio_free(GPIO_LED);
}//以下都是函数指针
static int open(struct inode *node, struct file *file) {led2_init();printk("led4  open ...\n"); //内核打印return 0;
}static ssize_t read(struct file *file, char __user *buf, size_t len,loff_t *offset) {// copy_to_user();printk("led4 read ...\n");return 0;
}static ssize_t write(struct file *file, const char __user *buf, size_t len,loff_t *offset) {unsigned char data[10] = {0};unsigned int len_cp = (sizeof(data) < len) ? sizeof(data) : len;ssize_t ret = len_cp;// strcmp(buf,"ledon")// copy_to_user	用户访问copy_from_user(data, buf,len_cp); //让代码不要在内核空间运行去访问,如果数据有问题,内核访问野指针,内核崩溃if (!strcmp(data, "ledon"))led2_on();else if (!strcmp(data, "ledoff"))led2_off();elseret = -EINVAL; // EINVAL “-” 返回负,返回非正常值printk("led4 write ...\n");return ret;
}static int close(struct inode *node, struct file *file) {led2_deinit();printk("led4 close ...\n");return 0;
}static dev_t dev_num; //创建设备号
//操作方法
static struct file_operations fops = {//结构体都是函数指针,上面函数指针都已经初始化了,//所以赋给对应的结构体中函数指针// gnu中,不像数据结构(windos)一个一个赋值,可以部分初始化.owner = THIS_MODULE, //指向自己模块,默认.open = open,         //.read = read,.write = write,.release = close};static struct cdev cdev; //设备的结构体,//(要把设备号和操作方法放进去),然后在给到内核
static struct class *led_class;
static struct device *led_device;static int __init led_init(void) {int ret = 0;ret = alloc_chrdev_region(&dev_num, 0, 1, DEV_NAME);if (ret < 0) {printk("alloc_chrdev_region failed\n");return ret;}cdev_init(&cdev,&fops); // cdev_init 把操作方法放进cdev结构体ret = cdev_add(&cdev, dev_num,1); //添加几个设备?--和对应设备号放进cdev结构体if (ret < 0) ////判断int类型goto err_cdev;led_class = class_create(THIS_MODULE, "led_class");if (IS_ERR(led_class)) ////判断int类型goto err_class;led_device = device_create(led_class,NULL,dev_num,NULL,DEV_NAME);if (IS_ERR(led_device))goto err_device;printk("led4_init   ....\n");return ret;err_class:unregister_chrdev_region(dev_num, 1); //取消注册class_destroy(led_class);printk("register_chrdev_region  failed\n");err_device:unregister_chrdev_region(dev_num, 1); device_destroy(led_class, dev_num);printk("register_chrdev_region  failed\n");err_cdev:cdev_del(&cdev); //销毁初始化的cdev结构体printk("cdev_add  failed\n");return ret;
}static void __exit led_exit(void) {unregister_chrdev_region(dev_num, 1);device_destroy(led_class, dev_num);class_destroy(led_class);cdev_del(&cdev);printk("led4_exit   ....\n");
}module_init(led_init); //模块初始化 修饰对应的函数,然后执行对应函数
module_exit(led_exit); //在操作系统注销时和卸载  -- 启动和注销
MMODULE_LICENSE("GPL");

六:misc杂项设备驱动

#include <linux/major.h>
#include <linux/miscdevice.h>
//不需要#define MAJOR/MINOP NUMstatic struct miscdevice misc = 
{.minor = MISC_DYNAMIC_MINOR,.name = DEV_NAME,.fops = &fops	
};static int __init led_init(void)	
{int ret = misc_register(&misc); 	//自动分配主设备号10,自动分配次设备号,//其中misc会把之前字符驱动的动作自动完成if(ret < 0)goto err_misc_register;printk("led4_init   ....\n");return ret;err_misc_register:misc_deregister(&misc);printk("cmisc_deregister failed\n");return ret;	
}static void __exit led_exit(void)
{misc_deregister(&misc);printk("led4_exit   ....\n");
}

好处就是自动分配主设备号10,和随机分配次设备号

之后进入内核就不需要在mknod节点,只需要insmod就可以

七:字符和杂项驱动总结

1.字符设备驱动模板

#define DEV_NAME  “led”dev_t dev_num;struct cdev cdev;struct file_operations fops;led_init(){MKDEV();register_chrdev_region();    alloc_chrdev_register();     //    /proc/devices    DEV_NAME ,二选一--------------------------------------------------------cdev_init();cdev_add();class_create();device_create();       // /dev/led     DEV_NAME}led_exit(){//对应按相反顺序注销
}module_init(led_init);module_exit(led_exit);

2.杂项设备驱动(字符设备)

#define  DEV_NAME  “led”struct miscdevice misc;     // DEV_NAMEled_init(){misc_register();     //      -- /dev/led}led_exit(){}module_init(led_init);module_exit(led_exit);

八:ioctl

ioctl: 实现设置和属性获取方面的功能

devNUMDIRDATA
led10,1,2
led20,1,2,3
led30,1

1.代码

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <mach/gpio-nrs.h>
#include <mach/gpio.h>
#include <linux/miscdevice.h>
#include <asm/ioctl.h>#define DEV_NAME "led4_ioctl"
#define GPIO_LED1 S3C2410_GPB(5)
#define GPIO_LED2 S3C2410_GPB(6)
#define GPIO_LED3 S3C2410_GPB(7)
#define GPIO_LED4 S3C2410_GPB(8)
#define LED_ON 0
#define LED_OFF 1
#define LED_LED 2
#define MAGIC_NUM 'x'#define CMD_LED_ON _IO(MAGIC_NUM, LED_ON)
#define CMD_LED_OFF _IO(MAGIC_NUM, LED_OFF)
#define CMD_LED_LED _IO(MAGIC_NUM, LED_LED)static unsigned long led[4] = {GPIO_LED1, GPIO_LED2, GPIO_LED3, GPIO_LED4};static void led2_init(void) {int i = 0;gpio_request(GPIO_LED1, "led1"); //申请gpio_request(GPIO_LED2, "led2"); //申请gpio_request(GPIO_LED3, "led3"); //申请gpio_request(GPIO_LED4, "led4"); //申请for (i = 0; i < 4; i++) {gpio_direction_output(led[i], LED_OFF);//操作对应引脚}
}static void led2_on(unsigned char num)
{ gpio_set_value(led[num - 1], LED_ON); }static void led2_off(unsigned char num) 
{ gpio_set_value(led[num - 1], LED_OFF); }static void led2_deinit(void) {int i = 0;for (i = 0; i < 4; i++) {gpio_set_value(led[i], LED_OFF);gpio_free(led[i]);}
}//以下都是函数指针
static int open(struct inode *node, struct file *file) {led2_init();printk("led4_ioctl  open ...\n"); //内核打印return 0;
}static ssize_t read(struct file *file, char __user *buf, size_t len,loff_t *offset) {// copy_to_user();printk("led4 read ...\n");return 0;
}static ssize_t write(struct file * file, const char __user * buf, size_t len, loff_t * offset)
{printk("led4 write ...\n");return 0;
}
typedef struct __tag {unsigned char l1;unsigned char l2;unsigned char l3;unsigned char l4;
}led_t;static led_t leda;static long ioctl(struct file *file, unsigned int cmd, unsigned long arg) {int ret = 0;printk("cmd = %d	arg = %ld\n", cmd, arg);switch (cmd) {case CMD_LED_ON:led2_on(arg);break;case CMD_LED_OFF:led2_off(arg);break;case CMD_LED_LED:copy_from_user(&leda, (led_t *)arg, sizeof(led_t));break;default:ret = -EINVAL;break;}return ret;
}static int close(struct inode *node, struct file *file) {led2_deinit();printk("led4 close ...\n");return 0;
}//操作方法
static struct file_operations fops = {//结构体都是函数指针,上面函数指针都已经初始化了,//所以赋给对应的结构体中函数指针// gnu中,不像数据结构(windos)一个一个赋值,可以部分初始化.owner = THIS_MODULE, //指向自己模块,默认.open = open,         //.read = read,.write = write,.unlocked_ioctl = ioctl,	//操作方法链接ioctl.release = close};static struct miscdevice misc = {.minor = MISC_DYNAMIC_MINOR, .name = DEV_NAME, .fops = &fops};static int __init led_init(void) {int ret = misc_register(&misc); //自动分配主设备号10,自动分配次设备号//其中misc会把之前字符驱动的动作自动完成if (ret < 0)goto err_misc_register;printk("led4_ioctl_init   ....\n");return ret;err_misc_register:misc_deregister(&misc);printk("cmisc_deregister failed\n");return ret;
}static void __exit led_exit(void) {misc_deregister(&misc);printk("led4_exit   ....\n");
}module_init(led_init); //模块初始化 修饰对应的函数,然后执行对应函数
module_exit(led_exit); //在操作系统注销时和卸载  -- 启动和注销
MODULE_LICENSE("GPL");
/
1.1 _IO宏
#define _IOC(dir,type,nr,size)			\((unsigned int)				\(((dir)  << _IOC_DIRSHIFT) |		\((type) << _IOC_TYPESHIFT) |		\((nr)   << _IOC_NRSHIFT) |		\((size) << _IOC_SIZESHIFT)))
//左移xx位,#define _IO(type,nr)		_IOC(_IOC_NONE,(type),(nr),0)//读
#define _IOR(type,nr,size)	_IOC(_IOC_READ,(type),(nr),sizeof(size))//写	
#define _IOW(type,nr,size)	_IOC(_IOC_WRITE,(type),(nr),sizeof(size))//读写
#define _IOWR(type,nr,size)	_IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))

2.配套配置文件

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>#define LED_ON 0
#define LED_OFF 1
#define LED_LED 2
#define MAGIC_NUM 'x'
#define CMD_LED_ON   _IO(MAGIC_NUM, LED_ON)
#define CMD_LED_OFF  _IO(MAGIC_NUM, LED_OFF)
#define CMD_LED_LED  _IO(MAGIC_NUM, LED_LED)typedef struct __tag
{unsigned char l1;unsigned char l2;unsigned char l3;unsigned char l4;
}led_t;static led_t led;int main(int argc, const char *argv[])
{int fd = open("/dev/led4_alloc",O_RDWR);	////打开对应内核文件if(fd < 0){perror("open demo failed");}unsigned char buf[20] = {0};int ret = 0;
#if 1while(1){//ioctl(fd,1,1);	//置电平1,led1ioctl(fd,CMD_LED_ON,4);sleep(1);ioctl(fd,CMD_LED_OFF,4);sleep(1);led.l1 = 0;led.l2 = 1;ret = ioctl(fd,CMD_LED_LED,&led);//CMD_LED_LED 为2 系统忽律,//要改在vim Documentation/ioctl/ioctl-number.txt printf("ret = %d\n",ret);sleep(1);}
#endif#if 0while(1){write(fd,"ledon",strlen("ledon"));sleep(1);write(fd,"ledoff",strlen("ledoff"));sleep(1);}
#endifclose(fd);return 0;
}

3.文档

This table lists ioctls visible from user land for Linux/i386.  It contains
most drivers up to 2.3.14, but I know I am missing some.Code	Seq#	Include File		Comments
========================================================
0x00	00-1F	linux/fs.h		conflict!
0x00	00-1F	scsi/scsi_ioctl.h	conflict!
0x00	00-1F	linux/fb.h		conflict!
0x00	00-1F	linux/wavefront.h	conflict!
0x02	all	linux/fd.h
0x03	all	linux/hdreg.h
0x04	D2-DC	linux/umsdos_fs.h	Dead since 2.6.11, but don't reuse these.
0x06	all	linux/lp.h
0x09	all	linux/md.h
0x12	all	linux/fs.h如果修改 eg: 'x' 00-1f     对应文件位置//尽量不修改

九:irq_key

1 .inline内联函数

​ 函数体原地展开

2. waitevent等待队列

static wait_queue_head_t wq;
static int condition;condition = 1;
wake_up_interruptible(&wq);wait_event_interruptible(wq,condition);ret = request_irq(IRQ_EINT8, eint8_handler, 			IRQF_TRIGGER_FALLING, "irq_eint8", &arg);if(ret <0)goto err_request_irq;init_waitqueue_head(&wq);printk("key4_init   ....\n");return ret;err_request_irq:disable_irq(IRQ_EINT8);free_irq(IRQ_EINT8,&arg);printk("request_irq failed\n");return ret;
2.1:代码
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <mach/gpio-nrs.h>
#include <mach/gpio.h>
#include <linux/miscdevice.h>
#include <asm/ioctl.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/wait.h>
#include <linux/sched.h>#define DEV_NAME "key_irq"
static wait_queue_head_t wq;
static int condition;irqreturn_t eint8_handler(int irq_num,void * dev)
{unsigned int arg = *(unsigned int *)dev;if(100 != arg){return IRQ_NONE;}condition = 1;wake_up_interruptible(&wq);printk("irq_num = %d	dev = %d\n",irq_num,arg);return IRQ_HANDLED;
}static void key2_init(void)
{
}static void key2_deinit(void)
{
}static int open(struct inode * node, struct file * file)
{key2_init();printk("key4  open ...\n");return 0;
}static ssize_t read(struct file * file, char __user * buf, size_t len, loff_t * offset)
{//copy_to_user();printk("key4 read start\n");condition = 0;wait_event_interruptible(wq,condition);printk("key4 read end...\n");return 0;
}static ssize_t write(struct file * file, const char __user * buf, size_t len, loff_t * offset)
{printk("key4 write ...\n");return 0;
}static int close(struct inode * node, struct file * file)
{key2_deinit();printk("key4 close ...\n");return 0;
}static struct file_operations fops = 
{.owner = THIS_MODULE,.open = open,.read = read,.write = write,.release = close
};static struct miscdevice misc = 
{.minor = MISC_DYNAMIC_MINOR,.name = DEV_NAME,.fops = &fops
};static unsigned int arg = 100;static int __init key1_init(void)
{int ret = misc_register(&misc);if(ret < 0)goto err_misc_register;ret = request_irq(IRQ_EINT8, eint8_handler, IRQF_TRIGGER_FALLING, "irq_eint8", &arg);if(ret <0)goto err_request_irq;init_waitqueue_head(&wq);printk("key4_init   ....\n");return ret;err_request_irq:disable_irq(IRQ_EINT8);free_irq(IRQ_EINT8,&arg);printk("request_irq failed\n");return ret;err_misc_register:misc_deregister(&misc);printk("misc_register  faikey\n");return ret;	
}static void __exit key1_exit(void)
{disable_irq(IRQ_EINT8);free_irq(IRQ_EINT8,&arg);misc_deregister(&misc);printk("key4_exit   ....\n");
}module_init(key1_init);
module_exit(key1_exit);
MODULE_LICENSE("GPL");
2.2完整流程
1. 配套关系在 Linux 内核里,等待/唤醒机制是典型的 进程同步机制,核心元素是:等待队列(wait queue)static wait_queue_head_t wq;
init_waitqueue_head(&wq);它是一个链表,里面挂着所有等待某个条件的进程(task_struct)。用于管理阻塞的进程。条件变量(condition)static int condition;用来表示某个事件是否发生。wait_event_interruptible() 会检查它,如果为假就让进程睡眠,否则直接返回。阻塞等待函数wait_event_interruptible(wq, condition);宏会判断 condition 是否为真。如果为假,就把调用进程放入 wq 队列,然后进入 可中断睡眠(TASK_INTERRUPTIBLE)。当唤醒函数触发时,进程会被唤醒,再次检查 condition。唤醒函数wake_up_interruptible(&wq);将 wq 队列里的所有可中断睡眠进程唤醒。唤醒后的进程重新调度,继续执行 wait_event_interruptible() 后面的代码。2. 结合你的代码流程假设用户进程执行了 read():condition = 0;
wait_event_interruptible(wq, condition);流程拆解:进程检查条件condition == 0 → 条件不满足。进程进入睡眠进程状态变为 TASK_INTERRUPTIBLE。内核把进程添加到 wq 队列里。CPU 会调度其它可运行进程,当前进程阻塞。中断触发用户按键按下 → 外部中断触发 → 调用 eint8_handler()。中断服务函数处理condition = 1;
wake_up_interruptible(&wq);设置条件为真(condition = 1)唤醒等待队列中的所有进程这些进程状态从 TASK_INTERRUPTIBLE → TASK_RUNNING等待调度器分配 CPU 运行进程被调度回 CPUwait_event_interruptible() 重新检查条件condition == 1 → 条件满足 → 返回进程继续执行 read() 后面的代码printk("key4 read end...\n");3. wait_event / wake_up 配套总结
元素	作用
wait_queue_head_t wq	管理所有等待该事件的进程
condition	条件变量,表示事件是否发生
wait_event_interruptible(wq, condition)	阻塞调用进程直到条件成立
wake_up_interruptible(&wq)	唤醒等待队列里的所有进程,重新检查条件关键点:wait_event 宏里会不断检查条件,直到满足才返回 → 避免“虚假唤醒”问题。wake_up_interruptible 只是将等待队列中的进程标记为可运行,实际调度由内核决定。💡 总结一句话:wait_event 是“睡觉等事件”,wake_up 是“事件来了叫醒睡着的进程”,二者配套实现了 中断到进程的异步通知机制。

3.中断底半部三种方式

上半部(Top Half)
→ 就是中断服务程序 (ISR)。
→ 要求执行尽量快,只做“紧急”的事,比如:

  • 读取寄存器清中断
  • 保存必要的数据到缓存
  • 通知内核有事件发生

下半部(Bottom Half)
→ 处理耗时的部分,比如:

  • 数据拷贝、复杂计算
  • 和用户空间交互
//下半部3种实现方式
1.软中断
2.tasklet	//基于软中断实现一种软中断
3.workqueue

4.异步调用

4.1 tasklet

Tasklet = 一种基于 Softirq 的轻量级下半部机制,不能睡眠

​ 在同一个 CPU 上,同一个 tasklet 永远不会并行执行(保证了串行化)

串行化保证

  • 内核保证 同一个 tasklet 在同一个 CPU 上不会并行执行
  • 如果某个 tasklet 正在跑,调度器不会在同一时刻再次运行它。
  • 所以你可以放心在 tasklet 函数里操作它自己的数据,不用加锁。
  • 这一点确实有点像“隐式的互斥”。

避免数据竞争

  • 因为 tasklet 是单线程化的,使用它处理某个设备的中断数据,可以避免并发访问导致数据出错。
4.2 代码
static wait_queue_head_t wq;
static int condition;
static struct tasklet_struct task;static void task_func(unsigned long arg)
{ssleep(1);condition = 1;wake_up_interruptible(&wq);printk("task_fun arg = %ld\n",arg);
}static irqreturn_t eint8_handler(int irq_num,void * dev)
{unsigned int arg = *(unsigned int *)dev;if(100 != arg){return IRQ_NONE;}tasklet_schedule(&task);printk("irq_num = %d	dev = %d\n",irq_num,arg);return IRQ_HANDLED;
}static int __init key1_init(void)
{int ret = misc_register(&misc);if(ret < 0)goto err_misc_register;ret = request_irq(IRQ_EINT8, eint8_handler, IRQF_TRIGGER_FALLING, "irq_eint8", &arg);if(ret <0)goto err_request_irq;init_waitqueue_head(&wq);tasklet_init(&task,task_func,200);printk("key4_init   ....\n");return ret;err_request_irq:disable_irq(IRQ_EINT8);free_irq(IRQ_EINT8,&arg);printk("request_irq failed\n");return ret;err_misc_register:misc_deregister(&misc);printk("misc_register  faikey\n");return ret;	
}
//完整代码
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <mach/gpio-nrs.h>
#include <mach/gpio.h>
#include <linux/miscdevice.h>
#include <asm/ioctl.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/wait.h>
#include <linux/sched.h>#define DEV_NAME "key_irq"
static wait_queue_head_t wq;
static int condition;
static struct tasklet_struct task;static void task_func(unsigned long arg)
{ssleep(1);condition = 1;wake_up_interruptible(&wq);printk("task_fun arg = %ld\n",arg);
}static irqreturn_t eint8_handler(int irq_num,void * dev)
{unsigned int arg = *(unsigned int *)dev;if(100 != arg){return IRQ_NONE;}tasklet_schedule(&task);printk("irq_num = %d	dev = %d\n",irq_num,arg);return IRQ_HANDLED;
}static void key2_init(void)
{
}static void key2_deinit(void)
{
}static int open(struct inode * node, struct file * file)
{key2_init();printk("key4  open ...\n");return 0;
}static ssize_t read(struct file * file, char __user * buf, size_t len, loff_t * offset)
{//copy_to_user();printk("key4 read start\n");condition = 0;wait_event_interruptible(wq,condition);printk("key4 read end...\n");return 0;
}static ssize_t write(struct file * file, const char __user * buf, size_t len, loff_t * offset)
{printk("key4 write ...\n");return 0;
}static int close(struct inode * node, struct file * file)
{key2_deinit();printk("key4 close ...\n");return 0;
}static struct file_operations fops = 
{.owner = THIS_MODULE,.open = open,.read = read,.write = write,.release = close
};static struct miscdevice misc = 
{.minor = MISC_DYNAMIC_MINOR,.name = DEV_NAME,.fops = &fops
};static unsigned int arg = 100;static int __init key1_init(void)
{int ret = misc_register(&misc);if(ret < 0)goto err_misc_register;ret = request_irq(IRQ_EINT8, eint8_handler, IRQF_TRIGGER_FALLING, "irq_eint8", &arg);if(ret <0)goto err_request_irq;init_waitqueue_head(&wq);tasklet_init(&task,task_func,200);printk("key4_init   ....\n");return ret;err_request_irq:disable_irq(IRQ_EINT8);free_irq(IRQ_EINT8,&arg);printk("request_irq failed\n");return ret;err_misc_register:misc_deregister(&misc);printk("misc_register  faikey\n");return ret;	
}static void __exit key1_exit(void)
{disable_irq(IRQ_EINT8);free_irq(IRQ_EINT8,&arg);misc_deregister(&misc);printk("key4_exit   ....\n");
}module_init(key1_init);
module_exit(key1_exit);
MODULE_LICENSE("GPL");
4.3 workqueue

Tasklet 虽然能把工作延后,但它仍然运行在 中断上下文

  • 不能睡眠
  • 不能调度别的进程
  • 不能调用可能阻塞的函数(例如 kmalloc(GFP_KERNEL)copy_to_usermutex_lock
  • 如果我们在下半部需要做一些 耗时或可能阻塞的工作(比如磁盘 IO,文件操作,访问用户空间),就必须要有一个能在 进程上下文 运行的机制(Workqueue)。

Workqueue 的特点

  • 运行在内核线程(kworker)里,属于 进程上下文
  • 可以睡眠(这是和 tasklet 的最大区别)。
  • 内核会维护一组 kworker 线程,专门执行 work。
4.4 代码
static wait_queue_head_t wq;
static int condition = 0;
static struct work_struct work;static void work_func(unsigned long arg)
{ssleep(1);condition = 1;wake_up_interruptible(&wq);printk("task_fun arg = %ld\n",arg);
}static irqreturn_t eint8_handler(int irq_num,void * dev)
{unsigned int arg = *(unsigned int *)dev;if(100 != arg){return IRQ_NONE;}schedule_work(&work);printk("irq_num = %d	dev = %d\n",irq_num,arg);return IRQ_HANDLED;
}init_waitqueue_head(&wq);INIT_WORK(&work,work_func);
完整代码
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <mach/gpio-nrs.h>
#include <mach/gpio.h>
#include <linux/miscdevice.h>
#include <asm/ioctl.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/workqueue.h>
#include <linux/delay.h>#define DEV_NAME "key"
static wait_queue_head_t wq;
static int condition = 0;
static struct work_struct work;static void work_func(unsigned long arg)
{ssleep(1);condition = 1;wake_up_interruptible(&wq);printk("task_fun arg = %ld\n",arg);
}static irqreturn_t eint8_handler(int irq_num,void * dev)
{unsigned int arg = *(unsigned int *)dev;if(100 != arg){return IRQ_NONE;}schedule_work(&work);printk("irq_num = %d	dev = %d\n",irq_num,arg);return IRQ_HANDLED;
}static void key2_init(void)
{
}static void key2_deinit(void)
{
}static int open(struct inode * node, struct file * file)
{key2_init();printk("key4_work  open ...\n");return 0;
}static ssize_t read(struct file * file, char __user * buf, size_t len, loff_t * offset)
{//copy_to_user();printk("key4 read start\n");condition = 0;wait_event_interruptible(wq,condition);printk("key4 read end...\n");return 0;
}static ssize_t write(struct file * file, const char __user * buf, size_t len, loff_t * offset)
{printk("key4 write ...\n");return 0;
}static int close(struct inode * node, struct file * file)
{key2_deinit();printk("key4 close ...\n");return 0;
}static struct file_operations fops = 
{.owner = THIS_MODULE,.open = open,.read = read,.write = write,.release = close
};static struct miscdevice misc = 
{.minor = MISC_DYNAMIC_MINOR,.name = DEV_NAME,.fops = &fops
};static unsigned int arg = 100;static int __init key1_init(void)
{int ret = misc_register(&misc);if(ret < 0)goto err_misc_register;ret = request_irq(IRQ_EINT8, eint8_handler, IRQF_TRIGGER_FALLING, "irq_eint8", &arg);if(ret <0)goto err_request_irq;init_waitqueue_head(&wq);INIT_WORK(&work,work_func);printk("key4_init   ....\n");return ret;err_request_irq:disable_irq(IRQ_EINT8);free_irq(IRQ_EINT8,&arg);printk("request_irq failed\n");return ret;err_misc_register:misc_deregister(&misc);printk("misc_register  faikey\n");return ret;	
}static void __exit key1_exit(void)
{disable_irq(IRQ_EINT8);free_irq(IRQ_EINT8,&arg);misc_deregister(&misc);printk("key4_exit   ....\n");
}module_init(key1_init);
module_exit(key1_exit);
MODULE_LICENSE("GPL");
4.5两者区别
特性TaskletWorkqueue
执行上下文软中断(不可睡眠)内核线程(可睡眠)
互斥性同一 tasklet 永远不会并行同一个 work 不会并行,不同 work 可并行
并发性多个 tasklet 可在不同 CPU 并发多个 work 可在多个 worker 线程并发
调度粒度基于 softirq,立即调度基于调度器,可延迟
灵活性简单、轻量丰富(延迟、优先级、CPU 绑定)
典型用途中断下半部、快速轻量任务复杂/耗时任务,需要睡眠的场景
上下文软中断(softirq)上下文,属于 中断上下文运行在内核线程(kworker)上下文,属于 进程上下文
http://www.xdnf.cn/news/1382005.html

相关文章:

  • 中国剩余定理(以及扩展..)
  • .Net Core Web 架构(管道机制)的底层实现
  • [光学原理与应用-321]:皮秒深紫外激光器产品不同阶段使用的工具软件、对应的输出文件
  • 【黑客技术零基础入门】2025最新黑客工具软件大全,零基础入门到精通,收藏这篇就够了!
  • JAVA全栈Redis篇————List常用命令讲解
  • 【架构师干货】软件工程
  • Linux学习-TCP并发服务器构建(epoll)
  • Cesium 入门教程(十一):Camera相机功能展示
  • Burp系列【密码暴力破解+令牌token破解】
  • 深度学习篇---VGGNet网络结构
  • DeepInteraction++基于多模态交互的自动驾驶感知与规划框架
  • 【iOS】Masnory自动布局的简单学习
  • Linux(二) | 文件基本属性与链接扩展
  • Spring Security 深度学习(二): 自定义认证机制与用户管理
  • npm install --global @dcloudio/uni-cli 时安装失败
  • 一天认识一个神经网络之--CNN卷积神经网络
  • QT之双缓冲 (QMutex/QWaitCondition)——读写分离
  • LINUX ---网络编程(三)
  • 如何通过docker进行本地部署?
  • 机器学习回顾(二)——KNN算法
  • Day16_【机器学习概述】
  • 设计模式:组合模式(Composite Pattern)
  • 【数据结构与算法】LeetCode 20.有效的括号
  • Vue 组件循环 简单应用及使用要点
  • 微服务保护和分布式事务-01.雪崩问题-原因分析
  • 步进电机、直流电机常见问题
  • APP手游使用游戏盾SDK为何能有效抵御各类攻击?
  • Java全栈工程师的实战面试:从基础到微服务的全面解析
  • 算法 --- 二分
  • Paimon——官网阅读:非主键表