Linux驅動開發-①中斷②阻塞、非阻塞IO和異步通知

Linux驅動開發-①中斷②阻塞、非阻塞IO和異步通知

  • 一,中斷
    • 1.中斷的流程
    • 2.上半部和下半部
      • 2.1上半部
      • 2.2下半部
        • 2.2.1 tasklet
        • 2.2.2 工作隊列
    • 3.按鍵延時消抖中斷程序
  • 二,阻塞和非阻塞IO和異步通知
    • 1.阻塞IO
      • 1.1 常見結構1
      • 1.2 常見結構2
    • 2.非阻塞IO
      • 2.1 驅動結構
      • 2.2 select 應用程序
      • 2.3 poll 應用程序
    • 3.異步通知

一,中斷

1.中斷的流程

??①獲取中斷號以及中斷名②申請中斷③釋放中斷

/*中斷結構體*/
struct irq_key{int key_gpio;     //io編號int irq;  //中斷號unsigned char gpio_name[6];//設置中斷名irqreturn_t (*handler)(int, void *);
};static irqreturn_t key_irq_hander(int irq, void *dev_id)//中斷處理函數,遇到上升沿或者下降沿觸發定時器{..............}/*中斷設置*/key.irq_struct.irq = gpio_to_irq(key.irq_struct.key_gpio);//獲取中斷號key.irq_struct.handler = key_irq_hander;//中斷函數表注冊,即中斷函數ret = request_irq(key.irq_struct.irq,key.irq_struct.handler,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, key.irq_struct.gpio_name, &key);/*釋放中斷*/free_irq(key.irq_struct.irq, &key);

2.上半部和下半部

2.1上半部

??上半部執行的是緊急的,不可延遲的任務。當中斷上半部執行時,當前CPU上的其他程序包括用戶空間程序和內核線程都會暫時阻塞,直到中斷處理完成
??不可搶占:在中斷上下文中,當前 CPU 會禁用搶占,直到中斷處理完成。不能睡眠:中斷上下文中不能調用可能睡眠的函數(如 kmalloc(GFP_KERNEL)、mutex_lock 等)。高優先級:中斷處理程序的優先級高于普通進程和內核線程。

2.2下半部

??下半部處理非緊急的,耗時的任務,將復雜的任務推遲到合適的時機,避免阻塞中斷上半部。可以運行在進程上下文中,睡眠或者調用睡眠的函數,能被其他中斷或者是任務搶占。用的tasklet(軟中斷,依舊不能睡眠,優先級依舊高,適用于網絡數據包處理,塊設備處理)和工作隊列(可以睡眠,比較耗時的任務,比如訪問系統文件,分配內存)。

2.2.1 tasklet

??tasklet跟工作隊列實際使用差不多,tasklet他的處理函數傳遞參數,工作隊列不傳遞。這個在初始化的時候也能發現,tasklet函數初始化需要三個參數,包括傳遞的設備參數結構體,工作隊列初始化只需要兩個參數。

struct tasklet_struct tasklet;
/*tasklet*/
static void tasklet_func(unsigned long data)//中斷下半部tasklet函數
{
........
}
static irqreturn_t key_irq_hander(int irq, void *dev_id)//中斷處理函數
{struct key_dev *key_irq = (struct key_dev*)dev_id;tasklet_schedule(&key_irq->irq_struct.tasklet);//調用中斷下半部return IRQ_HANDLED;
}/*初始化*/	
tasklet_init(&key.irq_struct.tasklet,tasklet_func, (unsigned long)&key);
2.2.2 工作隊列
 struct work_struct testwork; //工作隊列
/*工作隊列*/
void testwork_func_t(struct work_struct *work)//中斷下半部
{
........
}
static irqreturn_t key_irq_hander(int irq, void *dev_id)//中斷處理函數
{struct key_dev *key_irq = (struct key_dev*)dev_id;schedule_work(&key_irq->irq_struct.testwork);//工作隊列return IRQ_HANDLED;
}/*初始化*/	
INIT_WORK(&key.irq_struct.testwork,testwork_func_t);

3.按鍵延時消抖中斷程序

