71. 我的第一個Linux驅動實驗

一、字符設備驅動框架

字符設備驅動的編寫主要就是驅動對應的open、close、read。。。其實就是
file_operations結構體的成員變量的實現。
其中關于 C 庫以及如何通過系統調用“陷入” 到內核空間這個我們不用去管,我們重點關注的是應用程序和具體的驅動,應用程序使用到的函數在具體驅動程序中都有與之對應的函數,比如應用程序中調用了 open 這個函數,那么在驅動程序中也得有一個名為 open 的函數。每一個系統調用,在驅動中都有與之對應的一個驅動函數,在 Linux 內核文件 include/linux/fs.h 中有個叫做 file_operations 的結構體,此結構體就是 Linux 內核驅動操作函數集合,內容如下所示

二、驅動模塊的加載與卸載

Linux 驅動有兩種運行方式,第一種就是將驅動編譯進 Linux 內核中,這樣當 Linux 內核啟動的時候就會自動運行驅動程序。第二種就是將驅動編譯成模塊(Linux 下模塊擴展名為.ko),在Linux 內核啟動以后使用“insmod”命令加載驅動模塊。在調試驅動的時候一般都選擇將其編譯為模塊,這樣我們修改驅動以后只需要編譯一下驅動代碼即可,不需要編譯整個 Linux 代碼。而且在調試的時候只需要加載或者卸載驅動模塊即可,不需要重啟整個系統。總之,將驅動編譯為模塊最大的好處就是方便開發,當驅動開發完成,確定沒有問題以后就可以將驅動編譯進Linux 內核中,當然也可以不編譯進 Linux 內核中,具體看自己的需求。
模塊有加載和卸載兩種操作,我們在編寫驅動的時候需要注冊這兩種操作函數,模塊的加載和卸載注冊函數如下:
在這里插入圖片描述

module_init 函數用來向 Linux 內核注冊一個模塊加載函數,參數 xxx_init 就是需要注冊的具體函數,當使用“insmod”命令加載驅動的時候, xxx_init 這個函數就會被調用。 module_exit()函數用來向 Linux 內核注冊一個模塊卸載函數,參數 xxx_exit 就是需要注冊的具體函數,當使用“rmmod”命令卸載具體驅動的時候 xxx_exit 函數就會被調用。字符設備驅動模塊加載和卸載模板如下所示:
在這里插入圖片描述

Linux驅動程序可以編譯到kernel里面,也就是zImage,也可以編譯為模塊,.ko。測試的時候只需要加載.ko模塊就可以。

編寫驅動的時候注意事項!
1、編譯驅動的時候需要用到linux內核源碼!因此要解壓縮linux內核源碼,編譯linux內核源碼。得到zImage和.dtb。需要使用編譯后的到的zImage和dtb啟動系統。
2、從SD卡啟動,SD卡燒寫了uboot。uboot通過tftp從ubuntu里面獲取zimage和dtb,rootfs也是通過nfs掛在。
3、設置bootcmd和bootargs

bootargs=console=ttymxc0,115200 rw root=/dev/nfs nfsroot=192.168.1.66:/home/zzk/linux/nfs/rootfs ip=192.168.1.50:192.168.1.66:192.168.1.1:255.255.255.0::eth0:off
bootcmd=tftp 80800000 zImage;tftp 83000000 imx6ull-alientek-emmc.dtb;bootz 80800000 - 83000000;

4、將編譯出來的.ko文件放到根文件系統里面。加載驅動會用到加載命令:insmod,modprobe。移除驅動使用命令rmmod。對于一個新的模塊使用modprobe加載的時候需要先調用一下depmod命令。
驅動編譯完成以后擴展名為.ko,有兩種命令可以加載驅動模塊: insmod和 modprobe, insmod是最簡單的模塊加載命令,此命令用于加載指定的.ko 模塊,比如加載 drv.ko 這個驅動模塊,命令如下:

