Linux第68步_旧字符设备驱动的一般模板
file_operations结构体中的函数就是我们要实现的具体操作函数。
注意:
register_chrdev()和 unregister_chrdev()这两个函数是老版本驱动使用的。现在新字符设备驱动已经不再使用这两个函数,而是使用Linux内核推荐的新字符设备驱动API函数。
1、创建CharDeviceXXX.c
输入“cd /home/zgq/linux/Linux_Drivers/回车”,切换到“/home/zgq/linux/Linux_Drivers/”目录
输入“ls回车”,查看“/home/zgq/linux/Linux_Drivers/”目录
输入“mkdir CharDeviceXXX回车”,创建“CharDeviceXXX”目录
输入“ls回车”,查看“/home/zgq/linux/Linux_Drivers/”目录
输入“cd CharDeviceXXX回车”,切换到“/home/zgq/linux/Linux_Drivers/ CharDeviceXXX/”目录
输入“vi CharDeviceXXX.c回车”,打开“CharDeviceXXX.c”
CharDeviceXXX.c文件如下:
#include <linux/types.h>
//数据类型重命名
//使能bool,u8,u16,u32,u64, uint8_t, uint16_t, uint32_t, uint64_t
//使能s8,s16,s32,s64,int8_t,int16_t,int32_t,int64_t
#include <linux/kernel.h> //必须要包含的头文件
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h> //必须要包含的头文件
#include <linux/string.h> //下面要用到字符串,显然也要包含
//#include <linux/device.h> //必须要包含的头文件
//#include <linux/fs.h> //使能结构体"file_operations"
#define CharDeviceXXX_MAJOR 200
//定义主设备号
//静态分配设备号:在串口输入“cat/proc/devices”查询当前已用的主设备号
//然后使用一个“没有被使用的设备号”作为该设备的的主设备号
#define CharDeviceXXX_NAME "CharDeviceXXXName" //定义设备的名字
static char CharDeviceXXX_readbuf[100]; //读缓冲区
static char CharDeviceXXX_writebuf[100]; //写缓冲区
static char My_DataBuffer[] = {"My Data!"};
/* 打开设备 */
static int CharDeviceXXX_open(struct inode *inode, struct file *filp)
{
/* 用户实现具体功能 */
printk("CharDeviceXXX_open!\r\n");
return 0;
}
/* 从设备读取数据,保存到首地址为buf的数据块中,长度为cnt个字节 */
//file结构指针变量flip表示要打开的设备文件
//buf表示用户数据块的首地址
//cnt表示用户数据的长度,单位为字节
//loff_t结构指针变量offt表示“相对于文件首地址的偏移”
static ssize_t CharDeviceXXX_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
int ret = 0;
memcpy(CharDeviceXXX_readbuf, My_DataBuffer,sizeof(My_DataBuffer));
//将My_DataBuffer[]中的所有数据拷贝到CharDeviceXXX_readbuf[]
ret = copy_to_user( buf, CharDeviceXXX_readbuf, cnt );
//将CharDeviceXXX_readbuf[]中的前cnt个字节拷贝到buf[]中
if(ret==0) printk("Send the data to the user, and the result is ok!\r\n");
else printk("Send the data to the user, and the result is failed!\r\n");
return 0;
}
/* 向设备写数据,将数据块首地址为buf的数据,长度为cnt个字节,发送给用户 */
//file结构指针变量flip表示要打开的设备文件
//buf表示用户数据块的首地址
//cnt表示用户数据的长度,单位为字节
//loff_t结构指针变量offt表示“相对于文件首地址的偏移”
static ssize_t CharDeviceXXX_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int ret = 0;
ret = copy_from_user(CharDeviceXXX_writebuf, buf, cnt);
//将buf[]中的前cnt个字节拷贝到CharDeviceXXX_writebuf[]中
if(ret==0) printk("Receive the data form user , and the result is ok!\r\n");
else printk("Receive the data form user , and the result is failed!\r\n");
return 0;
}
/* 关闭/释放设备 */
static int CharDeviceXXX_release(struct inode *inode, struct file *filp)
{
/* 用户实现具体功能 */
printk("CharDeviceXXX_release!\r\n");
return 0;
}
/*声明file_operations结构变量MyCharDevice_fops*/
/*它是指向设备的操作函数集合变量*/
const struct file_operations CharDeviceXXX_fops = {
.owner = THIS_MODULE,
.open = CharDeviceXXX_open,
.read = CharDeviceXXX_read,
.write = CharDeviceXXX_write,
.release = CharDeviceXXX_release,
};
/*驱动入口函数 */
static int __init CharDeviceXXX_init(void)
{
int ret;
ret = register_chrdev(CharDeviceXXX_MAJOR, CharDeviceXXX_NAME, &CharDeviceXXX_fops);
//注册字符设备驱动
//CharDeviceXXX_MAJOR为主设备号,采用宏CharDeviceXXX_NAME定义设备名字
//CharDeviceXXX_fops是设备的操作函数集合,它是file_operations结构变量
if (ret < 0)
{
pr_err("CharDeviceXXX_init is failed!!!\r\n");
return ret;
}
else pr_err("CharDeviceXXX_init is ok!!!\r\n");
return 0;
}
/*驱动出口函数 */
static void __exit CharDeviceXXX_exit(void)
{/*出口函数具体内容 */
unregister_chrdev(CharDeviceXXX_MAJOR, CharDeviceXXX_NAME);
//注销字符设备驱动
//CharDeviceXXX_MAJOR为主设备号,采用宏CharDeviceXXX_NAME定义设备名字
}
module_init(CharDeviceXXX_init);
//指定CharDeviceXXX_init()为驱动入口函数
module_exit(CharDeviceXXX_exit);
//指定CharDeviceXXX_exit()为驱动出口函数
MODULE_AUTHOR("Zhanggong");//添加作者名字
MODULE_LICENSE("GPL");//LICENSE采用“GPL协议”
MODULE_INFO(intree,"Y");
//去除显示“loading out-of-tree module taints kernel.”
2、Makefile文件的一般模板
输入“vi Makefile回车”
KERNELDIR := /home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31
#使用“:=”将其后面的字符串赋值给KERNELDIR
CURRENT_PATH := $(shell pwd)
#采用“shell pwd”获取当前打开的路径
#使用“$(变量名)”引用“变量的值”
obj-m := CharDeviceXXX.o
#生成“obj-m”需要依赖“CharDeviceXXX.o”
build: kernel_modules
#生成“build”需要依赖“kernel_modules”
@echo $(KERNELDIR)
#输出KERNELDIR的值为“/home/zgq/linux/atk-mp1/linux/linux-5.4.31”
@echo $(CURRENT_PATH)
#输出CURRENT_PATH的值为/home/zgq/linux/Linux_Drivers/CharDeviceXXX”
@echo $(MAKE)
#输出MAKE的值为make
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
#后面的"modules"表示编译成模块
#“KERNELDIR”上面定义为“/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31”,即“指定的工作目录”
#“CURRENT_PATH”上面定义为“当前的工作目录”
#“-C $(KERNELDIR) M=$(CURRENT_PATH) ”表示将“当前的工作目录”切换到“指定的目录”中
#即切换到“/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31”。
#M表示模块源码目录
#在“make和modules”之间加入“M=$(CURRENT_PATH)”,表示切换到由“CURRENT_PATH”指定的目录中读取源码,同时将其编>译为.ko 文件
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
#“KERNELDIR”上面定义为“/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31”,即“指定的工作目录”
#“CURRENT_PATH”上面定义为“当前的工作目录
3、创建“c_cpp_properties.json”
打开VSCode,按下“Ctrl+Shift+P”,打开VSCode控制台,然后输入“C/C++:Edit Configurations(JSON)”,见下图:
打开以后会自动在“.vscode ”目录下生成一个名为“c_cpp_properties.json” 的文件,此文件默认内容如下所示:
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**"
],
"defines": [],
"compilerPath": "/usr/bin/gcc",
"cStandard": "gnu11",
"cppStandard": "gnu++14",
"intelliSenseMode": "gcc-x64"
}
],
"version": 4
}
修改“includePath”后,“c_cpp_properties.json”文件如下:
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**",
"/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31",
"/home/zgq/linux/Linux_Drivers/CharDeviceXXX_1",
"/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31/arch/arm/include",
"/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31/include",
"/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31/arch/arm/include/generated"
],
"defines": [],
"compilerPath": "/usr/bin/gcc",
"cStandard": "gnu11",
"cppStandard": "gnu++14",
"intelliSenseMode": "gcc-x64"
}
],
"version": 4
}
4、了解APP程序需要用到的相关函数
编写Linux应用程序,需要用到C库里面“和文件操作有关”的函数。
1)、open()函数
int open(const char *pathname, int flags)
指针变量pathname表示要打开的设备文件名;
flags表示“文件打开模式”:O_RDONLY表示只读模式;
O_WRONLY表示只写模式;
O_RDWR表示读写模式;
其他可选模式:O_APPEND表示每次写操作都写入文件的尾部;O_CREAT表示如果指定文件不存在,则创建这个文件;
O_EXCEL表示如果要创建的文件已经存在,则返回-1,并修改errno的值;O_TRUNC表示如果文件存在,并且以“只写或读写”方式打开,则清空文件全部内容;O_NOCTTY表示如果路径名指向终端设备,不要把这个设备用作控制终端;O_NONBLOCK表示如果路径名指向FIFO/块文字/字符文件,则把文件的打开和后继I/O设置为非阻塞;O_DSYNC表示等待物理I/O结束后再write。在不影响读取新写入的数据的前提下,不等待文件属性更新;O_RSYNC表示read等待所有写入同一区域的写操作完成后再进行;O_SYNC表示等待物理I/O结束后再write,包括更新文件属性的IO;
返回值:如果文件打开成功,则返回“文件描述符”。
在终端输入“man 2 open”可以查询open()这个函数
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
需要包含上面的头文件,才可以使用open()函数
2)、read()函数
ssize_t read(int fd, void *buf, size_t count)
fd表示要进行读操作的“文件描述符”
buf表示将读到的数据保存到“以buf为首地址的数据块”
count表示读取到的“数据长度”,单位为字节
返回值:
大于0表示读取到的字节数
0表示读到了文件的尾部
小于0表示读取失败
在终端输入“man 2 read”可以查询read()这个函数
#include <unistd.h>
需要包含上面这个头文件,才可以使用read()函数
3)、write()函数
ssize_t write(int fd, const void *buf, size_t count)
fd表示要进行写操作的“文件描述符”
buf和count表示将“以buf为首地址的数据块”,长度为count个字节,发送给用户
返回值:
大于0表示写入的字节数
0表示没有写入任何数据
小于0表示写入失败
在终端输入“man 2 write”可以查询write()这个函数
#include <unistd.h>
需要包含上面这个头文件,才可以使用write()函数
4)、close()函数
int close(int fd)
fd表示要关闭的“文件描述符”
返回值:
0表示关闭成功
小于0表示关闭失败
在终端输入“man 2 close”可以查询close()这个函数
#include <unistd.h>
需要包含上面这个头文件,才可以使用close()函数
在终端输入“man 3 memcpy”在第3节中可以查询memcpy()这个函数
#include <string.h>
需要包含上面这个头文件,才可以使用memcpy()函数
linux之man命令 (baidu.com)
man后面的数字代表的内容:
1:用户在shell环境可操作的命令或执行文件;如输入“man 1 ls”就可以查询ls命令
2:系统内核可调用的函数与工具等;如输入“man 2 read”就可以查询read()函数
3:一些常用的函数(function)与函数库(library),大部分为C的函数库(libc);
;如输入“man 3 strstr”就可以查询strstr()函数
4:设备文件说明,通常在/dev下的文件
5:配置文件或某些文件格式
6:游戏(games)
7:惯例与协议等,如Linux文件系统,网络协议,ASCII code等说明
8:系统管理员可用的管理命令
9:跟kernel有关的文件。
5、编写CharDeviceXXX_APP.c
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
static char usrdataBuffer[] = {"usr data!"};
//例如当argv[]是指向输入参数“./CharDeviceXXX /dev/chrdevbase 1”
/*
参数argc: argv[]数组元素个数
参数argv[]:是一个指针数组
返回值: 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;
}
//argv[]是指向输入参数“./CharDeviceXXXApp” “/dev/CharDeviceXXX” “1”
filename = argv[1];
//argv[1]指向字符串“/dev/CharDeviceXXX”
fd = open(filename, O_RDWR);
//以“读写方式”打开“/dev/CharDeviceXXX”文件,若成功则fd为“文件描述符”
//fd=0表示标准输入流; fd=1表示标准输出流;fd=2表示错误输出流;
if(fd < 0)
{
printf("Can't open file %s\r\n", filename);
return -1;
}
else printf("open file: %s\r\n", filename);
if(atoi(argv[2]) == 1)
{
retvalue = read(fd, readbuf, 50);
//fd表示要进行读操作的“文件描述符”
//readbuf表示将读到的数据保存到“以readbuf为首地址的数据块”
//50表示读取到的“数据长度”,单位为字节
//返回值大于0表示读取到的字节数
//返回值为0表示读到了文件的尾部
//返回值小于0表示读取文件失败
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, usrdataBuffer, sizeof(usrdataBuffer));
//将usrdataBuffer[]中所有数据拷贝到writebuf[]
retvalue = write(fd, writebuf, 50);
//fd=2表示要进行写操作的“文件描述符”
//将writebuf[]中前50个字节发送给用户
//返回值大于0表示写入的字节数;
//返回值等于0表示没有写入任何数据;
//返回值小于0表示写入失败
if(retvalue < 0)
{
printf("write file %s failed!\r\n", filename);
}
}
/* 关闭设备 */
retvalue = close(fd);
//fd表示要关闭的“文件描述符”
//返回值等于0表示关闭成功
//返回值小于0表示关闭失败
if(retvalue < 0)
{
printf("Can't close file %s\r\n", filename);
return -1;
}
return 0;
}
6、编写脚本文件
CharDeviceXXX_APP.sh文件内容如下:
#!/bin/sh
file="CharDeviceXXX_APP"
if [ -s $file ]
#"-s $file"检测文件是否为空(文件大小是否大于0),不为空返回 true
then
rm CharDeviceXXX_APP
echo clear CharDeviceXXX_APP
else
arm-none-linux-gnueabihf-gcc CharDeviceXXX_APP.c -o CharDeviceXXX_APP
echo CharDeviceXXX_APP
fi
7、测试
输入“make回车”编译生成“CharDeviceXXX.ko”
输入“chmod 777 CharDeviceXXX_APP.sh回车”
将“CharDeviceXXX_APP.sh”赋可执行权限
输入“./CharDeviceXXX_APP.sh回车”,编译生成“CharDeviceXXX_APP”文件
输入“sudo cp CharDeviceXXX.ko CharDeviceXXX_APP /home/zgq/linux/nfs/rootfs/lib/modules/5.4.31/ -f回车”
启动开发板,从网络下载程序
输入“root”
输入“cd /lib/modules/5.4.31/回车”
切换到“/lib/modules/5.4.31/”目录
注意:“lib/modules/5.4.31/”在虚拟机中是位于“/home/zgq/linux/nfs/rootfs/”目录下,但在开发板中,却是位于根目录中。
输入“ls”查看“CharDeviceXXX.ko和CharDeviceXXXApp”是否存在
输入“depmod”,驱动在第一次执行时,需要运行“depmod”
输入“modprobe CharDeviceXXX.ko”,加载“CharDeviceXXX.ko”模块
输入“lsmod”查看有哪些驱动在工作
输入“mknod /dev/CharDeviceXXX c 200 0回车”
//“mknod”是创建节点命令
//“/dev/CharDeviceXXX”表示节点文件
//“c”表示CharDeviceXXX是个字符设备
//“200”表示设备的主设备号
//“0”表示设备的次设备号
输入“ls /dev/CharDeviceXXX -l回车”,发现节点文件“/dev/CharDeviceXXX”
输入“./CharDeviceXXX_APP /dev/CharDeviceXXX 1回车”执行读操作
输入“./CharDeviceXXX_APP /dev/CharDeviceXXX 2回车”执行写操作
输入“cat /proc/devices回车”查询设备号
操作完成,则执行卸载模块:
输入“rmmod CharDeviceXXX.ko”,卸载“CharDeviceXXX.ko”模块
注意:输入“rmmod CharDeviceXXX”也可以卸载“CharDeviceXXX.ko”模块
输入“lsmod”查看有哪些驱动在工作。
至此,CharDeviceXXX设备的整个驱动就验证完成了,驱动工作正常。
8、修改Makefile文件如下:
KERNELDIR := /home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31
#使用“:=”将其后面的字符串赋值给KERNELDIR
CURRENT_PATH := $(shell pwd)
#采用“shell pwd”获取当前打开的路径
#使用“$(变量名)”引用“变量的值”
obj-m := CharDeviceXXX.o
#生成“obj-m”需要依赖“CharDeviceXXX.o”
ko: kernel_modules
#生成“build”需要依赖“kernel_modules”
@echo $(KERNELDIR)
#输出KERNELDIR的值为“/home/zgq/linux/atk-mp1/linux/linux-5.4.31”
@echo $(CURRENT_PATH)
#输出CURRENT_PATH的值为/home/zgq/linux/Linux_Drivers/CharDeviceXXX”
@echo $(MAKE)
#输出MAKE的值为make
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
#后面的"modules"表示编译成模块
#“KERNELDIR”上面定义为“/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31”,即“指定的工作目录”
#“CURRENT_PATH”上面定义为“当前的工作目录”
#“-C $(KERNELDIR) M=$(CURRENT_PATH) ”表示将“当前的工作目录”切换到“指定的目录”中
#即切换到“/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31”。
#M表示模块源码目录
#在“make和modules”之间加入“M=$(CURRENT_PATH)”,表示切换到由“CURRENT_PATH”指定的目录中读取源码,同时将其编>译为.ko 文件
clean_ko:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
#“KERNELDIR”上面定义为“/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31”,即“指定的工作目录”
#“CURRENT_PATH”上面定义为“当前的工作目录
app:
arm-none-linux-gnueabihf-gcc CharDeviceXXX_APP.c -o CharDeviceXXX_APP
clean_app:
rm CharDeviceXXX_APP
9、使用Makefile编译
输入“rm CharDeviceXXX_APP.sh回车”
输入“make clean_ko回车”,清除CharDeviceXXX.*
输入“make ko回车”,编译生成CharDeviceXXX.ko
输入“make clean_app回车”,清除CharDeviceXXX_APP
输入“make app回车”,编译生成CharDeviceXXX_APP
输入“ls -l回车”
输入“sudo cp CharDeviceXXX.ko CharDeviceXXX_APP /home/zgq/linux/nfs/rootfs/lib/modules/5.4.31/ -f回车”
相关文章:

Linux第68步_旧字符设备驱动的一般模板
file_operations结构体中的函数就是我们要实现的具体操作函数。 注意: register_chrdev()和 unregister_chrdev()这两个函数是老版本驱动使用的。现在新字符设备驱动已经不再使用这两个函数,而是使用Linux内核推荐的新字符设备驱动API函数。 1、创建C…...

23种设计模式——工厂方法模式
定义: 一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其他子类。 工厂方法通用类图: 这个图更好理解 在工厂方法模式中,抽象产品类Product负责定义产品的共性,实现对事物最抽象的…...

水豚鼠标助手 强大的鼠标美化工具
水豚鼠标助手 水豚鼠标助手是一款 鼠标换肤、屏幕画笔、放大镜、聚光灯、屏幕放大、倒计时功能的强大屏幕演示工具。 软件助手获取 水豚鼠标助手1.0.0 安装教程 第一步:下载后,双击软件安装包 第二步:Windows可能会出现提示弹窗ÿ…...

ArrayList集合源码分析
ArrayList集合源码分析 文章目录 ArrayList集合源码分析一、字段分析二、构造方法分析三、方法分析四、总结 内容如有错误或者其他需要注意的知识点,欢迎指正或者探讨补充,共同进步。 一、字段分析 //默认初始化容量。这里和Vector一样,只是…...

