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

烟台网站关键字优化微信第三方平台

烟台网站关键字优化,微信第三方平台,企业网站建设平台的功能,苏州网站建设 网络推广公司本文代码在哪个基础上修改而成#xff1f; 本文是在博文 https://blog.csdn.net/wenhao_ir/article/details/145228617 的代码基础上修改而成。 关于工作队列(Workqueue)的概念 工作队列(Workqueue)可以用于实现Linux的中断下半部的#xff0c;之前在博文 https://blog.cs…本文代码在哪个基础上修改而成 本文是在博文 https://blog.csdn.net/wenhao_ir/article/details/145228617 的代码基础上修改而成。 关于工作队列(Workqueue)的概念 工作队列(Workqueue)可以用于实现Linux的中断下半部的之前在博文 https://blog.csdn.net/wenhao_ir/article/details/145309140 中已经介绍过中断上半部和中断下半部的概念。 它和软中断(SoftIRQ)、任务队列(Tasklet)相比最大的不同是它是可以进入阻塞或休眠状态它允许调用会导致阻塞或休眠的函数比如msleep、mutex_lock、schedule 等函数。 当然在三者中工作队列(Workqueue)的优先级相对来说是最低的。 本文利用工作队列(Workqueue)实现中断下半部的思路是在硬中断中将任务加入到处理工作队列(Workqueue)的内核线程中然后由这个内核线程去调度这个任务的执行。 完整源代码 驱动程序gpio_key_drv.c中的代码 #include linux/module.h#include linux/fs.h #include linux/errno.h #include linux/miscdevice.h #include linux/kernel.h #include linux/major.h #include linux/mutex.h #include linux/proc_fs.h #include linux/seq_file.h #include linux/stat.h #include linux/init.h #include linux/device.h #include linux/tty.h #include linux/kmod.h #include linux/gfp.h #include linux/gpio/consumer.h #include linux/platform_device.h #include linux/of_gpio.h #include linux/of_irq.h #include linux/interrupt.h #include linux/irq.h #include linux/slab.h #include linux/workqueue.h #include asm/current.h #include linux/delay.hstruct gpio_key{int gpio;struct gpio_desc *gpiod;int flag;int irq;struct work_struct work; } ;static struct gpio_key *gpio_keys_100ask;/* 主设备号 */ static int major 0; static struct class *gpio_key_class;static int g_key 0;static DECLARE_WAIT_QUEUE_HEAD(gpio_key_wait);/* 环形缓冲区 */ #define BUF_LEN 128 static int g_keys[BUF_LEN]; static int r, w;#define NEXT_POS(x) ((x1) % BUF_LEN)static int is_key_buf_empty(void) {return (r w); }static int is_key_buf_full(void) {return (r NEXT_POS(w)); }static void put_key(int key_value) {if (!is_key_buf_full()){g_keys[w] key_value;w NEXT_POS(w);} }static int get_key(void) {int key_value 0;if (!is_key_buf_empty()){key_value g_keys[r];r NEXT_POS(r);}return key_value; }/* 实现文件操作结构体中的read函数 */ static ssize_t gpio_key_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset) {//printk(%s %s line %d\n, __FILE__, __FUNCTION__, __LINE__);int err;int key_value;wait_event_interruptible(gpio_key_wait, !is_key_buf_empty());//从缓形缓冲区中取出数据key_value get_key();err copy_to_user(buf, key_value, 4);// 返回值为4表明读到了4字节的数据return 4; }/* 定义自己的file_operations结构体 */ static struct file_operations gpio_key_drv {.owner THIS_MODULE,.read gpio_key_drv_read, };static void key_work_func(struct work_struct *work) {struct gpio_key *gpio_key container_of(work, struct gpio_key, work);int val;val gpiod_get_value(gpio_key-gpiod);printk(The function keyw_ork_func is sleeping for 1000 milliseconds...\n);// 内核空间函数msleep可使线程休眠一段时间,单位为毫秒// 需要包含头文件 #include linux/delay.h// 在中断下半部中,只有工作队列(Workqueue)才能进行休眠操作msleep(1000);// g_key的高8位中存储的是GPIO口的编号低8位中存储的是按键按下时的逻辑值g_key (gpio_key-gpio 8) | val;//装按键值放入环形缓冲区put_key(g_key);wake_up_interruptible(gpio_key_wait);printk(key_work_func: the process is %s pid %d\n,current-comm, current-pid); // current-comm代表当前进程(线程)的名字 printk(key_work_func key %d %d\n, gpio_key-gpio, val); }static irqreturn_t gpio_key_isr(int irq, void *dev_id) {struct gpio_key *gpio_key dev_id;// 任务加入到内核kworker线程的工作队列上schedule_work(gpio_key-work);return IRQ_HANDLED; // 表示中断已处理 }/* 1. 从platform_device获得GPIO* 2. gpioirq* 3. request_irq*/ static int gpio_key_probe(struct platform_device *pdev) {int err;// 获取设备树节点指针struct device_node *node pdev-dev.of_node;// count用于存储设备树中描述的GPIO口的数量int count;int i;enum of_gpio_flags flag;unsigned flags GPIOF_IN;printk(%s %s line %d\n, __FILE__, __FUNCTION__, __LINE__);count of_gpio_count(node);if (!count){printk(%s %s line %d, there isnt any gpio available\n, __FILE__, __FUNCTION__, __LINE__);return -1;}gpio_keys_100ask kzalloc(sizeof(struct gpio_key) * count, GFP_KERNEL);if (!gpio_keys_100ask) {printk(Memory allocation failed for gpio_keys_100ask\n);return -ENOMEM;}for (i 0; i count; i){// 获取GIPO的全局编号及其标志位信息的代码gpio_keys_100ask[i].gpio of_get_gpio_flags(node, i, flag);if (gpio_keys_100ask[i].gpio 0){printk(%s %s line %d, of_get_gpio_flags fail\n, __FILE__, __FUNCTION__, __LINE__);return -1;}// 获取GPIO口的GPIO描述符的代码gpio_keys_100ask[i].gpiod gpio_to_desc(gpio_keys_100ask[i].gpio);if (!gpio_keys_100ask[i].gpiod) {printk(Failed to get GPIO descriptor for GPIO %d\n, gpio_keys_100ask[i].gpio);return -EINVAL;}// 结构体gpio_key的成员flag用于存储对应的GPIO口是否是低电平有效假如是低电平有效成员flag的值为1假如不是低电平有效成员flag的值为0。// 后续代码实际上并没有用到成员flag这里出现这句代码只是考虑到代码的可扩展性所以在这里是可以删除的。gpio_keys_100ask[i].flag flag OF_GPIO_ACTIVE_LOW;// 每次循环都重新初始化flagsflags GPIOF_IN;// 假如GPIO口是低电平有效,则把flags添加上低电平有效的信息if (flag OF_GPIO_ACTIVE_LOW)flags | GPIOF_ACTIVE_LOW;// 请求一个GPIO硬件资源与设备结构体pdev-dev进行绑定// 注意,这个绑定操作会在调用函数platform_driver_unregister()注销platform_driver时自动由内核解除绑定操作,所以gpio_key_remove函数中不需要显示去解除绑定// 由devm开头的函数通常都会内核自动管理资源咱们在退出函数中不用人为的去释放资源或解除绑定。err devm_gpio_request_one(pdev-dev, gpio_keys_100ask[i].gpio, flags, NULL);// 获取GPIO口的中断请求号gpio_keys_100ask[i].irq gpio_to_irq(gpio_keys_100ask[i].gpio);// 初始化工作队列(Workqueue)INIT_WORK(gpio_keys_100ask[i].work, key_work_func);}for (i 0; i count; i){char irq_name[32]; // 用于存储动态生成的中断名称//使用snprintf()函数将动态生成的中断名称写入irq_name数组snprintf(irq_name, sizeof(irq_name), swh_gpio_irq_%d, i); // 根据i生成名称//调用函数request_irq()来请求并设置一个中断err request_irq(gpio_keys_100ask[i].irq, gpio_key_isr, IRQF_TRIGGER_FALLING, irq_name, gpio_keys_100ask[i]);}/* 注册file_operations */major register_chrdev(0, swh_read_keys_major, gpio_key_drv); gpio_key_class class_create(THIS_MODULE, swh_read_keys_class);if (IS_ERR(gpio_key_class)) {printk(%s %s line %d\n, __FILE__, __FUNCTION__, __LINE__);unregister_chrdev(major, swh_read_keys_major);return PTR_ERR(gpio_key_class);}// 由于这里是把多个按键看成是一个设备,你可以想像一个键盘上对应多个按键,但键盘本身是一个设备,所以只有一个设备文件device_create(gpio_key_class, NULL, MKDEV(major, 0), NULL, read_keys0); /* /dev/read_keys0 */return 0;}static int gpio_key_remove(struct platform_device *pdev) {struct device_node *node pdev-dev.of_node;int count;int i;device_destroy(gpio_key_class, MKDEV(major, 0));class_destroy(gpio_key_class);unregister_chrdev(major, swh_read_keys_major);count of_gpio_count(node);for (i 0; i count; i) {// 只有在irq有效时才释放中断资源if (gpio_keys_100ask[i].irq 0) {// 释放GPIO中断资源,下面这句代码做了下面两件事// 1、解除 gpio_keys_100ask[i].irq 中断号和 gpio_key_isr 中断处理函数的绑定。// 2、解除 gpio_keys_100ask[i].irq 中断号和中断处理函数与 gpio_keys_100ask[i] 数据结构的绑定。free_irq(gpio_keys_100ask[i].irq, gpio_keys_100ask[i]);}// 释放GPIO描述符if (gpio_keys_100ask[i].gpiod) {gpiod_put(gpio_keys_100ask[i].gpiod);}}// 释放内存kfree(gpio_keys_100ask);return 0; }static const struct of_device_id irq_matach_table[] {{ .compatible swh-gpio_irq_key },{ }, };/* 1. 定义platform_driver */ static struct platform_driver gpio_keys_driver {.probe gpio_key_probe,.remove gpio_key_remove,.driver {.name swh_irq_platform_dirver,.of_match_table irq_matach_table,}, };/* 2. 在入口函数注册platform_driver */ static int __init gpio_key_init(void) {int err;printk(%s %s line %d\n, __FILE__, __FUNCTION__, __LINE__);err platform_driver_register(gpio_keys_driver); return err; }/* 3. 有入口函数就应该有出口函数卸载驱动程序时就会去调用这个出口函数* 卸载platform_driver*/ static void __exit gpio_key_exit(void) {printk(%s %s line %d\n, __FILE__, __FUNCTION__, __LINE__);platform_driver_unregister(gpio_keys_driver); }/* 7. 其他完善提供设备信息自动创建设备节点 */module_init(gpio_key_init); module_exit(gpio_key_exit);MODULE_LICENSE(GPL); 测试程序button_test.c中的代码 #include sys/types.h #include sys/stat.h #include fcntl.h #include unistd.h #include stdio.h #include string.h #include pthread.h #include stdlib.h #include time.h/** ./button_test /dev/100ask_button0**/// 打印线程的执行函数 void* print_while_waiting(void* arg) {while (1){printf(I am another thread, and while the main thread is waiting for a button to be pressed, I can still run normally.\n);sleep(10); // 每隔10秒打印一次}return NULL; }int main(int argc, char **argv) {int fd;int val;pthread_t print_thread;int keystroke 0; //记录按键次数/* 1. 判断参数 */if (argc ! 2) {printf(Usage: %s dev\n, argv[0]);return -1;}/* 2. 打开文件 */fd open(argv[1], O_RDWR);if (fd -1){printf(Can not open file %s\n, argv[1]);return -1;}// 创建一个线程每隔一段时间打印输出一条信息表示在等待按键期间另外的线程在继续正常执行。if (pthread_create(print_thread, NULL, print_while_waiting, NULL) ! 0){printf(Failed to create print thread\n);close(fd);return -1;}while (1){/* 3. 读文件 */read(fd, val, 4);/* 提取 GPIO 编号和逻辑值 */int gpio_number (val 8) 0xFF; // 高8位为 GPIO 编号int gpio_value val 0xFF; // 低8位为逻辑值keystroke;/* 打印读到的信息 */printf(GPIO Number: %d, Logical Value: %d\n, gpio_number, gpio_value);printf(keystrokes is %d\n, keystroke);}//pthread_join的作用是使主线程等待线程print_threa结束后再继续执行剩下的代码。//如果主线程在结束时未等待子线程完成可能会导致未完成的资源清理或意外的程序终止。//这里由于主线程中有个条件永远为真的while循环实际上这句代码没有实际作用。pthread_join(print_thread, NULL);close(fd);return 0; } 与工作队列(Workqueue)相关的代码解读 由于工作队列(Workqueue)还是属于中断下半部一种所以和前面的内核定时器(https://blog.csdn.net/wenhao_ir/article/details/145281064) 和 任务队列(Tasklet)(https://blog.csdn.net/wenhao_ir/article/details/145309140) 的使用基本相同。 首先还是为按键结构体 struct gpio_key添加一个类型为work_struct的成员。 为什么呢因为每一个GPIO口我们都要为其分配一个work_struct结构体。 然后在platform中的probe操作函数gpio_key_probe对每个GPIO口初始化时为每个GPIO口初始化一个work代码如下 INIT_WORK(gpio_keys_100ask[i].work, key_work_func);第1个参数就是每个按键对应的work_struct结构体的实例第2个参数是任务的回调函数。 肯定要问怎么不为回调函数传入数据答因为数据存储于gpio_keys_100ask[i]中我们可以通过gpio_keys_100ask[i].work反推出gpio_keys_100ask[i]的指针位置所以其实数据是已经传入了。这一点和新版的内核定时器是一样的详情见 https://blog.csdn.net/wenhao_ir/article/details/145281064 【搜索“注意-Linux_5.x以上对内核定时器进行了修改”】 然后我们在硬中断处理函数中把任务加入到系统内核的工作队列(Workqueue)线程中 static irqreturn_t gpio_key_isr(int irq, void *dev_id) {struct gpio_key *gpio_key dev_id;// 任务加入到内核kworker线程的工作队列上schedule_work(gpio_key-work);return IRQ_HANDLED; // 表示中断已处理 }代码很简单详细的描述略。 注意 schedule_work函数并不一定要运行于硬中断的处理函数中具体的情况本文后面有说明。 接下来就去看任务的回调函数key_work_func了代码如下 static void key_work_func(struct work_struct *work) {struct gpio_key *gpio_key container_of(work, struct gpio_key, work);int val;val gpiod_get_value(gpio_key-gpiod);printk(The function keyw_ork_func is sleeping for 1000 milliseconds...\n);// 内核空间函数msleep可使线程休眠一段时间,单位为毫秒// 需要包含头文件 #include linux/delay.h// 在中断下半部中,只有工作队列(Workqueue)才能进行休眠操作msleep(1000);// g_key的高8位中存储的是GPIO口的编号低8位中存储的是按键按下时的逻辑值g_key (gpio_key-gpio 8) | val;//装按键值放入环形缓冲区put_key(g_key);wake_up_interruptible(gpio_key_wait);printk(key_work_func: the process is %s pid %d\n,current-comm, current-pid); // current-comm代表当前进程(线程)的名字 printk(key_work_func key %d %d\n, gpio_key-gpio, val); }同样属于中断下半部工作队列(Workqueue)和软中断(SoftIRQ)、任务队列(Tasklet)相比最大的不同是它是可以进入阻塞或休眠状态它允许调用会导致阻塞或休眠的函数比如msleep、mutex_lock、schedule 等函数所以我们这里就利用内核心空间函数msleep使其休眠1000毫秒再运行从而看是不是真的可以进行休眠状态。 代码很简单没啥好说的只是代码末尾处还利用printk打印出了内核处理工作队列的线程的名字和进程号具体关于名字的分析在测试程序之后我有分析。 在本代码中的工作队列(Workqueue)不需要释放什么资源因为其占用的资源由内核处理工作队列(Workqueue)的线程和相关机制自动管理 所以不需要释放什么资源所以函数gpio_key_remove中不需要增添对工作队列(Workqueue)相关资源的释放。 至此与工作队列(Workqueue)相关的代码分析完毕。 工作队列(Workqueue)机制的缺点及解决方法 工作队列(Workqueue)的缺点 前面的队列任务阻塞时会影响后面的队列任务的执行因为它们相当于是挂在同一个内核心线程上的任务。 详细解释如下 schedule_work() 函数会将工作项work_struct 类型添加到内核中的一个 工作队列 上而这个工作队列会由内核管理的一个专用线程或多个线程来执行。 详细原理 工作队列的核心 工作队列Workqueue是 Linux 内核提供的一种机制用于在进程上下文中延迟执行任务。schedule_work() 是将工作项加入到 system_wq内核默认的全局工作队列中。 内核线程执行 内核为工作队列创建了一个专用的内核线程kworker通常命名为 kworker/CPU编号。当你调用 schedule_work() 时内核将你的工作项添加到队列中kworker 线程会取出并执行这些工作项。 为什么要这样设计 中断上下文无法进行阻塞或复杂操作但很多任务需要在进程上下文中运行。工作队列为这种需求提供了解决方案允许开发者在内核线程中运行需要延迟执行的任务同时允许这些任务休眠、阻塞或执行耗时操作。 schedule_work() 的执行过程 调用 schedule_work() 时 检查 work_struct 是否已在队列中防止重复排队。如果没有重复排队将它添加到全局工作队列system_wq。 内核线程kworker被唤醒 kworker 线程会检查它负责的队列是否有任务。如果有任务取出并调用工作项的处理函数由开发者定义。 运行你的工作项 调用你在 INIT_WORK() 中指定的处理函数。工作项处理完成后从队列中移除。 在队列任务的执行中如果前面的队列任务进入了阻塞状态就会影响后续队列任务的执行。这是因为 工作队列 中的任务是按照 FIFO先进先出顺序依次执行的而一个任务阻塞后kworker 线程会一直等待任务完成无法继续处理后续任务。 工作队列的运行机制 默认情况一个 kworker 线程 系统默认的全局工作队列system_wq使用共享的 kworker 线程。kworker 线程是单线程处理的一次只能运行一个任务。如果某个任务阻塞后续任务必须等待。 顺序处理的特点 如果工作队列中前面的任务 A 阻塞了kworker 会等待任务 A 完成再去处理任务 B。任务之间没有抢占关系因此阻塞会直接导致后续任务延迟执行。 如何避免阻塞影响其他任务 有几种方法可以解决这个问题 使用线程化的中断处理 如果是中断处理中使用工作队列(Workqueue)实现中断下半部的处理那么可以在注册中断的同时为这个中断注册一个属于这个中断的线程。详情见 https://blog.csdn.net/wenhao_ir/article/details/145326705 创建独立的工作队列 使用 alloc_workqueue() 创建一个专用的工作队列。每个工作队列会有独立的线程互不干扰。示例代码#include linux/workqueue.hstatic struct workqueue_struct *my_wq; static struct work_struct my_work;static void my_work_handler(struct work_struct *work) {msleep(5000); // 模拟阻塞操作printk(KERN_INFO Task finished\n); }static int __init my_init(void) {// 创建独立的工作队列my_wq alloc_workqueue(my_workqueue, WQ_UNBOUND, 0);if (!my_wq)return -ENOMEM;// 初始化并提交工作项到自定义工作队列INIT_WORK(my_work, my_work_handler);queue_work(my_wq, my_work);printk(KERN_INFO Work queued\n);return 0; }static void __exit my_exit(void) {// 销毁工作队列if (my_wq)destroy_workqueue(my_wq);printk(KERN_INFO Module exited\n); }module_init(my_init); module_exit(my_exit); MODULE_LICENSE(GPL);通过 alloc_workqueue() 创建的工作队列有独立的内核线程任务阻塞不会影响其他队列的任务。 使用 WQ_UNBOUND 属性 在创建工作队列时使用 WQ_UNBOUND 标志 允许工作项不绑定到特定的 CPU可以并发运行多个任务。内核会动态分配线程来处理这些任务从而减少任务阻塞的影响。 示例代码alloc_workqueue(my_workqueue, WQ_UNBOUND, 0);避免任务长时间阻塞 如果任务本身需要长时间阻塞可以考虑拆分任务将长时间的阻塞部分移到用户态完成或者异步处理如通过线程或其他机制。 总结 默认行为全局工作队列system_wq使用共享的 kworker 线程阻塞任务会影响后续任务的执行。解决办法 创建专用的工作队列独立线程。使用 WQ_UNBOUND 来增加并发能力。避免任务本身长时间阻塞。 通过这些方法可以有效避免阻塞任务影响整个工作队列的运行效率。 schedule_work函数并不一定要运行在硬件中断的处理函数中 schedule_work 函数并不一定要运行在硬件中断的处理函数中。它可以在任何可以运行内核代码的上下文中被调用具体包括以下场景 硬件中断处理函数中调用 硬件中断的处理函数通常要求执行迅速因此适合将复杂或耗时的任务推迟到中断下半部如工作队列中执行。在中断处理函数中调用 schedule_work将工作任务添加到工作队列中由内核的工作线程kworker在合适的时机处理。 内核线程或其他上下文中调用 schedule_work 可以在任何普通的内核上下文中调用比如 从设备驱动的 probe 或其他文件操作函数中。在定时器回调函数中。在内核模块的入口初始化函数module_init中。 无需限制在中断上下文中使用。 用户态系统调用触发的内核函数中 用户态程序触发的系统调用例如读写驱动设备中驱动程序可以调用 schedule_work 将任务推迟到工作队列中执行。这可以避免耗时任务阻塞用户态进程。 为什么不一定要在中断上下文中调用 设计目的 schedule_work 的作用是将任务加入到工作队列它本身的调用非常轻量可以在任何允许调用内核函数的上下文中使用。上下文限制 如果是中断上下文不能执行可能会阻塞的操作如 msleep 或 mutex_lock。通过 schedule_work可以将任务推迟到工作线程kworker中运行从而避开这些限制。如果是进程上下文则不存在中断上下文的限制可以直接使用。 注意事项 调用 schedule_work 后必须确保对应的 work_struct 已正确初始化通过 INIT_WORK 或 INIT_DELAYED_WORK。如果模块卸载时还有未完成的工作队列任务需要确保处理完毕或取消任务避免资源泄漏或非法访问。 总结 schedule_work 并不一定需要在硬件中断处理函数中调用。它可以在任何允许执行内核代码的上下文中调用目的是将任务加入工作队列交由 kworker 内核线程在合适的时间处理。这样做可以延迟执行复杂任务减少上下文的阻塞时间提高内核代码的效率和响应性。 设备树文件的修改和更新 和下面两篇博文一样 https://blog.csdn.net/wenhao_ir/article/details/145225508 https://blog.csdn.net/wenhao_ir/article/details/145176361 Makfile文件内容 # 使用不同的Linux内核时, 一定要修改KERN_DIRKERN_DIR代表已经配置、编译好的Linux源码的根目录KERN_DIR /home/book/100ask_imx6ull-sdk/Linux-4.9.88all:make -C $(KERN_DIR) Mpwd modules# 因为测试程序中有线程的创建,所以下面的语句需要添加 -lpthread 链接选项$(CROSS_COMPILE)gcc -o button_test_02 button_test.c -lpthread clean:make -C $(KERN_DIR) Mpwd cleanrm -rf modules.orderrm -f button_test_02obj-m gpio_key_drv.o 交叉编译出驱动模块和测试程序 源码复制到Ubuntu中。 make将交叉编译出的gpio_key_drv.ko和button_test_02复制到NFS文件目录中备用。 加载模块 打开串口终端→打开开发板→挂载网络文件系统 mount -t nfs -o nolock,vers3 192.168.5.11:/home/book/nfs_rootfs /mntinsmod /mnt/workqueue/gpio_key_drv.ko检查设备文件生成没有 ls /dev/有了 运行测试程序 先把内核printk打印的显示打开 echo 7 4 1 7 /proc/sys/kernel/printk然后 cd /mnt/workqueue./button_test_02 /dev/read_keys0从运行过程可以感知到工作队列所在的内核线程的确是休眠1000毫秒之后再继续运行的。在延迟1000毫秒之后后面的代码把按键值放入了环形缓冲区进面用户空间中的程序可以读取相应的按键值。 测试成功。 关于处理工作队列的内核线程的名字的详细解释 我们的程序在测试中还打印出了处理工作队列的内核线程的名字和进程号名字为kworker/0:1其所在进程号为1720我们可以用ps命令看一下 可见内核中确实存在着一个进程号为1720的内核线程在处理中断下半部的工作队列(Workqueue)。 这里我解释一下名字的kworker/0:1含义 进程名字 kworker/0:1 是 Linux 内核中 kworker内核工作线程 的标准命名格式。我们来逐个解析其含义 kworker 表示这是一个 内核工作线程Kernel Worker Thread。这些线程是由内核的 Workqueue工作队列 机制管理的用于处理一些延迟执行的任务或繁重的内核工作。 0 表示 CPU 的编号。这里的 0 指的是第 0 号 CPU也就是说这个 kworker 线程被绑定或调度在 CPU 0 上运行。 1 表示 线程的 ID也可以理解为线程在某个 CPU 上的序号。每个 CPU 可能会有多个 kworker 线程它们会被分配一个唯一的 ID 来区分。在本例中1 表示第一个线程。 完整解释 kworker/0:1 表示 这是一个内核工作线程。它被绑定或主要调度在 CPU 0 上运行。它是 CPU 0 上的第 1 号 kworker 线程。 补充说明 多核系统中的 kworker 在多核系统中每个 CPU 都可能有多个 kworker 线程。例如 kworker/1:0表示 CPU 1 上的第 0 号 kworker 线程。kworker/2:2表示 CPU 2 上的第 2 号 kworker 线程。 动态生成的 kworker 线程 kworker 线程的数量和命名不是固定的它们根据内核的工作负载动态生成和销毁。如果内核的某些任务需要延迟执行或者负载增加时会创建更多的 kworker 线程。 如何查看这些线程 使用 htop、top 或直接在 /proc 文件系统中可以看到这些 kworker 线程。比如运行以下命令可以列出所有 kworker 线程ps -e | grep kworker调试信息中的 kworker dmesg 或内核日志中经常会看到 kworker 线程参与的内核任务它们通常用来执行 硬件中断处理的延迟部分、文件系统同步、网络包处理 等。 总结 kworker/0:1 是内核的工作线程。0 是 CPU 编号表示线程绑定在 CPU 0 上。1 是线程序号表示 CPU 0 上的第 1 号工作线程。 卸载驱动程序模块 rmmod gpio_key_drv.ko运行上面命令后过了较长时间系统仍然能正常运行说明卸载没有问题。证明说明工作队列(Workqueue)的相关资源确实并不需要释放。原因就是其占用的资源由内核处理工作队列(Workqueue)的线程和相关机制自动管理 所以不需要释放什么资源。 附完整工程文件 https://pan.baidu.com/s/1b6Nysvb4zU9B1bNQNeh3rw?pwdcvjq
http://www.hkea.cn/news/14498329/