insmod drv.ko

insmod 命令不能解決模塊的依賴關系,比如 drv.ko 依賴 first.ko 這個模塊,就必須先使用insmod 命令加載 first.ko 這個模塊,然后再加載 drv.ko 這個模塊。 但是 modprobe 就不會存在這個問題, modprobe 會分析模塊的依賴關系,然后會將所有的依賴模塊都加載到內核中,因此modprobe 命令相比 insmod 要智能一些。 modprobe 命令主要智能在提供了模塊的依賴性分析、錯誤檢查、錯誤報告等功能,推薦使用 modprobe 命令來加載驅動。 modprobe 命令默認會去/lib/modules/目錄中查找模塊,比如本書使用的 Linux kernel 的版本號為 4.1.15,因此 modprobe 命令默認會到/lib/modules/4.1.15 這個目錄中查找相應的驅動模塊,一般自己制作的根文件系統中是不會有這個目錄的,所以需要自己手動創建。

/lib/modules/4.1.15
/lib/modules/4.1.15 # modprobe chrdevbase
chrdevbase init!

驅動模塊的卸載使用命令“rmmod”即可,比如要卸載 drv.ko,使用如下命令即可: rmmod drv.ko
也可以使用“modprobe -r”命令卸載驅動,比如要卸載 drv.ko,命令如下: modprobe -r drv.ko
使用 modprobe 命令可以卸載掉驅動模塊所依賴的其他模塊,前提是這些依賴模塊已經沒有被其他模塊所使用,否則就不能使用 modprobe 來卸載驅動模塊。所以對于模塊的卸載,還是推薦使用 rmmod 命令。
5,驅動模塊加載成功以后可以使用lsmod查看一下。
6,卸載模塊使用rmmod命令

/lib/modules/4.1.15 # rmmod chrdevbase.ko 
chrdevbase exit!
/lib/modules/4.1.15 # lsmod
chrdevbase 1884 0 - Live 0x7f00c000 (O)

當應用程序調用 open 函數的時候此函數就會調用,本例程中我們沒有做任何工作,只是輸出一串字符,用于調試。這里使用了 printk 來輸出信息,而不是 printf!因為在 Linux 內核中沒有 printf 這個函數。 printk 相當于 printf 的孿生兄妹, printf運行在用戶態, printk 運行在內核態。在內核中想要向控制臺輸出或顯示一些內容,必須使用printk 這個函數。不同之處在于, printk 可以根據日志級別對消息進行分類,一共有 8 個消息級別,這 8 個消息級別定義在文件 include/linux/kern_levels.h 里面,定義如下:

三、字符設備的注冊與注銷

對于字符設備驅動而言,當驅動模塊加載成功以后需要注冊字符設備,同樣,卸載驅動模塊的時候也需要注銷掉字符設備。字符設備的注冊和注銷函數原型如下所示:
static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)
static inline void unregister_chrdev(unsigned int major, const char *name)
register_chrdev 函數用于注冊字符設備,此函數一共有三個參數,這三個參數的含義如下: major: 主設備號, Linux 下每個設備都有一個設備號,設備號分為主設備號和次設備號兩部分,關于設備號后面會詳細講解。name:設備名字,指向一串字符串。
fops: 結構體 file_operations 類型指針,指向設備的操作函數集合變量。unregister_chrdev 函數用戶注銷字符設備,此函數有兩個參數,這兩個參數含義如下: major: 要注銷的設備對應的主設備號。
name: 要注銷的設備對應的設備名。
一般字符設備的注冊在驅動模塊的入口函數 xxx_init 中進行,字符設備的注銷在驅動模塊的出口函數 xxx_exit 中進行。在示例代碼 40.2.1.1 中字符設備的注冊和注銷,內容如下所示

1、我們需要向系統注冊一個字符設備,使用函數register_chrdev。
2、卸載驅動的時候需要注銷掉前面注冊的字符設備,使用函數unregister_chrdev,注銷字符設備。

