linux 基础知识点之工作队列workqueue

        多年前就了解了workqueue着玩意,但理解上就并不是很很深刻,今天重新梳理一下,本文重点的是哪个些现成的demo代码,都是可以直接拿来用的,这就是写这文章的目的和作用,就是为了备份后续工作用到的时候,可以快速Ctrl+c and Ctrl + v,接下来复制些前辈写好的理解概念,再结合一下个所整理得demo。

一、workqueue工作队列简介

        工作队列是除软中断和tasklet以外最常用的一种下半部机制,其基本原理是:把work(需要推迟执行的函数)交由一个内核线程来执行,工作队列总是在进程上下文执行。                               工作队列 ( workqueue )是将操作(或回调)延期异步执行的一种机制。工作队列可以把工作推后,交由一个内核线程去执行,并且工作队列是执行在线程上下文中,因此工作执行过程中可以被重新调度、抢占、睡眠。

​ 工作队列的优点:

    因工作队列在进程下文中执行,因此工作队列允许重新调度和睡眠,是异步执行的进程上下文。
    解决了如果软中断和tasklet执行时间过长会导致系统实时性下降等问题。

(1-1)work_struct工作

在 Linux 内核中,work_struct 是工作队列(workqueue)机制的核心数据结构,用于表示一个工作项。工作队列允许内核将任务推迟到其他线程中执行,这在处理中断或其他需要快速响应的场景中非常有用

struct my_work {struct work_struct work;int data;
};定义一个包含 work_struct 的结构体,用于存储工作项的数据

linux内核中使用work_struct结构体来表示一个工作,如下定义(/inlcude/linux/workqueue.h):

struct work_struct {atomic_long_t data;struct list_head entry;work_func_t func;
#ifdef CONFIG_LOCKDEPstruct lockdep_map lockdep_map;
#endif
};

(1-2)workqueue工作队列

把工作(包括该工作任务的执行回调函数)添加到一个队列,称为workqueue,即工作队列,然后通过worker-pool中的内核工作线程(worker)去执行这个回调函数。工作队列使用workqueue_struct结构体来表示,定义如下(/kernel/workqueue.c):

    创建工作队列:my_workqueue = create_workqueue("my_workqueue");使用 create_workqueue 创建一个工作队列,名称为 "my_workqueue"


(1-3)worker工作者线程

​ linux 内核使用工作者线程(worker thread)来处理工作队列中的各个工作,linux 内核使用
worker 结构体表示工作者线程,worker 结构体定义如下(/kernel/workqueue_internal.h)
  
二、workqueue工作队列的使用

​ 每一个worker工作线程中都有一个工作队列,工作线程处理自己工作队列中的所有工作。

在实际开发中,推荐使用默认的workqueue·工作队列,而不是新创建workqueue。使用方法如下:

​ 直接定义一个work_struct结构体变量,然后使用INIT_WORK宏来完成初始化工作,INIT_WORK定义如下:

#define INIT_WORK(_work, _func)

_work表示要初始化的工作,_func是工作对应的处理函数。

也可以使用 DECLARE_WORK 宏一次性完成工作的创建和初始化,宏定义如下:

#define DECLARE_WORK(n, f)

​ n 表示定义的工作(work_struct),f 表示工作对应的处理函数。和 tasklet 一样,工作也是需要调度才能运行的,工作的调度函数为schedule_work(),函数原型如下所示:

bool schedule_work(struct work_struct *work) //其实你就理解为工作队列启动回调的函数

使用cancel_work_sync()取消一个工作,函数原型如下所示:

bool cancel_work_sync(struct work_struct *work)

当然也可以自己创建一个workqueue,特别是网络子系统、块设备子系统情况下等。具体步骤如下:

    使用alloc_workqueue()创建新的workqueue。
    使用INIT_WORK()宏声明一个work和该work的回调函数。
    使用queue_work()在新的workqueue上调度一个work //其实就理解为工作队列启动回调的函数
    使用flush_workqueue()去flush 工作队列上的所有work。