循环队列与循环双端队列
文章目录 前言循环队列循环双端队列 前言 1、学习循环队列和循环双端队列能加深我们对队列的理解,提高我们的编程能力。 2、本文循环队列使用的是数组,循环双端队列用的是双向链表 3、题目连接:设计循环队列 ,设计循环双端队列。 …...

https【详解】与http的区别,对称加密,非对称加密,证书,解析流程图
http 和 https 的区别 http 是明文传输,敏感信息容易在传输过程中被劫持https http加密,劫持了也无法解密 https 用到的加密方式 https 同时使用了对称加密和非对称加密,之所以没有全部使用非对称加密,是因为非对称加密的运算更加…...

(C语言)qsort函数模拟实现
前言 我们需先了解qsort函数 qsort函数详解:http://t.csdnimg.cn/rTNv9 qsort函数可以排序多种数据类型,很是神奇,这是为什么,我们在里模拟实现这样的功能 目录 1. qsort函数模拟实现 2. 我们使用bubble_sort函数排序整形数…...

WordPress建站入门教程:如何在本地电脑搭建WordPress网站?
前面跟大家分享了『WordPress建站入门教程:如何安装本地WordPress网站运行环境?』,接下来boke112百科就继续跟大家分享本地电脑如何搭建WordPress网站。 小皮面板(phpstudy)的“软件管理 – 网站程序”虽然可以一键部…...
Vue3教程
1.1 配置环境 vue官网: Vue.js - The Progressive JavaScript Framework | Vue.js 终端 Linux和Mac上可以用自带的终端。 Windows上推荐用powershell或者cmd。Git Bash有些指令不兼容。 安装Nodejs 安装地址: Node.js 安装vue/cli 打开Git Bash&#x…...