四、設備號

1,Linux內核使用dev_t。

typedef __kernel_dev_t dev_t;
typedef __u32 __kernel_dev_t;
typedef unsigned int __u32;
2、Linux內核將設備號分為兩部分:主設備號和次設備號。主設備號占用前12位,次設備號占用低20位。
因此 Linux系統中主設備號范圍為 0~4095,所以大家在選擇主設備號的時候一定不要超過這個范圍。
在文件 include/linux/kdev_t.h 中提供了幾個關于設備號的操作函數(本質是宏),如下所示
3、設備號的操作函數,或宏
從dev_t獲取主設備號和次設備號,MAJOR(dev_t),MINOR(dev_t)。也可以使用主設備號和次設備號構成dev_t,通過MKDEV(major,minor)
示例代碼 40.3.3 設備號操作函數
在這里插入圖片描述

第 6 行,宏 MINORBITS 表示次設備號位數,一共是 20 位。第 7 行,宏 MINORMASK 表示次設備號掩碼。
第 9 行,宏 MAJOR 用于從 dev_t 中獲取主設備號,將 dev_t 右移 20 位即可。
第 10 行,宏 MINOR 用于從 dev_t 中獲取次設備號,取 dev_t 的低 20 位的值即可。
第 11 行,宏 MKDEV 用于將給定的主設備號和次設備號的值組合成 dev_t 類型的設備號。

/lib/modules/4.1.15 # cat /proc/devices
Character devices:1 mem4 /dev/vc/04 tty5 /dev/tty5 /dev/console5 /dev/ptmx7 vcs10 misc13 input29 fb81 video4linux89 i2c90 mtd
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
200 chrdevbase
207 ttymxc
226 drm
250 ttyLP
251 watchdog
252 ptp
253 pps
254 rtcBlock devices:1 ramdisk
259 blkext7 loop8 sd31 mtdblock65 sd66 sd67 sd68 sd69 sd70 sd71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
179 mmc
/lib/modules/4.1.15 # 

五、file_operations的具體實現

struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
int (*iterate) (struct file *, struct dir_context *);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*mremap)(struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **, void **);
long (*fallocate)(struct file *file, int mode, loff_t offset,loff_t len);
void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
unsigned (*mmap_capabilities)(struct file *);
#endif
};