#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/fs.h>  
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/ioctl.h>
#include <linux/irq.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <linux/interrupt.h>
#include <linux/string.h>
#define KEY_NAME "key_irq"
#define KEY_EN_VALUE  0X01
#define KEY_NO_VALUE  0X00
static int key_pan=0;
/*中斷結構體*/
struct irq_key{int key_gpio;     //io編號int irq;  //中斷號unsigned char value; //按鍵值unsigned char gpio_name[6];//設置按鍵名irqreturn_t (*handler)(int, void *);struct tasklet_struct tasklet;//taskletstruct work_struct testwork; //工作隊列};
struct key_dev{struct class *class;struct device *device;struct device_node *nd;struct cdev cdev;dev_t key_hao;int major;  //主設備號int minor;  //次設備號struct irq_key irq_struct;/*中斷結構體*/int timer_period;//定時器周期  利用原子操作保護atomic_t lock_yuan;//原子操作struct timer_list timer;//定義定時器  
};
struct key_dev key;
static int pgkey_open(struct inode *innode,struct file *filp)
{filp->private_data = &key;//將key結構體數據設為私有數據return 0;
}static int pgkey_release(struct inode *innode,struct file *filp)
{return 0;
}
static ssize_t key_read(struct file *filp,  char __user *buf, size_t count, loff_t *ppos)
{static int key_value = 0,ret = 0;struct key_dev *key_read =(struct key_dev *)filp->private_data;key_value = key_read->irq_struct.value; if(key_pan)//按下{ret = __copy_to_user(buf, &key_value, sizeof(key_value));if(ret<0){printk("key value read error!\r\n");return -1;}        }key_pan = 0;return 0;}
static struct file_operations pgkey_fops={.owner=THIS_MODULE,.open=pgkey_open,.read=key_read,.release=pgkey_release,};
static void key_timer_function(unsigned long arg)//定時結束后會執行第操作,
{static int ret = 0;struct key_dev *key_t = (struct key_dev*)arg;ret = gpio_get_value(key_t->irq_struct.key_gpio);if(ret==0){printk("KEY PUSH\r\n");key_t->irq_struct.value = KEY_EN_VALUE;key_pan = 1;}else {printk("KEY PULL\r\n");key_t->irq_struct.value = KEY_NO_VALUE;}
}/*工作對列*/
void testwork_func_t(struct work_struct *work)//工作隊列和tasklet函數使用不同,ta這個函數通過本身傳遞參數,工作隊列不傳參數進來.
{// struct key_dev *key_work = (struct key_dev*)work;key.timer.data = (unsigned long)&key;mod_timer(&key.timer, jiffies+msecs_to_jiffies(15));//添加定時器,并且設置定時器時間printk("key_work !!!\r\n");
}// /*tasklet*/
// static void tasklet_func(unsigned long data)//中斷下半部tasklet函數
// {
//     struct key_dev *key_task = (struct key_dev*)data;
//     key_task->timer.data = data;
//     mod_timer(&key_task->timer, jiffies+msecs_to_jiffies(15));//添加定時器,并且設置定時器時間
//     printk("tasklet !!!\r\n");
// }
static irqreturn_t key_irq_hander(int irq, void *dev_id)//中斷處理函數,遇到上升沿或者下降沿觸發定時器
{struct key_dev *key_irq = (struct key_dev*)dev_id;schedule_work(&key_irq->irq_struct.testwork);//工作隊列// tasklet_schedule(&key_irq->irq_struct.tasklet);//調用中斷下半部函數// key_irq->timer.data = (unsigned long)dev_id;// mod_timer(&key_irq->timer, jiffies+msecs_to_jiffies(15));//添加定時器,并且設置定時器時間return IRQ_HANDLED;
}static void gpio_init(void)
{int ret =0;// atomic_set(&key.lock_yuan, key.timer_period);key.nd = of_find_node_by_path("/key");//在設備樹中獲取節點key.irq_struct.key_gpio = of_get_named_gpio(key.nd,"key-gpio", 0);//得到gpio引腳ret = gpio_request(key.irq_struct.key_gpio,KEY_NAME);if(ret<0){printk("gpio requst error !\r\n");}ret = gpio_direction_input(key.irq_struct.key_gpio);//設置輸入if(ret == 0){ printk("從設備樹讀取節點和gpio正確\r\n");}else {gpio_free(key.irq_struct.key_gpio);}/*中斷設置*/key.irq_struct.irq = gpio_to_irq(key.irq_struct.key_gpio);//獲取中斷號key.irq_struct.handler = key_irq_hander;//中斷函數表注冊key.irq_struct.value = KEY_EN_VALUE;//按鍵值//key.irq_struct.tasklet = tasklet_func;//添加下半部函數taskletmemset(key.irq_struct.gpio_name,0,sizeof(key.irq_struct.gpio_name));strncpy(key.irq_struct.gpio_name, "key0", sizeof(key.irq_struct.gpio_name));//按鍵名// tasklet_init(&key.irq_struct.tasklet,tasklet_func, (unsigned long)&key);INIT_WORK(&key.irq_struct.testwork,testwork_func_t);ret = request_irq(key.irq_struct.irq,key.irq_struct.handler,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,key.irq_struct.gpio_name, &key);if(ret<0){printk("irq requset error !\r\n");}
}static int __init pgkey_init(void)
{gpio_init();//gpio初始化/*注冊*//*1.設備號*/if(key.major){key.key_hao = MKDEV(key.major,0);register_chrdev_region(key.key_hao, 1, KEY_NAME);//主動注冊}else{alloc_chrdev_region(&key.key_hao, 0, 1, KEY_NAME);//自動注冊}printk("major = %d,minor = %d",MAJOR(key.key_hao),MINOR(key.key_hao));/*2.注冊函數*/key.cdev.owner = THIS_MODULE;cdev_init(&key.cdev,&pgkey_fops);cdev_add(&key.cdev,key.key_hao,1);/*3.節點申請*/ key.class = class_create(THIS_MODULE,KEY_NAME);key.device = device_create(key.class, NULL,key.key_hao, NULL,KEY_NAME);/*初始化定時器*/init_timer(&key.timer);//初始化定時器key.timer.function = key_timer_function;//定時器函數return 0;
}static void  __exit pgkey_exit(void)
{free_irq(key.irq_struct.irq, &key);del_timer(&key.timer);gpio_free(key.irq_struct.key_gpio);cdev_del(&key.cdev);//先刪除設備unregister_chrdev_region(key.key_hao,1);//刪除設備號device_destroy(key.class,key.key_hao);//先刪除和設備第關系class_destroy(key.class);//再刪除類}/*驅動入口和出口*/
module_init(pgkey_init);
module_exit(pgkey_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wyt");

二,阻塞和非阻塞IO和異步通知

在這里插入圖片描述

1.阻塞IO

??這種思想就是讓應用程序等一下,等好了,在運行。應用程序在對驅動程序操作時,如果不能獲取到資源,阻塞IO會將應用程序休眠,線程掛起,直到能獲取到資源時,再進行應用程序的操作。不然應用程序一直對按鍵進行查詢,占用CPU率高達90%以上。

1.1 常見結構1

??利用函數 wait_event_interruptible,放在讀函數中,只有在定時器函數中,判斷出按鍵按下,才會喚醒,wake_up_interruptible喚醒等待隊列中的線程,驅動函數 wait_event_interruptible下面的內容才能執行,從而應用讀取到數據。

wait_queue_head_t r_wait;//等待隊列頭 
static ssize_t key_read(struct file *filp,  char __user *buf, size_t count, loff_t *ppos)
{static int key_value = 0,ret = 0;struct key_dev *key_read =(struct key_dev *)filp->private_data;wait_event_interruptible(key_read->r_wait,key_pan);//等待事件,當按鍵按下有效.........return 0;
}
static void key_timer_function(unsigned long arg)//定時結束后會執行第操作,
{......../*喚醒進程*/if(key_pan){wake_up_interruptible(&key_t->r_wait);}
}
init_waitqueue_head(&key.r_wait);//初始化

1.2 常見結構2

??等待隊列頭是一個鏈表頭,用于管理所有等待某個事件的線程,因為可能有好多文件都調用這個驅動模塊。
??等待隊列項表示一個正在等待某個事件的線程,表示某一個。

wait_queue_head_t r_wait;//等待隊列頭 
static ssize_t key_read(struct file *filp,  char __user *buf, size_t count, loff_t *ppos)
{static int key_value = 0,ret = 0;struct key_dev *key_read =(struct key_dev *)filp->private_data;DECLARE_WAITQUEUE(wait,current);//定義的等待隊列add_wait_queue(&key_read->r_wait,&wait);//將隊列添加到等待隊列頭__set_current_state(TASK_INTERRUPTIBLE);//設置為可被打斷狀態,即用kill -9能殺掉schedule();//讓出 CPU,線程進入睡眠狀態,等待被喚醒/*判斷被其他信號喚醒*/if(signal_pending(current)){goto data_error;}......data_error:__set_current_state(TASK_RUNNING);//將當前任務設置為運行狀態remove_wait_queue(&key_read->r_wait,&wait);//將對應的隊列項從等待隊列頭刪除return 0;
}
static void key_timer_function(unsigned long arg)//定時結束后會執行第操作,
{......../*喚醒進程*/if(key_pan){wake_up_interruptible(&key_t->r_wait);}
}
init_waitqueue_head(&key.r_wait);//初始化

2.非阻塞IO

??這種思想是讓應用程序每隔一定時間(很短)查一下,其他時間去干別的事情,當查詢好了的話,就可以運行了。應用程序在對驅動程序操作時,如果不能獲取到資源,非阻塞IO會一直輪詢等待,設置間隔時間,比如200ms,每隔200ms就會去查詢驅動能否獲取數據。中間的時間,也就是這個200ms內進行其他操作。這個要在應用程序中,打開文檔操作加上O_NONBLOCK: fd = open(filename,O_RDWR | O_NONBLOCK);//按照非阻塞方式打開

2.1 驅動結構

wait_queue_head_t r_wait;//等待隊列頭 
static ssize_t key_read(struct file *filp,  char __user *buf, size_t count, loff_t *ppos)
{static int key_value = 0,ret = 0;struct key_dev *key_read =(struct key_dev *)filp->private_data;if(filp->f_flags & O_NONBLOCK)//判斷是阻塞還是非阻塞方式{if(key_pan==0){return -EAGAIN;//非阻塞            }}else wait_event_interruptible(key_read->r_wait,key_pan);//否則按照阻塞方式......
}
static unsigned int key_poll(struct file *filp, struct poll_table_struct *wait)
{int ret = 0;struct key_dev *key_poll = (struct key_dev *)filp->private_data;poll_wait(filp, &key_poll->r_wait, wait);//文件,等待隊列頭,等待隊列項if(key_pan)//poll判斷的內容,按鍵按下可讀{ret = POLLIN | POLLRDNORM;}return ret;
}
static struct file_operations pgkey_fops={.owner=THIS_MODULE,.open=pgkey_open,.read=key_read,.release=pgkey_release,.poll = key_poll,};
init_waitqueue_head(&key.r_wait);//初始化

2.2 select 應用程序

??應用程序有兩種,poll和select,后者能監視的文件數量有最大限制1024,poll函數沒有最大文件限制。

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include "poll.h"
#include "sys/select.h"
#include "sys/time.h"#include "linux/ioctl.h"
int main(unsigned char argc,unsigned char *argv[])
{int rel = 0,fd = 0,arg = 0;int cmd = 0;struct pollfd fds;fd_set readfds;struct timeval timeout;    unsigned char *filename,value[1]={0};filename = argv[1];fd = open(filename,O_RDWR | O_NONBLOCK);//按照非阻塞方式打開if(fd<0) printf("open file error\r\n");/*select*/while(1){FD_ZERO(&readfds);//清除FD_SET(fd,&readfds);//添加fd到readfds中timeout.tv_sec=0;timeout.tv_usec = 500000;//500msrel = select(fd+1,&readfds,NULL,NULL,&timeout);switch (rel){case 0:printf("time out !!\r\n");break;case -1:break;default:rel = read(fd,value,sizeof(value));if(rel<0){}else{if(value[0]){printf("value = %x\r\n",value[0]);}          }break;}}   rel = close(fd);if(rel<0) printf("close in  APP error\r\n");return 0;
}

2.3 poll 應用程序

```c
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include "poll.h"
#include "sys/select.h"
#include "sys/time.h"#include "linux/ioctl.h"
int main(unsigned char argc,unsigned char *argv[])
{int rel = 0,fd = 0,arg = 0;int cmd = 0;struct pollfd fds;fd_set readfds;struct timeval timeout;    unsigned char *filename,value[1]={0};filename = argv[1];fd = open(filename,O_RDWR | O_NONBLOCK);//按照非阻塞方式打開if(fd<0) printf("open file error\r\n");fds.fd=fd;fds.events = POLLIN;while(1){rel = poll(&fds,1,1000);switch (rel){case 0:printf("time out !!\r\n");break;case -1:break;default:rel = read(fd,value,sizeof(value));if(rel<0){}else{if(value[0]){printf("value = %x\r\n",value[0]);}          }break;}}rel = close(fd);if(rel<0) printf("close in  APP error\r\n");return 0;
}

3.異步通知

??類似于中斷的思想,當驅動中判斷出按鍵按下,發送一個信號,到應用對應的進程中,從而引起應用程序執行讀取按鍵的操作,在應用程序中,類似于中斷的方式。
驅動框架:

 struct fasync_struct *key_fasync;//異步通知
static void key_timer_function(unsigned long arg)//定時結束后會執行第操作,
{.......if(key_pan)//按下{printk("in linux  key push  in fasync!\r\n");kill_fasync(&key_t->key_fasync,SIGIO,POLL_IN);//添加異步通知,發送這個信號}
}static int key_file_fasync(int fd,struct file *filp,int on)
{struct key_dev *dev = (struct key_dev *)filp->private_data;return fasync_helper(fd, filp,on,&dev->key_fasync);
}
static int pgkey_release(struct inode *innode,struct file *filp)
{//這段還是之前的釋放函數,不過添加異步通知后,在釋放文件時,關閉異步通知。return key_file_fasync(-1,filp,0);
}
static struct file_operations pgkey_fops={.owner=THIS_MODULE,.open=pgkey_open,.read=key_read, .fasync =key_file_fasync,.release=pgkey_release,
}; 

??應用程序:這個key_signal就類似于中斷函數, signal(SIGIO,key_signal)相當于中斷函數注冊的意思,從而在應用程序中,將應用程序對應的進程號給內核,并且打開異步通知,內核中驅動程序判斷按鍵按下后,將信號傳遞給這個應用程序進程,執行key_signal函數,讀取按鍵值。

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include "poll.h"
#include "sys/select.h"
#include "sys/time.h"
#include "linux/ioctl.h"
#include "signal.h"
static int fd = 0;
static void key_signal(int num)
{printf("getin APP key_signal\r\n");static int ret =0;unsigned char value = 0;ret = read(fd,&value,sizeof(value));if(ret<0){printf("read error!\r\n");}else {printf("KEY = %d\r\n",value);}
}
int main(unsigned char argc,unsigned char *argv[])
{int rel = 0,arg = 0,flags = 0;struct pollfd fds;fd_set readfds;struct timeval timeout;    unsigned char *filename,value[1]={0};filename = argv[1];fd = open(filename,O_RDWR);//按照阻塞方式打開if(fd<0) printf("open file error\r\n");/*設置信號處理函數*/signal(SIGIO,key_signal);fcntl(fd,F_SETOWN,getpid());//將本應用的進程號告訴內核,從而內核傳遞的信號到這個進程flags = fcntl(fd,F_GETFD);//獲取當前進程狀態fcntl(fd,F_SETFL,flags | FASYNC);//開啟當前進程異步通知printf("open fasync!\r\n");while(1){sleep(2);}printf("OVER\r\n");rel = close(fd);if(rel<0) printf("close in  APP error\r\n");return 0;}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/898597.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/898597.shtml
英文地址,請注明出處:http://en.pswp.cn/news/898597.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Docker和Dify學習筆記

文章目錄 1 docker學習1.1 基本命令使用1.1.1 docker ps查看當前正在運行的鏡像1.1.2 docker stop停止容器1.1.3 docker compose容器編排1.1.4 docker網絡[1] 進入到容器里面敲命令[2] docker network ls[3] brige網絡模式下容器訪問宿主機的方式 2 Dify的安裝和基礎使用2.1 下…

高并發庫存系統是否適合使用 ORM(Hibernate / MyBatis)

在設計高并發的庫存管理系統時&#xff0c;數據層的選擇至關重要。許多企業開發中習慣使用 ORM&#xff08;如 Hibernate、MyBatis&#xff09;來簡化數據庫訪問&#xff0c;但在高并發、高吞吐的場景下&#xff0c;ORM 的適用性往往成為爭議焦點。本文將探討高并發庫存系統是否…

Web爬蟲利器FireCrawl:全方位助力AI訓練與高效數據抓取。本地部署方式

開源地址&#xff1a;https://github.com/mendableai/firecrawl 01、FireCrawl 項目簡介 Firecrawl 是一款開源、優秀、尖端的 AI 爬蟲工具&#xff0c;專門從事 Web 數據提取&#xff0c;并將其轉換為 Markdown 格式或者其他結構化數據。 Firecrawl 還特別上線了一個新的功…

探秘Transformer系列之(16)--- 資源占用

探秘Transformer系列之&#xff08;16&#xff09;— 資源占用 文章目錄 探秘Transformer系列之&#xff08;16&#xff09;--- 資源占用0x00 概述0x01 背景知識1.1 數據類型1.2 進制&換算數字進制存儲度量換算 1.3 參數顯存占用有參數的層無參數的層所需資源 1.4 計算量 0…

jaeger安裝和簡單使用

文章目錄 jaeger安裝和使用什么是jaegerjaeger安裝 jaeger安裝和使用 什么是jaeger 官網&#xff1a;https://www.jaegertracing.io/ Jaeger 是一個分布式追蹤系統。Jaeger的靈感來自 Dapper 和 OpenZipkin&#xff0c;是一個由 Uber 創建并捐贈給 云原生計算基金會&#xf…

【Mybatis-plus】在mybatis-plus中 if test標簽如何判斷 list不為空

博主介紹&#xff1a;?全網粉絲22W&#xff0c;CSDN博客專家、Java領域優質創作者&#xff0c;掘金/華為云/阿里云/InfoQ等平臺優質作者、專注于Java技術領域? 技術范圍&#xff1a;SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大數據、物…

FRP在物聯網設備中的穿透方案

物聯網設備常位于NAT后&#xff0c;FRP為其提供穩定穿透鏈路。 配置要點 輕量化部署&#xff1a;使用ARM版本FRP客戶端&#xff0c;適配樹莓派等設備9。 自啟動腳本&#xff1a;通過systemd或crontab實現設備重啟后自動連接26。 低功耗優化&#xff1a;調整心跳間隔&#xf…

【遞歸,搜索與回溯算法篇】- 名詞解釋

一. 遞歸 1. 什么是遞歸&#xff1f; 定義&#xff1a; 函數自己調用自己的情況關鍵點&#xff1a; ?終止條件&#xff1a; 必須明確遞歸出口&#xff0c;避免無限遞歸 ?子問題拆分&#xff1a; 問題需能分解成結構相同的更小的子問題缺點&#xff1a; ?棧溢出風險&#x…

條件變量,鎖,共享數據的關系

條件變量、共享數據和鎖之間的三方耦合關系源于多線程環境下對資源訪問的同步需求。以下是關鍵點分析&#xff1a; 條件變量中通常會對共享數據進行判斷和處理&#xff0c;如果不加鎖就會出現數據競爭的問題&#xff0c;所以并不是條件變量要跟鎖一起使用&#xff0c;而是上鎖為…

大屏技術匯集【目錄】

Cesium 自從首次發布以來&#xff0c;經歷了多個版本的迭代和更新&#xff0c;每個版本都帶來了性能改進、新功能添加以及對現有功能的優化。以下是 Cesium 一些重要版本及其主要特點&#xff1a; 主要版本概述 Cesium 1.0 (2012年) 初始版本發布&#xff0c;確立了Cesium作為…

圖解AUTOSAR_CP_EEPROM_Abstraction

AUTOSAR EEPROM抽象模塊詳細說明 基于AUTOSAR標準的EEPROM抽象層技術解析 目錄 1. 概述 1.1 核心功能1.2 模塊地位2. 架構概覽 2.1 架構層次2.2 模塊交互3. 配置結構 3.1 主要配置容器3.2 關鍵配置參數4. 狀態管理 4.1 基本狀態4.2 狀態轉換5. 接口設計 5.1 主要接口分類5.2 接…

C++相關基礎概念之入門講解(下)

1. 引用 ? int main() {const int a10;int& aaa;aa;cout<<aa<<endl; } 引用 不是新定義一個變量&#xff0c;而 是給已存在變量取了一個別名 &#xff0c;編譯器不會為引用變量開辟內存空 間&#xff0c;它和它引用的變量 共用同一塊內存空間&#xff08;初…

注意力機制,本質上是在做什么?

本文以自注意機制為例&#xff0c;輸入一個4*4的矩陣 如下&#xff1a; input_datatorch.tensor([[1,2,3,4], [5,6,7,8], [9,10,11,12], [13,14,15,16] ],dtypetorch.float) 得到Q和K的轉置如下。 此時&#xff0c;計算QK^T ,得到如下結果 第一行第一個位置就是第一條樣本和第…

記一次wsl2+docker無法運行的經歷

前情提要 由于某個大創項目的需要和對貓娘機器人的迫切渴求&#xff08;bushi 需要在電腦里面安裝docker desktop。由于電腦里面安裝了wsl2環境 因此決定使用wsl2dockerdesktop的方式配置docker 遇到的問題 在像往常一樣安裝docker desktop并且啟動時 提示錯誤&#xff1a; …

PageHelper插件依賴引入不報錯,但用不了

情況: 父模塊pom. Xml 引入1. 4. 0以上版本的pagehelper-spring-boot-starter。 要用到插件的子模塊&#xff0c;去掉版本號&#xff0c;引入和父模塊一樣的依賴。 引入成功&#xff0c;沒有報錯&#xff0c;但是打開右邊的maven里面沒有找到PageHelper插件。 終端清空并重…

Windows搭建免翻墻的BatteryHistorian

文章參考 GitCode - 全球開發者的開源社區,開源代碼托管平臺 免翻墻的BatteryHistorian主要原理&#xff1a;修改go源碼 1.安裝Java環境 1.點擊下載 Java JDK&#xff0c;并安裝,一路next 2.java -version 檢驗是否安裝成功 2.安裝Git工具 1、點擊下載 Git&#xff0c;并…

項目中pnpm版本和全局pnpm版本不一致

項目中pnpm版本和全局pnpm版本不一致 檢查package.json中&#xff0c;是否存在"packageManager": “pnpm8.6.10”&#xff0c;限制了pnpm的版本。

透析Vue的nextTick原理

nextTick 是 Vue.js 中的一個核心機制&#xff0c;用于在 下一次 DOM 更新周期后 執行回調函數。它的核心原理是 利用 JavaScript 的事件循環機制&#xff08;Event Loop&#xff09;&#xff0c;結合微任務&#xff08;Microtask&#xff09;或宏任務&#xff08;Macrotask&am…

WRF/Chem 模式技術解讀:為大氣污染治理提供有力支撐

技術點目錄 第一部分、WRF-Chem模式應用案例和理論基礎第二部分、Linux環境配置及WRF-CHEM第三部分、WRF-Chem模式編譯&#xff0c;排放源制作第四部分、WRF-Chem數據準備&#xff08;氣象、排放、初邊界條件等&#xff09;&#xff0c;案例實踐第五部分、模擬結果提取、數據可…

ccfcsp2701如此編碼

//如此編碼 #include<iostream> using namespace std; int main(){int n,m;cin>>n>>m;int a[21],b[21],c[21];for(int i1;i<n;i){cin>>a[i];}c[0]1;for(int i1;i<n;i){c[i]c[i-1]*a[i];}b[1](m%c[1])/c[0];int s1,s20;for(int i2;i<n;i){s2s2…