Linux系统Docker部署RStudio Server
文章目录 前言1. 安装RStudio Server2. 本地访问3. Linux 安装cpolar4. 配置RStudio server公网访问地址5. 公网远程访问RStudio6. 固定RStudio公网地址 前言 RStudio Server 使你能够在 Linux 服务器上运行你所熟悉和喜爱的 RStudio IDE,并通过 Web 浏览器进行访问…...
【C++】每周一题——2024.3.3(手滑再再写一篇)
题目 Cpp 【问题描述】 求N个字符串的最长公共子串,2 < N<=20,字符串长度不超过255。 例如:N=3,由键盘依次输入三个字符串为 What is local bus? Name some local buses. local bus is a h…...

TabLayout与ToolBar、ViewPager的使用
目录 1. 在ToolBar中添加TabLayout 2. 将工具栏设为活动栏 3. 初始化TabLayout 4. TabLayout监听器 可以在ToolBar工具栏中添加TabLayout配合,效果如下图。 1. 在ToolBar中添加TabLayout TabLayout的常用属性有: tabBackground 指定标签的背景 t…...

链表基础知识详解(非常详细简单易懂)
概述: 链表作为 C 语言中一种基础的数据结构,在平时写程序的时候用的并不多,但在操作系统里面使用的非常多。不管是RTOS还是Linux等使用非常广泛,所以必须要搞懂链表,链表分为单向链表和双向链表,单向链表很…...