六、字符設備驅動框架的搭建

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
/***************************************************************
文件名     : chrdevbase.c
描述      : chrdevbase驅動文件。
***************************************************************/#define CHRDEVBASE_MAJOR    200             /* 主設備號 */
#define CHRDEVBASE_NAME     "chrdevbase"    /* 設備名     */static char readbuf[100];       /* 讀緩沖區 */
static char writebuf[100];      /* 寫緩沖區 */
static char kerneldata[] = {"kernel data!"};/** @description     : 打開設備* @param - inode   : 傳遞給驅動的inode* @param - filp    : 設備文件,file結構體有個叫做private_data的成員變量*                    一般在open的時候將private_data指向設備結構體。* @return          : 0 成功;其他 失敗*/
static int chrdevbase_open(struct inode *inode, struct file *filp)
{//printk("chrdevbase open!\r\n");return 0;
}/** @description     : 從設備讀取數據 * @param - filp    : 要打開的設備文件(文件描述符)* @param - buf     : 返回給用戶空間的數據緩沖區* @param - cnt     : 要讀取的數據長度* @param - offt    : 相對于文件首地址的偏移* @return          : 讀取的字節數,如果為負值,表示讀取失敗*/
static ssize_t chrdevbase_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{int retvalue = 0;/* 向用戶空間發送數據 */memcpy(readbuf, kerneldata, sizeof(kerneldata));retvalue = copy_to_user(buf, readbuf, cnt);if(retvalue == 0){printk("kernel senddata ok!\r\n");}else{printk("kernel senddata failed!\r\n");}//printk("chrdevbase read!\r\n");return 0;
}/** @description     : 向設備寫數據 * @param - filp    : 設備文件,表示打開的文件描述符* @param - buf     : 要寫給設備寫入的數據* @param - cnt     : 要寫入的數據長度* @param - offt    : 相對于文件首地址的偏移* @return          : 寫入的字節數,如果為負值,表示寫入失敗*/
static ssize_t chrdevbase_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{int retvalue = 0;/* 接收用戶空間傳遞給內核的數據并且打印出來 */retvalue = copy_from_user(writebuf, buf, cnt);if(retvalue == 0){printk("kernel recevdata:%s\r\n", writebuf);}else{printk("kernel recevdata failed!\r\n");}//printk("chrdevbase write!\r\n");return 0;
}/** @description     : 關閉/釋放設備* @param - filp    : 要關閉的設備文件(文件描述符)* @return          : 0 成功;其他 失敗*/
static int chrdevbase_release(struct inode *inode, struct file *filp)
{//printk("chrdevbase release!\r\n");return 0;
}/** 設備操作函數結構體*/
static struct file_operations chrdevbase_fops = {.owner = THIS_MODULE,   .open = chrdevbase_open,.read = chrdevbase_read,.write = chrdevbase_write,.release = chrdevbase_release,
};/** @description : 驅動入口函數 * @param       : 無* @return      : 0 成功;其他 失敗*/
static int __init chrdevbase_init(void)
{int retvalue = 0;/* 注冊字符設備驅動 */retvalue = register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME, &chrdevbase_fops);if(retvalue < 0){printk("chrdevbase driver register failed\r\n");}printk("chrdevbase init!\r\n");return 0;
}/** @description : 驅動出口函數* @param       : 無* @return      : 無*/
static void __exit chrdevbase_exit(void)
{/* 注銷字符設備驅動 */unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME);printk("chrdevbase exit!\r\n");
}/* * 將上面兩個函數指定為驅動的入口和出口函數 */
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);/* * LICENSE和作者信息*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("mmk");

七、應用程序編寫

Linux下一切皆文件,首先要open

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
/***************************************************************
文件名     : chrdevbaseApp.c
版本      : V1.0
描述      : chrdevbase驅測試APP。
其他      : 使用方法:./chrdevbase /dev/chrdevbase <1>|<2>argv[2] 1:讀文件argv[2] 2:寫文件      
***************************************************************/static char usrdata[] = {"usr data!"};/** @description     : main主程序* @param - argc    : argv數組元素個數* @param - argv    : 具體參數* @return          : 0 成功;其他 失敗*/
int main(int argc, char *argv[])
{int fd, retvalue;char *filename;char readbuf[100], writebuf[100];if(argc != 3){printf("Error Usage!\r\n");return -1;}filename = argv[1];/* 打開驅動文件 */fd  = open(filename, O_RDWR);if(fd < 0){printf("Can't open file %s\r\n", filename);return -1;}if(atoi(argv[2]) == 1){ /* 從驅動文件讀取數據 */retvalue = read(fd, readbuf, 50);if(retvalue < 0){printf("read file %s failed!\r\n", filename);}else{/*  讀取成功,打印出讀取成功的數據 */printf("read data:%s\r\n",readbuf);}}if(atoi(argv[2]) == 2){/* 向設備驅動寫數據 */memcpy(writebuf, usrdata, sizeof(usrdata));retvalue = write(fd, writebuf, 50);if(retvalue < 0){printf("write file %s failed!\r\n", filename);}}/* 關閉設備 */retvalue = close(fd);if(retvalue < 0){printf("Can't close file %s\r\n", filename);return -1;}return 0;
}

測試 APP 比較簡單,只有一個文件,因此就不需要編寫 Makefile 了,直接輸入命令編譯。因為測試 APP 是要在 ARM 開發板上運行的,所以需要使用 arm-linux-gnueabihf-gcc 來編譯,輸入如下命令:

arm-linux-gnueabihf-gcc chrdevbaseApp.c -o chrdevbaseApp 

編譯完成以后會生成一個叫做 chrdevbaseApp 的可執行程序,輸入如下命令查看chrdevbaseAPP 這個程序的文件信息

file chrdevbaseApp
chrdevbaseApp: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 2.6.31, BuildID[sha1]=5d017375992cf6c40e8fccb19a238dfd552c

chrdevbaseAPP 這個可執行文件是 32 位 LSB 格式, ARM 版本的,因此 chrdevbaseAPP 只能在 ARM 芯片下運行。
拷 貝 完 成 以 后 就 會 在 開 發 板 的 /lib/modules/4.1.15 目 錄 下 存 在 chrdevbase.ko 和chrdevbaseAPP 這兩個文件
在這里插入圖片描述

八、測 試

1、加載驅動。

modprobe chrdevbase.ko

或者

modprobe chrdevbase

2,設備驅動在dev里,進入/dev查看設備文件,chrdevbase。/dev/chrdevbase。但是實際沒有,因為我們沒有創建設備節點。

mknod /dev/chrdevbase c 200 0
/dev # ls
autofs              ram14               tty37
bus                 ram15               tty38
console             ram2                tty39
cpu_dma_latency     ram3                tty4
dri                 ram4                tty40
fb0                 ram5                tty41
full                ram6                tty42
fuse                ram7                tty43
hwrng               ram8                tty44
i2c-0               ram9                tty45
i2c-1               random              tty46
input               rfkill              tty47
kmsg                rtc0                tty48
loop-control        snd                 tty49
loop0               tty                 tty5
loop1               tty0                tty50
loop2               tty1                tty51
loop3               tty10               tty52
loop4               tty11               tty53
loop5               tty12               tty54
loop6               tty13               tty55
loop7               tty14               tty56
mem                 tty15               tty57
memory_bandwidth    tty16               tty58
mmcblk0             tty17               tty59
mmcblk1             tty18               tty6
mmcblk1boot0        tty19               tty60
mmcblk1boot1        tty2                tty61
mmcblk1p1           tty20               tty62
mmcblk1p2           tty21               tty63
mmcblk1rpmb         tty22               tty7
network_latency     tty23               tty8
network_throughput  tty24               tty9
null                tty25               ttymxc0
pps0                tty26               ttymxc1
pps1                tty27               ubi_ctrl
ptmx                tty28               urandom
ptp0                tty29               vcs
ptp1                tty3                vcs1
pts                 tty30               vcsa
ram0                tty31               vcsa1
ram1                tty32               video0
ram10               tty33               watchdog
ram11               tty34               watchdog0
ram12               tty35               zero
ram13               tty36

創建設備節點文件
驅動加載成功需要在/dev 目錄下創建一個與之對應的設備節點文件,應用程序就是通過操作這個設備節點文件來完成對具體設備的操作。輸入如下命令創建/dev/chrdevbase 這個設備節點文件:
mknod /dev/chrdevbase c 200 0
其中“mknod”是創建節點命令,“/dev/chrdevbase”是要創建的節點文件,“c”表示這是個字符設備,“ 200”是設備的主設備號,“ 0”是設備的次設備號。創建完成以后就會存在/dev/chrdevbase 這個文件,可以使用“ls /dev/chrdevbase -l”命令查看,
/dev # ls /dev/chrdevbase -l
crw-r–r-- 1 0 0 200, 0 Jan 1 01:34 /dev/chrdevbase
如果 chrdevbaseAPP 想要讀寫 chrdevbase 設備,直接對/dev/chrdevbase 進行讀寫操作即可。相當于/dev/chrdevbase 這個文件是 chrdevbase 設備在用戶空間中的實現。前面一直說 Linux 下一切皆文件,包括設備也是文件,現在大家應該是有這個概念了吧?
3、測試
進入/lib/modules/4.1.15 #

