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回车

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

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

相关文章

公众号公司主体变更如何操作?

公众号迁移有什么用&#xff1f;只能改主体吗&#xff1f;好多朋友都想做公众号迁移&#xff0c;但是又不太清楚具体有啥用&#xff0c;今天我就来详细说说。首先&#xff0c;公众号迁移最重要的作用就是可以修改主体。比如你的公众号原来是 A 公司的&#xff0c;现在 A 公司不…

SpringCloud(20)之Skywalking Agent原理剖析

一、Agent原理剖析 使用Skywalking的时候&#xff0c;并没有修改程序中任何一行 Java 代码&#xff0c;这里便使用到了 Java Agent 技术&#xff0c;我 们接下来展开对Java Agent 技术的学习。 1.1 Java Agent Java Agent 是从 JDK1.5 开始引入的&#xff0c;算是一个比较老的…

STL中push_back和emplace_back效率的对比

文章目录 过程对比1.通过构造参数向vector中插入对象&#xff08;emplace_back更高效&#xff09;2.通过插入实例对象&#xff08;调用copy函数&#xff09;3.通过插入临时对象&#xff08;调用move函数&#xff09; 效率对比emplace_back 的缺点 我们以STL中的vector容器为例。…

解决 Pandas 导出文件出现 dtype: object 字样

文章目录 1. 问题2. 解决方法 1. 问题 python 用 pandas 输出 excel 文件时&#xff0c;发现有些列的单元格出现 “dtype: object” 的字样&#xff0c;如下图&#xff1a; 这是 pandas 没有处理好导致的 2. 解决方法 结果用 .values 进行输出&#xff0c;这样就转成字符串…

聊天室项目

服务器 #include <myhead.h> #define SER_IP "192.168.122.39" #define SER_PORT 8888 typedef struct Node //链表存储客户端的所有信息 {struct sockaddr_in cin; //存储客户端的网络地址信息struct Node *next; }*List; typedef struct Message//消息结构…

洛谷 P1731 [NOI1999] 生日蛋糕

题目 题目链接 自己没看题解写的&#xff0c;摸石头过河&#xff0c;解释一下 首先&#xff0c;输入输出都是正整数。先搞定输入&#xff0c;再判断条件&#xff0c;如果无解&#xff0c;输出0&#xff0c;否则输出蛋糕外表面面积Q&#xff08;这里用全局变量&#xff0c;开l…

数据库:2024/3/6

作业1&#xff1a;使用C语言完成数据库的增删改 代码&#xff1a; #include <myhead.h>//定义添加员工信息函数 int Add_worker(sqlite3 *ppDb) {//准备sql语句printf("请输入要添加的员工信息:\n");//从终端获取员工信息char rbuf[128]"";fgets(r…

React学习

&#x1f4d1;前言 本文主要是【React】——React基础的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f304;每日一句&#x…

C++ 哈希表OJ

目录 1、1. 两数之和 2、面试题 01.02. 判定是否互为字符重排 3、217. 存在重复元素 4、 219. 存在重复元素 II 5、49. 字母异位词分组 频繁查找某一个数的时候可以使用哈希表&#xff0c;哈希表可以使用容器&#xff0c;也可以使用数组模拟&#xff0c;当元素是字符串中的字…

算法打卡day9|栈与队列篇01|Leetcode 232.用栈实现队列、225. 用队列实现栈

栈与队列理论基础 栈&#xff08;Stack&#xff09; 栈是一种后进先出&#xff08;LIFO&#xff09;的数据结构,即最近添加到栈中的元素将是第一个被移除的元素。 栈通常有两个主要的操作&#xff1a;push 用于添加一个元素到栈顶&#xff0c;而 pop 用于移除栈顶的元素。此外…

二维码样式修改如何在线处理?在电脑上改二维码图案的方法