来个demo:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/time.h>#include <linux/jiffies.h>
#include <linux/workqueue.h>
#include <linux/slab.h> //kmalloc kfree#include <linux/sched.h>
#include <linux/delay.h>#define WAIT_TIMES 5000static char data[] = "[Tab] test for demo work";struct work_ctx{struct work_struct real_work;char *str;int arg;
}work_ctx;struct work_ctx *demo_work;// 定义工作队列
static struct workqueue_struct *my_workqueue;static void demo_work_func(struct work_struct *work){struct work_ctx *temp_work = container_of(work,struct work_ctx,real_work);printk(KERN_INFO "[work]=> PID: %d; NAME: %s\n", current->pid, current->comm);printk(KERN_INFO "[work]=> sleep 1 seconds\n");set_current_state(TASK_INTERRUPTIBLE);schedule_timeout(1 * HZ); //Wait 1 secondsprintk(KERN_INFO "[work]=> data is: %d  %s\n", temp_work->arg,temp_work->str);
}static int __init demo_thread_init(void){int count = 10;// 创建工作队列my_workqueue = create_workqueue("my_workqueue");if (!my_workqueue) {return -ENOMEM;}    demo_work = kmalloc(sizeof(*demo_work),GFP_KERNEL);	INIT_WORK(&demo_work->real_work, demo_work_func);demo_work->str = data;while(count--){//msleep(5000);msleep(WAIT_TIMES);//mdelay(WAIT_TIMES);demo_work->arg = count;//schedule_work(&demo_work->real_work);// 提交工作项到工作队列//queue_work(my_workqueue, &my_work->work);        queue_work(my_workqueue, &demo_work->real_work);}return 0;
}module_init(demo_thread_init);static void __exit demo_thread_exit(void){flush_work(&demo_work->real_work);kfree(demo_work);
}
module_exit(demo_thread_exit);MODULE_LICENSE("GPL");	

除此之外,linux内核还提供了一个workqueue机制与timer机制相结合的延时机制—delayed_work。
Talk is cheap,show me the code

结合hrtimer_demo升级了一下:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>#include <linux/time.h>#include <linux/jiffies.h>
#include <linux/workqueue.h>
#include <linux/slab.h> //kmalloc kfree#include <linux/sched.h>
#include <linux/delay.h>static struct hrtimer my_timer;
static int count = 0;#define WAIT_TIMES 5000static char data[] = "[Tab] test for demo work";struct work_ctx{struct work_struct real_work;char *str;int arg;
}work_ctx;struct work_ctx *demo_work;// 定义工作队列
static struct workqueue_struct *my_workqueue;static void demo_work_func(struct work_struct *work){struct work_ctx *temp_work = container_of(work,struct work_ctx,real_work);printk(KERN_INFO "[work]=> PID: %d; NAME: %s\n", current->pid, current->comm);printk(KERN_INFO "[work]=> sleep 1 seconds\n");set_current_state(TASK_INTERRUPTIBLE);schedule_timeout(1 * HZ); //Wait 1 secondsprintk(KERN_INFO "[work]=> data is: %d  %s\n", temp_work->arg,temp_work->str);
}#if 1
enum hrtimer_restart my_timer_callback(struct hrtimer *timer)
{count++;//pr_info("Timer expired! Count: %d\n", count);printk("enter [%s] line = %d, count = %d. \n", __func__, __LINE__, count);demo_work->arg = count;schedule_work(&demo_work->real_work);// 提交工作项到工作队列   //queue_work(my_workqueue, &demo_work->real_work); // 重新启动定时器,设置下次到期时间为 1 秒hrtimer_forward_now(timer, ms_to_ktime(2000));//调整高分辨率定时器(high-resolution timer,hrtimer)到期时间的一个函数return HRTIMER_RESTART; // 代表重复定时器 //重启方式
}
#elseenum hrtimer_restart my_timer_callback(struct hrtimer *timer)
{count++;//pr_info("Timer expired! Count: %d\n", count);printk("enter [%s] line = %d, count = %d. \n", __func__, __LINE__, count);// 重新启动定时器,设置下次到期时间为 1 秒hrtimer_forward_now(timer, ms_to_ktime(2000));//调整高分辨率定时器(high-resolution timer,hrtimer)到期时间的一个函数return HRTIMER_RESTART; // 代表重复定时器 //重启方式
}enum hrtimer_restart my_timer_callback(struct hrtimer *timer)
{pr_info(KERN_INFO "High-resolution timer expired after 1 second\n");printk("enter [%s] line = %d, count = %d. \n", __func__, __LINE__, count);return HRTIMER_NORESTART; // 代表不重复定时器
}
#endifstatic int __init my_module_init(void)
{ktime_t ktime;printk("enter [%s] line = %d \n", __func__, __LINE__);pr_info("High-Resolution Timer Example Module Loaded\n");// 创建工作队列my_workqueue = create_workqueue("my_workqueue");if (!my_workqueue) {return -ENOMEM;}    demo_work = kmalloc(sizeof(*demo_work),GFP_KERNEL);	INIT_WORK(&demo_work->real_work, demo_work_func);demo_work->str = data;// 设置定时器初始时间为 1 秒ktime = ktime_set(10, 0); // 1 秒, 0 纳秒hrtimer_init(&my_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);//初始化高分辨率定时器的函数my_timer.function = my_timer_callback;// 定时到期时的回调函数hrtimer_start(&my_timer, ktime, HRTIMER_MODE_REL);//启动高分辨率定时器的函数return 0;
}static void __exit my_module_exit(void)
{// 停止定时器hrtimer_cancel(&my_timer);pr_info("High-Resolution Timer Example Module Unloaded\n");
}module_init(my_module_init);
module_exit(my_module_exit);MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("High-Resolution Timer Example");
MODULE_AUTHOR("Pan Shuai");


                        原文链接:https://blog.csdn.net/iriczhao/article/details/122870227

 