./chrdevbaseApp /dev/chrdevbase

使用 chrdevbaseApp 軟件操作 chrdevbase 這個設備,看看讀寫是否正常,首先進行讀操作,輸入如下命令

./chrdevbaseApp /dev/chrdevbase
/lib/modules/4.1.15 # cat /proc/devices
Character devices:1 mem4 /dev/vc/04 tty5 /dev/tty5 /dev/console5 /dev/ptmx7 vcs10 misc13 input29 fb81 video4linux89 i2c90 mtd
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
200 chrdevbase
207 ttymxc
226 drm
250 ttyLP
251 watchdog
252 ptp
253 pps
254 rtcBlock devices:1 ramdisk
259 blkext7 loop8 sd31 mtdblock65 sd66 sd67 sd68 sd69 sd70 sd71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
179 mmc
/lib/modules/4.1.15 # 

沒有200

rmmod chrdevbase
cat /proc/devices
modprobe chrdevbase

有200 了

/lib/modules/4.1.15 # mknod /dev/chrdevbase c 200 0 
/lib/modules/4.1.15 # mknod /dev/chrdevbase c 200 0 
mknod: /dev/chrdevbase: File exists
./chrdevbaseApp /dev/chrdevbase
/lib/modules/4.1.15 # ./chrdevbaseApp /dev/chrdevbase
Error Usage!

/

lib/modules/4.1.15 # ./chrdevbaseApp /dev/chrdevbase 1
kernel senddata ok!
read data:kernel data!
/lib/modules/4.1.15 # ./chrdevbaseApp /dev/chrdevbase 2
kernel recevdata:usr data!

在這里插入圖片描述

/lib/modules/4.1.15 # lsmod
chrdevbase 1884 0 - Live 0x7f004000 (O)

八、chrdevbase虛擬設備驅動的完善

要求:應用程序可以對驅動讀寫操作,讀的話就是從驅動里面讀取字符串,寫的話就是應用向驅動寫字符串。
1、chrdevbase_read驅動函數編寫
驅動給應用傳遞數據的時候需要用到copy_to_user函數。

if(atoi(argv[2]) == 1){ /* 從驅動文件讀取數據 */retvalue = read(fd, readbuf, 50);if(retvalue < 0){printf("read file %s failed!\r\n", filename);}else{/*  讀取成功,打印出讀取成功的數據 */printf("App read data:%s\r\n",readbuf);}
}

//編譯驅動

make

//編譯App

arm-linux-gnueabihf-gcc chrdevbaseApp.c -o chrdevbaseApp
/lib/modules/4.1.15 # ./chrdevbaseApp /dev/chrdevbase 1
kernel senddata ok!
App read data:kernel data!

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

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

相關文章

jdk21使用Vosk實現語音文字轉換,免費的語音識別

1.下載vosk的model vosk官網&#xff1a;https://alphacephei.com/vosk/models 我這里使用較小的vosk-model-small-cn-0.22 2.添加相關pom文件 <!-- 獲取音頻信息 --><dependency><groupId>org</groupId><artifactId>jaudiotagger</artifac…

如何一鍵安裝所有Python項目的依賴!

在開發項目時&#xff0c;常常需要在多個環境中安裝各種依賴。對開發者來說&#xff0c;每次手動一個個安裝這些依賴是不是很麻煩&#xff1f;&#x1f605; 其實有個超簡單的辦法&#xff01;只需要一個腳本&#xff0c;就能快速解決問題&#xff01;&#x1f4a1; 這就是我們…

Blender配置渲染設置并輸出動畫