相关文章:

  • 西安网站建设g广州网站设计开发招聘
  • 公司长沙建站网站建设创意报告书
  • 有没有专门做衣服搭配的网站国外网站开发技术现状
  • 网站开发html书籍下载企业小程序建设的公司
  • 如何用phpstudy做网站牛肉煲的做法
  • 阿里云虚拟主机与网站吗影视自助建站官网
  • 教育培训网站抄袭个人网页网站制作模板
  • 新民专业网站开发公司网站怎么上传数据库
  • 营销建设网站上海网站备案在哪里查询
  • 网站域名备案查询官网网站怎么创建自己的网站
  • 网站改版流程百度推广要多少钱
  • 福州网站设计外包软件开发服务费用报销分录
  • 优秀网站模板下载网络工程技术适合女生吗
  • 网站扩展名网页制作成品网站
  • 扬州市做网站.net 网站开发视频教程
  • 东丽区做网站昆山网站优化公司
  • 旅游网站建设方网站代码是多少
  • 南京品牌网站设计百度销售系统登录
  • 济南免费网站建设优化江西省做网站
  • 租车网站 模板网络安全行业前景
  • 怎么做网站的wordpress对话框模板
  • 企业网站推广优化唐山业之峰装饰公司怎么样
  • 网站建设 书籍石家庄网络平台推广
  • PHP做克隆网站seo怎样优化网站
  • 企业建站做网站小白怎么做跨境电商
  • 秦皇岛企业网站建设网站开发需要多少钱如何
  • 北京网站建设公司网站优化linux wordpress 主题
  • 西安网站有哪些外链代发公司
  • 十大搞笑素材网站网页加速器免费下载
  • 合肥网站建设找佳达网站服务器和空间大小