SAP PP学习笔记05 - BOM配置(Customize)1 - 修正参数
上次学习了BOM相关的内容。 SAP PP学习笔记04 - BOM1 - BOM创建,用途,形式,默认值,群组BOM等_sap销售bom与生产bom-CSDN博客 SAP PP学习笔记04 - BOM2 -通过Serial来做简单的BOM变式配置,副明细,BOM状态&…...

前端从普通登录到单点登录(SSO)
随着前端登录场景的日益复杂化和技术思想的不断演进,前端在登录方面的知识结构变得越来越复杂。对于前端开发者来说,在日常工作中根据不同的登录场景提供合适的解决方案是我们的职责所在,本文将梳理前端登录的演变过程。 1、无状态的HTTP H…...
考研总计划(基础篇)
分为数学,专业课,英语三个部分 数学规划表 高数基础:3月初到4月15号 具体实行计划:分为看课日和写题日 看课日:早上10点到12点半看课,19:30到21:30继续看课。 写题日:早上10点到12点半复习前一天的题目࿰…...

力扣周赛387
第一题 代码 package Competition.The387Competitioin;public class Demo1 {public static void main(String[] args) {}public int[] resultArray(int[] nums) {int ans[]new int[nums.length];int arr1[]new int[nums.length];int arr2[]new int[nums.length];if(nums.leng…...

部署PhotoMaker通过堆叠 ID 嵌入自定义逼真的人物照片
PhotoMaker只需要一张人脸照片就可以生成不同风格的人物照片,可以快速出图,无需额外的LoRA培训。 安装环境 python 3.10gitVisual Studio 2022 安装依赖库 git clone https://github.com/bmaltais/PhotoMaker.git cd PhotoMaker python -m venv venv…...

挑战杯 基于深度学习的中文情感分类 - 卷积神经网络 情感分类 情感分析 情感识别 评论情感分类
文章目录 1 前言2 情感文本分类2.1 参考论文2.2 输入层2.3 第一层卷积层:2.4 池化层:2.5 全连接softmax层:2.6 训练方案 3 实现3.1 sentence部分3.2 filters部分3.3 featuremaps部分3.4 1max部分3.5 concat1max部分3.6 关键代码 4 实现效果4.…...
关于RSA公私钥加密报错Data must not be longer than 117 bytes问题解决办法
一、问题描述 1.背景 大家都知道,在日常项目开发过程中,数据的传输安全一直都是值得重视的问题,当然了市面上解决此类办法的技术也有很多,本项目在提供给第三方使用是数据以及校验第三方传递的参数,采用常用的RSA公私…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...

自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...

深度学习水论文:mamba+图像增强
🧀当前视觉领域对高效长序列建模需求激增,对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模,以及动态计算优势,在图像质量提升和细节恢复方面有难以替代的作用。 🧀因此短时间内,就有不…...

免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...

DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态
前言 在人工智能技术飞速发展的今天,深度学习与大模型技术已成为推动行业变革的核心驱动力,而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心,系统性地呈现了两部深度技术著作的精华:…...
Python 高效图像帧提取与视频编码:实战指南
Python 高效图像帧提取与视频编码:实战指南 在音视频处理领域,图像帧提取与视频编码是基础但极具挑战性的任务。Python 结合强大的第三方库(如 OpenCV、FFmpeg、PyAV),可以高效处理视频流,实现快速帧提取、压缩编码等关键功能。本文将深入介绍如何优化这些流程,提高处理…...