随着网络的不断发展&#xff0c;二维码的应用场景不断增多&#xff0c;很多人都会将内容放到二维码中&#xff0c;通过扫码的方式将储存在云端的数据调取显示。而面对不同的用途时&#xff0c;对二维码的样式也会有单独的要求&#xff0c;比如需要改变颜色、加入文字、logo、尺…

网络调试助手使用MQTT协议与Mosquitto通信(3)

一、连接报文 一开始设备需要连接到mqtt服务器&#xff0c;连接时的数据包内需要携带对应的设备ID&#xff0c;以及用户名和密码。这使用默认的用户名和密码。设备ID每一个设备都需要设置为不同的&#xff0c;两个相同的ID只能允许一台设备在线&#xff0c;另一个相同的ID的设备…

【C++庖丁解牛】模版初阶

&#x1f4d9; 作者简介 &#xff1a;RO-BERRY &#x1f4d7; 学习方向&#xff1a;致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f4d2; 日后方向 : 偏向于CPP开发以及大数据方向&#xff0c;欢迎各位关注&#xff0c;谢谢各位的支持 目录 1. 泛型编程2. 函数模…

Dgraph 入门教程二《 快速开始》

1、Clound 云 云地址&#xff1a;Dgraph Cloud 登录Clound 云后&#xff0c;可以用云上的东西操作&#xff0c;可以用谷歌账号或者github账号登录。 启动云 &#xff08;1&#xff09;在云控制台&#xff0c;点击 Launch new backend. &#xff08;2&#xff09;选择计划&…

【PowerMockito:编写单元测试过程中原方法使用@Value注解注入的属性出现空指针】

错误场景 执行到Value的属性时会出现空指针&#xff0c;因为Value的属性为null 解决方法 在测试类调用被测试方法前&#xff0c;提前设置属性值&#xff0c;属性可以先自己定义好 ReflectionTestUtils.setField(endpointConnectionService, "exportUdpList", lis…

工业深度学习异常缺陷检测实战

在工业生产过程中&#xff0c;由于现有技术、工作条件等因素的不足和局限性&#xff0c;极易影响制成品的质量。其中&#xff0c;表面缺陷是产品质量受到影响的最直观表现&#xff0c;因此&#xff0c;为了保证合格率和可靠的质量&#xff0c;必须进行产品表面缺陷检测。 “缺陷…

制片管理工具:提高制片效率的必备工具

一、什么是制片管理工具 制片管理工具是一种为制片人提供支持和协助的软件或工具&#xff0c;并提供一种集中管理制作进度、任务分配、成本预算、资源管理和进度跟踪的方式。它可以帮助制片人在项目的开发、制作和发布方面更有效地进行规划和监督&#xff0c;确保整个流程能够…

LLM | Gemma的初体验

一起来体验一下吧~ 技术报告书&#xff1a;jgoogle/gemma-7b-it Hugging Facegemma-report.pdf (storage.googleapis.com) 代码1 &#xff1a;google-deepmind/gemma: Open weights LLM from Google DeepMind. (github.com) 代码2 &#xff1a;https://github.com/google/gem…

报名开启丨掘金海外,探寻泛娱乐社交APP出海新风口

随着国内泛娱乐行业用户规模趋于见顶&#xff0c;泛娱乐社交APP转向出海是必然趋势。 根据行业数据显示&#xff0c;有超过35%的国内实时社交企业已启动或者正在规划出海&#xff0c;而其中出海商户的音视频流量增长均超过了100&#xff05;。尤其是在东南亚、中东、拉美等新兴…

Maya笔记 软选择

文章目录 1什么是软选择2注意3如何打开软选择3.1方法一3.2方法二 4调整软选择的范围5衰减模式5.1体积模式5.2表面模式 6衰减曲线 1什么是软选择 也就是渐变选择&#xff0c;从中心点向外影响力度越来越小 软选择针对的是点线面这些模型元素 下图中展示了对被软选择的区域移动…