Linux内核中的工作队列包括:共享工作队列和自定义工作队列。区别如下:

    1)共享工作队列:将新创建的工作任务添加到Linux内核创建的全局工作队列system_wq中,无需自己创建工作队列;

    2)自定义工作队列:将新创建的工作任务添加到自己创建的工作队列中;

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/kobject.h>
#include <linux/list.h>
#include <linux/kthread.h>
#include <asm/ptrace.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/interrupt.h>int data  = 10;static struct work_struct       work1;
static struct work_struct       work2;static void do_work1(struct work_struct *arg)
{printk(KERN_INFO "do_work1 .....");
}static void do_work2(struct work_struct *arg)
{printk(KERN_INFO "do_work2 .....");
}int threadfn(void *data)
{static int count =  0 ;int args = *(int *)data;printk(KERN_INFO "enter thead_fn");while(1){msleep(2*1000);printk(KERN_INFO "threadfn data: %d, count: %d",args , ++count);schedule_work(&work1);schedule_work(&work2);}
} static int __init test_kobj_init(void)
{INIT_WORK(&work1,do_work1);INIT_WORK(&work2,do_work2);struct task_struct *  thread =  kthread_create( threadfn,(void * )&data,"mythread");if(thread != NULL){printk(KERN_INFO "thread create success");wake_up_process(thread);}else{printk(KERN_ERR "thread create err");}return 0;
}static void __exit test_kobj_exit(void)
{printk(KERN_INFO "test_kobj_exit ");return;
}module_init(test_kobj_init);
module_exit(test_kobj_exit);MODULE_AUTHOR("copy other auther");
MODULE_LICENSE("GPL");