在Blender中&#xff0c;渲染設置和渲染動畫的選項位于不同的面板中。以下是具體步驟&#xff1a; 渲染設置 渲染設置用于配置輸出格式、分辨率、幀率等參數。 打開右側的 屬性面板&#xff08;按 N 鍵可切換顯示&#xff09;。 點擊 “輸出屬性” 選項卡&#xff08;圖標是…

C++修煉:string類的使用

Hello大家好&#xff01;很高興我們又見面啦&#xff01;給生活添點passion&#xff0c;開始今天的編程之路&#xff01; 我的博客&#xff1a;<但凡. 我的專欄&#xff1a;《編程之路》、《數據結構與算法之美》、《題海拾貝》、《C修煉之路》 歡迎點贊&#xff0c;關注&am…

【go微服務】如何快速掌握grpc開發

?? 歡迎大家來到景天科技苑?? &#x1f388;&#x1f388; 養成好習慣&#xff0c;先贊后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者簡介&#xff1a;景天科技苑 &#x1f3c6;《頭銜》&#xff1a;大廠架構師&#xff0c;華為云開發者社區專家博主&#xff0c;…

【區塊鏈 + 文化版權】基于 FISCO BCOS 的方言大數據語料庫 | FISCO BCOS 應用案例

蘇州喵自在區塊鏈科技有限公司打造的基于FISCO BCOS 的粵語大數據語料庫&#xff0c; 旨在利用區塊鏈技術保護和發展粵語文化遺產。該項目利用區塊鏈的不可篡改性、分布式存儲、智能合約和激勵機制等特性&#xff0c; 為保護非物質文化遺產&#xff0c; 加強粵語研究與教育和開…

大模型在支氣管擴張預測及治療方案制定中的應用研究

目錄 一、引言 1.1 研究背景與意義 1.2 研究目的與方法 1.3 國內外研究現狀 二、大模型技術概述 2.1 大模型的基本原理與架構 2.2 適用于支氣管擴張預測的大模型類型及特點 2.3 大模型在醫療領域的應用現狀與優勢 三、支氣管擴張的相關醫學知識 3.1 支氣管擴張的病因…

亞馬遜云科技提供完全托管的DeepSeek-R1模型

近日&#xff0c;亞馬遜云科技宣布在Amazon Bedrock上線完全托管的DeepSeek-R1模型。DeepSeek是首個登陸Amazon Bedrock的國產大模型&#xff0c;自今年1月底推出以來&#xff0c;已有數千客戶使用Amazon Bedrock的自定義模型導入功能部署了DeepSeek-R1模型。 DeepSeek在過去幾…

二叉樹、排序算法與結構圖

二叉樹、排序算法與數據庫 二叉樹 二叉樹的性質 節點數與深度的關系&#xff1a;深度為 k k k的二叉樹&#xff0c;最多有 2 k ? 1 2^k - 1 2k?1個節點。例如&#xff0c;深度為 3 3 3的二叉樹&#xff0c;最多有 2 3 ? 1 7 2^3 - 1 7 23?17個節點。葉子節點與度為2節…

vmwaretools解壓失敗|vmware tools distrib cannot mkdir read only file system|bug匯總

最簡單的一條路線&#xff1a;你的解壓命令用sudo了嗎&#xff1f; 這個方法不能解決的話就看下面內容。本文提供給你全過程思路。 如需轉載&#xff0c;標記出處 背景&#xff1a; 之前虛擬機和主機的復制黏貼還能用&#xff0c;今天突然用不了&#xff0c;重新下載安裝包&am…

jEasyUI 創建自定義視圖

jEasyUI 創建自定義視圖 引言 jEasyUI 是一款流行的 jQuery UI 組件庫&#xff0c;它提供了豐富的 UI 組件和交互效果&#xff0c;極大地簡化了 Web 開發的復雜度。在 jEasyUI 中&#xff0c;我們可以通過自定義視圖來擴展其功能&#xff0c;滿足特定的業務需求。本文將詳細介…

Spring MVC配置詳解:從歷史到實戰