2、自定义工作队列举例(demol来源于网络)

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/kobject.h>
#include <linux/list.h>
#include <linux/kthread.h>
#include <asm/ptrace.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
int data  = 10;static struct workqueue_struct *workqueue;
static struct work_struct       work1;
static struct work_struct       work2;static void do_work1(struct work_struct *arg)
{printk(KERN_INFO "do_work1 .....");
}static void do_work2(struct work_struct *arg)
{printk(KERN_INFO "do_work2 .....");
}int threadfn(void *data)
{static int count =  0 ;int args = *(int *)data;printk(KERN_INFO "enter thead_fn");while(1){msleep(2*1000);printk(KERN_INFO "threadfn data: %d, count: %d",args , ++count);queue_work(workqueue,&work1);//将任务放到自己创建的工作队列上去执行queue_work(workqueue,&work2);}
} static int __init test_kobj_init(void)
{workqueue = create_workqueue("create_new_work_queue");INIT_WORK(&work1,do_work1);INIT_WORK(&work2,do_work2);struct task_struct *  thread =  kthread_create( threadfn,(void * )&data,"mythread");if(thread != NULL){printk(KERN_INFO "thread create success");wake_up_process(thread);}else{printk(KERN_ERR "thread create err");}return 0;
}static void __exit test_kobj_exit(void)
{printk(KERN_INFO "test_kobj_exit ");destroy_workqueue(workqueue);return;
}module_init(test_kobj_init);
module_exit(test_kobj_exit);MODULE_AUTHOR("copy other auther");
MODULE_LICENSE("GPL");

在使用如下函数时注意事项


    1、flush_work():堵塞工作任务,直到工作任务完成

    2、flush_delayed_work():等待延时工作任务完成

    3、cancel_work_sync():取消工作任务并等待它完成

    4、cancel_delayed_work():取消延时工作任务

    5、cancel_delayed_work_sync():取消延时工作任务并等待它完成

    6、create_workqueue():对于多CPU系统,内核会在每个CPU上创建一个工作队列,使线程处理并行化

    7、create_singlethread_workqueue():内核只在一个CPU上创建一个工作队列

    8、queue_work_on():在指定CPU上添加工作任务,queue_work()调用queue_work_on()在所有CPU上添加工作任务

常见的应用场景:


work_struct 是 Linux 内核中用于处理异步任务的机制,它允许内核将任务推迟到工作队列中,由工作线程在适当的时候执行。work_struct 可以处理多种类型的任务,以下是一些常见的应用场景:
1. 中断处理
中断处理程序需要快速完成,以避免过长时间占用 CPU。如果中断处理程序需要执行一些耗时的操作,可以将这些操作推迟到工作队列中执行。例如:

static void my_interrupt_handler(int irq, void *dev_id) {struct work_struct *work = (struct work_struct *)dev_id;queue_work(my_workqueue, work);
}

2. 定时任务
如果需要在特定时间后执行某些任务,可以使用 queue_delayed_work 将任务推迟到工作队列中。例如:

static void my_delayed_work_handler(struct work_struct *work) {printk(KERN_INFO "Delayed work handler called\n");
}static int __init my_module_init(void) {my_workqueue = create_workqueue("my_workqueue");if (!my_workqueue) {return -ENOMEM;}struct my_work *my_work = kmalloc(sizeof(struct my_work), GFP_KERNEL);if (!my_work) {return -ENOMEM;}INIT_DELAYED_WORK(&my_work->work, my_delayed_work_handler);queue_delayed_work(my_workqueue, &my_work->work, msecs_to_jiffies(1000)); // 延迟 1 秒return 0;
}


3. 文件系统操作
在文件系统操作中,某些耗时的操作可以推迟到工作队列中执行。例如,文件系统的同步操作可以使用工作队列来处理:


static void my_sync_handler(struct work_struct *work) {printk(KERN_INFO "File system sync handler called\n");// 执行文件系统同步操作
}static int __init my_module_init(void) {my_workqueue = create_workqueue("my_workqueue");if (!my_workqueue) {return -ENOMEM;}struct my_work *my_work = kmalloc(sizeof(struct my_work), GFP_KERNEL);if (!my_work) {return -ENOMEM;}INIT_WORK(&my_work->work, my_sync_handler);queue_work(my_workqueue, &my_work->work);return 0;
}

4. 设备驱动程序
在设备驱动程序中,某些操作(如硬件初始化、数据传输等)可以推迟到工作队列中执行。例如:

static void my_device_init_handler(struct work_struct *work) {printk(KERN_INFO "Device initialization handler called\n");// 执行设备初始化操作
}static int __init my_module_init(void) {my_workqueue = create_workqueue("my_workqueue");if (!my_workqueue) {return -ENOMEM;}struct my_work *my_work = kmalloc(sizeof(struct my_work), GFP_KERNEL);if (!my_work) {return -ENOMEM;}INIT_WORK(&my_work->work, my_device_init_handler);queue_work(my_workqueue, &my_work->work);return 0;
}

5. 网络协议栈
在网络协议栈中,某些操作(如数据包处理、连接管理等)可以推迟到工作队列中执行。例如:

static void my_packet_handler(struct work_struct *work) {printk(KERN_INFO "Packet handler called\n");// 处理数据包
}static int __init my_module_init(void) {my_workqueue = create_workqueue("my_workqueue");if (!my_workqueue) {return -ENOMEM;}struct my_work *my_work = kmalloc(sizeof(struct my_work), GFP_KERNEL);if (!my_work) {return -ENOMEM;}INIT_WORK(&my_work->work, my_packet_handler);queue_work(my_workqueue, &my_work->work);return 0;
}

6. 用户空间请求
在处理用户空间请求时,某些操作可以推迟到工作队列中执行。例如:

static void my_user_request_handler(struct work_struct *work) {printk(KERN_INFO "User request handler called\n");// 处理用户请求
}static int __init my_module_init(void) {my_workqueue = create_workqueue("my_workqueue");if (!my_workqueue) {return -ENOMEM;}struct my_work *my_work = kmalloc(sizeof(struct my_work), GFP_KERNEL);if (!my_work) {return -ENOMEM;}INIT_WORK(&my_work->work, my_user_request_handler);queue_work(my_workqueue, &my_work->work);return 0;
}

注意事项

    工作函数的执行环境:工作函数在工作线程中执行,允许睡眠,因此可以执行耗时操作。
    工作队列的销毁:在模块退出时,确保使用 flush_workqueue 等待所有工作完成,然后使用 destroy_workqueue 销毁工作队列。
    工作项的释放:在工作函数中,确保释放分配的工作项,避免内存泄漏。

通过这些示例,你可以看到 work_struct 可以处理多种类型的任务,包括中断处理、定时任务、文件系统操作、设备驱动程序、网络协议栈和用户空间请求等。这使得 work_struct 成为内核中处理异步任务的强大工具。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/69506.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【CubeMX+STM32】SD卡 文件系统读写 FatFs+SDIO+DMA

本篇&#xff0c;将使用CubeMXKeil&#xff0c;创建一个SD卡的 FatFSSDIODMA 文件系统读写工程。 目录 一、简述 二、CubeMX 配置 FatFSSDIO DMA 三、Keil 编辑代码 四、实验效果 实现效果&#xff0c;如下图&#xff1a; 一、简述 上两篇&#xff0c;已循序渐进讲解了SD、…

docker环境下部署face-search开源人脸识别模型

由于我们是直接将face-search部署在docker容器中的,所以,在部署之前一定要检查一下自己的docker环境,要不然部署过程中会出现各种各样的问题 我这里的docker环境是 一、安装docker环境 如果docker版本比较低或者docker-compose的版本比较低的情况下,部署的时候docker的yml…

【Android开发AI实战】选择目标跟踪基于opencv实现——运动跟踪

文章目录 【Android 开发 AI 实战】选择目标跟踪基于 opencv 实现 —— 运动跟踪一、引言二、Android 开发与 AI 的融合趋势三、OpenCV 简介四、运动跟踪原理&#xff08;一&#xff09;光流法&#xff08;二&#xff09;卡尔曼滤波&#xff08;三&#xff09;粒子滤波 五、基于…

消费电子产品中的噪声对TPS54202的影响

本文章是笔者整理的备忘笔记。希望在帮助自己温习避免遗忘的同时&#xff0c;也能帮助其他需要参考的朋友。如有谬误&#xff0c;欢迎大家进行指正。 一、概述 在白色家电领域&#xff0c;降压转换器的应用非常广泛&#xff0c;为了实现不同的功能就需要不同的电源轨。TPS542…

5、大模型的记忆与缓存

文章目录 本节内容介绍记忆Mem0使用 mem0 实现长期记忆 缓存LangChain 中的缓存语义缓存 本节内容介绍 本节主要介绍大模型的缓存思路&#xff0c;通过使用常见的缓存技术&#xff0c;降低大模型的回复速度&#xff0c;下面介绍的是使用redis和mem0&#xff0c;当然redis的语义…

用 Python 给 Excel 表格截图(20250207)

我搜索了网络上的方案&#xff0c;感觉把 Excel 表格转换为 HTML 再用 platwright 截图是比较顺畅的路径&#xff0c;因为有顺畅的工具链。如果使用的是 Windows 系统则不需要阅读此文&#xff0c;因为 win32com 库更方便。这篇文章中 Excel 转 HTML 的方案&#xff0c;主要弥补…

ZooKeeper Watcher 机制详解:从注册到回调的全过程

引言 在分布式系统中&#xff0c;数据的实时性和一致性是至关重要的。ZooKeeper 通过其 Watcher 机制提供了一种高效的方式来监听数据变化或事件&#xff0c;从而使客户端能够在数据发生变化时立即收到通知。本文将深入探讨 ZooKeeper 的 Watcher 机制&#xff0c;具体包括客户…

继承QLineEdit类实现自动补全功能

QlineEdit类本身是没有自动补全功能的&#xff0c;可以使用QCompleter配合实现功能。 但是在开发过程中发现&#xff0c;输入的字符串如果匹配那么QCompleter类会弹窗显示匹配项&#xff0c;如果输入的字符串不匹配则QCompleter类会关闭弹出(这点我也倒是能理解&#xff0c;没有…

从量化投资到AI大模型:DeepSeek创始人梁文锋的创新之路

一、学术的启蒙:学霸的崭露头角 梁文锋的成长故事始于1985年,他出生在广东省湛江市的一个普通家庭。从小,梁文锋就展现出对知识的强烈渴望和非凡的学习能力,尤其在数学领域,他总是能够轻松解决复杂的难题,成为学校里备受瞩目的“学霸”。 2002年,年仅17岁的梁文锋以吴川…

【课程设计参考】迷宫小游戏 :基于 Python+Pygame+AI算法

一、内容 实现走迷宫 &#xff08;1&#xff09;游戏界面显示&#xff1a;迷宫地图、上下左右移动的特效。 &#xff08;2&#xff09;动作选择&#xff1a;上下左右键对应于上下左右的移动功能&#xff0c;遇到障碍的处理。 &#xff08;3&#xff09;得分统计功能&#xff…

redis高级数据结构Stream

文章目录 背景stream概述消息 ID消息内容常见操作独立消费创建消费组消费 Stream弊端Stream 消息太多怎么办?消息如果忘记 ACK 会怎样?PEL 如何避免消息丢失?分区 Partition Stream 的高可用总结 背景 为了解决list作为消息队列是无法支持消息多播问题&#xff0c;Redis5.0…

在stm32mp257的yocto中设置用户名和密码

在STM32MP257的Yocto环境中设置用户名和密码,通常需要修改根文件系统的用户配置。以下是详细步骤: 1. 设置root密码 默认情况下,root账户可能无密码或使用默认密码。通过以下方法修改: 方法1:在local.conf中直接设置 # 打开Yocto工程的配置文件 vi conf/local.conf# 添…

4.3 注入sidecar的mutatePod注入函数编写

本节重点总结 : serveMutate编写 准入控制请求参数校验根据annotation标签判断是否需要注入sidecarmutatePod 注入函数编写生成注入容器和volume的patch函数 serveMutate编写 普通校验请求 serveMutate方法body是否为空req header的Content-Type 是否为application/json v…

win10向windows server服务器传输文件

win10向windows server服务器传输文件 遇到无法直接拖动文件进行传输时 解决方案&#xff1a; 1.点击显示选项 2.点击本地资源-详细信息 3.在窗口中选择你需要共享的磁盘 4.然后远程连接到Windows server服务器 5.登录Windows server服务器后&#xff0c;在此电脑下就能看…

为AI聊天工具添加一个知识系统 之93 详细设计之34 Derivation 之 8 实现和平台

本文要点 要点 插入话题&#xff1a;实现 “实现”作为一个普通名词&#xff08;一般术语&#xff09;应该遵循第一性第二性第三性原则。其 第一性第二性第三性 分别是&#xff1a;完整性/鲁棒性/健壮性 &#xff0c;三者 分别注重 性能/功能/能力。即 首先是 实现完整性的性…

ASP.NET Core SignalR的协议协商

SignalR支持多种服务器推送方式&#xff1a;Websocket、Server-Sent Events、长轮询。默认按顺序尝试。F12查看协商过程。websocket和HTTP是不同的协议&#xff0c;为什么能用同一个端口。在【开发人员工具】的【网络】页签中看WebSocket通信过程。 协议协商问题 集群中协议协…

Sinusoidal(正弦曲线)位置编码公式详细推导过程

Sinusoidal(正弦曲线)位置编码公式推导 参考链接 Transformer升级之路&#xff1a;1、Sinusoidal位置编码追根溯源 1. 前置数学的基本概念 1.1 内积 定义&#xff1a; 内积是两个向量之间的一种运算&#xff0c;其结果为一个标量。公式&#xff1a; 对于向量 a [ a 1 , …

仿 RabbitMQ 实现的简易消息队列

文章目录 项目介绍开放环境第三⽅库介绍ProtobufMuduo库 需求分析核⼼概念实现内容 消息队列系统整体框架服务端模块数据管理模块虚拟机数据管理模块交换路由模块消费者管理模块信道&#xff08;通信通道&#xff09;管理模块连接管理模块 客户端模块 公共模块日志类其他工具类…

GOland的context的使用

超时控制 在 HTTP 请求、数据库查询或 RPC 调用等操作中&#xff0c;防止请求长时间阻塞。 package mainimport ("context""fmt""time" )func main() {// 设置 2 秒超时ctx, cancel : context.WithTimeout(context.Background(), 2*time.Secon…

openssl使用

openssl使用 提取密钥对 数字证书pfx包含公钥和私钥&#xff0c;而cer证书只包含公钥。提取需输入证书保护密码 openssl pkcs12 -in xxx.pfx -nocerts -nodes -out pare.key提取私钥 openssl rsa -in pare.key -out pri.key提取公钥 openssl rsa -in pare.key -pubout -ou…