文章目錄 一、Java Web的發展歷程1.Model I與Model II開發模式&#xff08;1&#xff09; Model I開發模式&#xff08;2&#xff09;Model II開發模式 2.MVC設計模式Spring MVC本質MVC工作流程 二、Spring MVC快速入門實戰1.環境搭建步驟&#xff08;1&#xff09;創建Maven W…

老是忘記package.json,備忘一下 webpack 環境下 Vue Cli 和 Vite 命令行工具對比

Vue 2.X webpack 環境下 Vue Cli 的命令 "scripts": {"dev": "vue-cli-service serve","prod": "vue-cli-service serve --mode production","build:dev": "vue-cli-service build --mode development"…

【樹莓派Pico FreeRTOS】-Mutex(互斥體)

Mutex(互斥體) 文章目錄 Mutex(互斥體)1、硬件準備2、軟件準備3、FreeRTOS的Mutex介紹4、完整示例RP2040 由 Raspberry Pi 設計,具有雙核 Arm Cortex-M0+ 處理器和 264KB 內部 RAM,并支持高達 16MB 的片外閃存。 廣泛的靈活 I/O 選項包括 I2C、SPI 和獨特的可編程 I/O (P…

sock文件介紹--以mysql.sock為例

socket 文件 (.sock) 通常是臨時文件。 MySQL 的 socket 文件是臨時文件&#xff0c;只在服務運行時有效。可通過配置文件更改 socket 文件的存放路徑&#xff0c;常見路徑如 /tmp/mysql.sock 或指定自定義目錄。如果連接出現問題&#xff0c;可能需要檢查 MySQL 服務狀態或路…

Docker應用部署之mysql篇(day5)

文章目錄 前言一、問題描述二、解決方案1. 搜索 MySQL 鏡像2. 拉取 MySQL 鏡像3. 創建并運行 MySQL 容器參數說明&#xff1a; 4. 驗證容器是否運行5. 進入 MySQL 容器 三、總結 前言 在日常開發和部署中&#xff0c;MySQL 是最常用的關系型數據庫之一。借助 Docker&#xff0…

【Elasticsearch基礎】基本核心概念介紹

Elasticsearch作為當前最流行的分布式搜索和分析引擎&#xff0c;其強大的功能背后是一套精心設計的核心概念體系。本文將深入解析Elasticsearch的五大核心概念&#xff0c;幫助開發者構建堅實的技術基礎&#xff0c;并為高效使用ES提供理論支撐。 1 索引&#xff08;Index&…

Qt在ARM中,如何使用drmModeObjectSetProperty 設置 Plane 的 zpos 值

在 Qt 中直接使用 drmModeObjectSetProperty 設置 Plane 的 zpos 值需要結合 Linux DRM/KMS API 和 Qt 的底層窗口系統&#xff08;如 eglfs 平臺插件&#xff09;。以下是詳細步驟和代碼示例&#xff1a; 1. 原理說明 DRM/KMS 基礎&#xff1a; Plane&#xff1a;負責圖層合成…

MFC添加免費版大漠3.1233

先創建一個MFC工程&#xff0c; 添加dm.dll 方法一&#xff1a;通過類向導-添加類-類型庫中的MFC類-文件&#xff0c;選擇dm.dll&#xff0c;如果沒有"添加類型庫中的MFC類"選項就用方法二添加 方法二&#xff1a;添加-新建項-MFC-Active或TypeLib-實現接口位置選…

【Linux】應用層協議 HTTP

應用層協議 HTTP 一. HTTP 協議1. URL 地址2. urlencode 和 urldecode3. 請求與響應格式 二. HTTP 請求方法1. GET 和 POST (重點) 三. HTTP 狀態碼四. HTTP 常見報頭五. 手寫 HTTP 服務器 HTTP&#xff08;超文本傳輸協議&#xff09;是一種應用層協議&#xff0c;用于在萬維網…