当前位置: 首页 > news >正文

Linux驱动开发-字符设备驱动开发

  • linux 驱动开发
  • 1. 驱动程序的类型
  • 2. 驱动开发流程
  • 字符设备驱动
    • 1. 基本概念
    • 2. 字符设备驱动的基本结构
  • 架构
  • 字符设备驱动开发中常用的 API
  • 示例
  • 以下代码加入了设备类和设备实例的创建

linux 驱动开发

1. 驱动程序的类型

在 Linux 中,驱动程序主要有以下几种类型:

字符设备驱动:处理字节流的设备,如串口、键盘等。它们通过字符设备接口(如 /dev/tty)与用户空间进行交互。

块设备驱动:处理块存储设备,如硬盘、SSD 等。它们支持随机访问,提供高效的数据传输。

网络设备驱动:用于网络接口卡,处理网络数据包的发送和接收。

USB 驱动:支持 USB 设备的连接和管理。

2. 驱动开发流程

以下是开发 Linux 驱动的一般流程:

2.1 环境准备
确保你有一个支持的 Linux 发行版。
安装开发工具,如 gcc 和 make。
下载和编译 Linux 内核源代码(与你的内核版本一致)。2.2 编写驱动代码
根据你要开发的设备类型,选择合适的接口。
创建相应的初始化(init)和退出(exit)函数。
根据设备实现文件操作结构体 file_operations。2.3 编写 Makefile
创建 Makefile,用于编译你的模块。2.4 编译模块
在终端中通过 make 命令编译模块。2.5 加载和测试模块
使用 insmod 加载模块,使用 rmmod 卸载模块。
通过 dmesg 查看内核日志,调试和验证模块功能。

字符设备驱动

字符设备驱动是Linux内核中的一种设备驱动类型,
主要用于处理字符设备(如键盘、鼠标、串口等)。
字符设备以字节流的形式进行数据传输,与块设备(如硬盘、SSD等)不同,
块设备以固定大小的块进行数据传输。

下面是一个简单的字符设备驱动入门指南:

1. 基本概念

字符设备(Character Device):以字节流的形式进行数据传输的设备。

设备文件:在Linux中,字符设备通过设备文件(通常位于/dev目录下)与用户空间进行交互。

主设备号和次设备号:设备文件由主设备号和次设备号标识。主设备号用于标识设备驱动,次设备号用于标识特定的设备实例。

2. 字符设备驱动的基本结构

字符设备驱动通常包括以下几个部分:

设备注册:向内核注册字符设备。

文件操作接口:定义设备文件的操作接口(如open, read, write, close等)。

设备操作函数:实现具体的设备操作逻辑。

架构

字符设备驱动程序在 Linux 中的架构通常包括以下几个关键组件:设备注册: 使用 register_chrdev 注册设备,并提供操作函数。驱动程序需要处理打开、读取、写入和关闭操作,通常通过定义 file_operations 结构体来实现。打开、读写、关闭接口: 驱动程序需要实现用户空间程序可以调用的接口。读取和写入操作使用 copy_to_user 和 copy_from_user 进行数据传输。内存管理: 使用 kmalloc 和 kfree 进行动态内存分配和释放,以管理驱动所需的数据结构。进程同步: 使用等待队列(基于 wait_queue_head_t)来管理进程的等待与唤醒,实现异步操作。错误处理: 驱动程序需要实现适当的错误处理机制,以确保在发生错误时能够提供适当的反馈。

字符设备驱动开发中常用的 API

/** 1. register_chrdev* 功能:注册字符设备。* 参数:*    int nr: 主设备号。如果设置为 0,内核会自动分配一个主设备号。*    const char *name: 设备的名字,将显示在 /proc/devices 中。*    struct file_operations *fops: 指向文件操作结构体的指针,定义设备的操作函数。* 返回值:*    返回主设备号,如果发生错误,则返回负值。*/
int register_chrdev(unsigned int nr, const char *name, struct file_operations *fops);/** 2. unregister_chrdev* 功能:注销字符设备。* 参数:*    int nr: 要注销的主设备号。*    const char *name: 设备的名字。* 返回值:*    无。*/
void unregister_chrdev(unsigned int nr, const char *name);/** 3. copy_to_user* 功能:将数据从内核空间复制到用户空间。* 参数:*    void __user *to: 用户空间的目标地址。*    const void *from: 内核空间的源地址。*    unsigned long count: 要复制的字节数。* 返回值:*    返回未成功复制的字节数。如果为 0,表示成功。*/
long copy_to_user(void __user *to, const void *from, unsigned long count);/** 4. copy_from_user* 功能:将数据从用户空间复制到内核空间。* 参数:*    void *to: 内核空间的目标地址。*    const void __user *from: 用户空间的源地址。*    unsigned long count: 要复制的字节数。* 返回值:*    返回未成功复制的字节数。如果为 0,表示成功。*/
long copy_from_user(void *to, const void __user *from, unsigned long count);/** 5. kmalloc* 功能:分配内核内存。* 参数:*    size_t size: 要分配的内存字节数。*    gfp_t flags: 分配内存的标志,如 GFP_KERNEL。* 返回值:*    返回指向分配内存区域的指针。如果分配失败,返回 NULL。*/
void *kmalloc(size_t size, gfp_t flags);/** 6. kfree* 功能:释放内核内存。* 参数:*    void *ptr: 指向要释放内存的指针。* 返回值:*    无。*/
void kfree(void *ptr);/** 7. init_waitqueue_head* 功能:初始化等待队列头。* 参数:*    wait_queue_head_t *q: 等待队列头指针。* 返回值:*    无。*/
void init_waitqueue_head(wait_queue_head_t *q);/** 8. wait_event* 功能:将当前进程放入等待队列,直到条件满足为止。* 参数:*    wait_queue_head_t *q: 等待队列头指针。*    int condition: 条件表达式,当其评估为真时,返回进程。* 返回值:*    无。*/
#define wait_event(q, condition) wait_event_interruptible(q, condition)/** 9. wake_up* 功能:唤醒等待队列中的进程。* 参数:*    wait_queue_head_t *q: 等待队列头指针。* 返回值:*    无。*/
void wake_up(wait_queue_head_t *q);/** 10. printk* 功能:在内核中打印日志信息。* 参数:*    const char *fmt: 格式化字符串。* 返回值:*    返回打印的字符数。*/
int printk(const char *fmt, ...);

示例

#include <linux/module.h>   // 包含模块相关的宏和函数
#include <linux/init.h>     // 包含模块初始化和退出相关的宏
#include <linux/fs.h>       // 包含文件系统相关的函数和结构体
#include <linux/cdev.h>     // 包含字符设备相关的函数和结构体
#include <linux/uaccess.h>  // 包含用户空间和内核空间数据传输的函数// 设备号
static dev_t dev_num;// 字符设备结构体
static struct cdev chr_dev;// 设备缓冲区
static char buffer[1024];// 缓冲区大小
static int buffer_size = 0;// 模块许可证
MODULE_LICENSE("GPL");// 模块作者
MODULE_AUTHOR("gopher");// 模块描述
MODULE_DESCRIPTION("A simple character device driver");// 打开设备文件时的回调函数
// 打开设备文件时,内核会创建设备文件结构体和相关资源
// 该函数在内核空间中执行,因此不能有进程上下文的操作
// 该函数的返回值是成功与否的标志
//@param struct inode *inode 设备文件节点结构体
// @param struct file *file 设备文件结构体
static int chr_dev_open(struct inode *inode, struct file *file) {printk(KERN_INFO "chr_dev: Device opened\n");//打印设备号return 0;
}// 关闭设备文件时的回调函数
// 关闭设备文件时,内核会释放设备文件结构体和相关资源
// 该函数在内核空间中执行,因此不能有进程上下文的操作
// 该函数的返回值是成功与否的标志
//@param struct inode *inode 设备文件节点结构体
// @param struct file *file 设备文件结构体
static int chr_dev_release(struct inode *inode, struct file *file) {printk(KERN_INFO "chr_dev: Device closed\n");return 0;
}// 从设备读取数据时的回调函数
// 从设备读取数据时,内核会将数据从内核缓冲区复制到用户空间缓冲区
// 并更新读取位置
// 从设备读取数据时,内核会返回实际读取的字节数
// 如果读取位置超出缓冲区大小,则返回0
// 如果发生其他错误,则返回-EFAULT
// 成功读取数据时,返回实际读取的字节数
// 注意:从设备读取数据时,内核不会阻塞,而是立即返回
//@param struct file *file 设备文件结构体
// @param char __user *user_buf 用户空间缓冲区
// @param size_t count 要读取的字节数
// @param loff_t *ppos 读取位置指针
static ssize_t chr_dev_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) {int ret;// 检查读取位置是否超出缓冲区大小if (*ppos >= buffer_size)return 0;// 将数据从内核缓冲区复制到用户空间缓冲区ret = copy_to_user(user_buf, buffer + *ppos, count);if (ret)return -EFAULT;  // 复制失败printk(KERN_INFO "chr_dev: Read %d bytes from device\n", count);// 更新读取位置*ppos += count;return count;
}// 向设备写入数据时的回调函数
// 向设备写入数据时,内核会将数据从用户空间缓冲区复制到内核缓冲区
// 并更新缓冲区大小
// 向设备写入数据时,内核会返回实际写入的字节数
// 如果缓冲区已满,则返回-ENOSPC
// 如果发生其他错误,则返回-EFAULT
// 成功写入数据时,返回实际写入的字节数
// 注意:向设备写入数据时,内核不会阻塞,而是立即返回
//@param struct file *file 设备文件结构体
// @param const char __user *user_buf 用户空间缓冲区
// @param size_t count 要写入的字节数
// @param loff_t *ppos 读取位置指针
static ssize_t chr_dev_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) {int ret;// 检查缓冲区是否有足够的空间if (count > sizeof(buffer) - buffer_size)return -ENOSPC;  // 空间不足// 将数据从用户空间缓冲区复制到内核缓冲区//@param void *to 目的地址// @param const void *from 源地址// @param size_t n 字节数ret = copy_from_user(buffer + buffer_size, user_buf, count);if (ret)return -EFAULT;  // 复制失败// 打印调试信息printk(KERN_INFO "chr_dev: Wrote %d bytes to device\n", count);// 更新缓冲区大小buffer_size += count;return count;
}// 文件操作结构体,定义了设备文件的操作接口
// 包含了文件操作的回调函数和一些其他属性
// 这些回调函数在设备文件被打开、关闭、读、写时被调用
static struct file_operations chr_dev_fops = {.owner = THIS_MODULE,  // 模块所有者.open = chr_dev_open,  // 打开设备文件的回调函数.release = chr_dev_release,  // 关闭设备文件的回调函数.read = chr_dev_read,  // 从设备读取数据的回调函数.write = chr_dev_write,  // 向设备写入数据的回调函数
};// 模块初始化函数
static int __init chr_dev_init(void) {int ret;// 分配设备号//@param dev_t dev_num 设备号// @param unsigned int major 主设备号// @param unsigned int minor 次设备号// @param const char *name 设备名ret = alloc_chrdev_region(&dev_num, 0, 1, "chr_dev");if (ret < 0) {printk(KERN_ERR "chr_dev: Failed to allocate device number\n");return ret;}// 初始化字符设备//@param struct cdev *cdev 字符设备结构体// @param struct file_operations *fops 字符设备操作结构体cdev_init(&chr_dev, &chr_dev_fops);chr_dev.owner = THIS_MODULE;// 添加字符设备//@param struct cdev *cdev 字符设备结构体// @param dev_t dev 设备号// @param unsigned int count 设备号计数ret = cdev_add(&chr_dev, dev_num, 1);if (ret < 0) {printk(KERN_ERR "chr_dev: Failed to add character device\n");unregister_chrdev_region(dev_num, 1);return ret;}printk(KERN_INFO "chr_dev: Device initialized\n");//打印设备号printk(KERN_INFO "chr_dev: Major number: %d\n", MAJOR(dev_num));printk(KERN_INFO "chr_dev: Minor number: %d\n", MINOR(dev_num));return 0;
}// 模块退出函数
static void __exit chr_dev_exit(void) {// 删除字符设备//@param dev_t dev 设备号/cdev_del(&chr_dev);// 释放设备号//@param dev_t dev 设备号 // @param unsigned int count 设备号计数unregister_chrdev_region(dev_num, 1);(KERN_INFO "chr_dev: Device removed\n");
}// 注册模块初始化函数
// 内核在加载模块时,调用模块的初始化函数
// 该函数在内核空间中执行,因此不能有进程上下文的操作
// 该函数的返回值是模块初始化成功与否的标志
module_init(chr_dev_init);// 注册模块退出函数
// 内核在卸载模块时,调用模块的退出函数
// 该函数在内核空间中执行,因此不能有进程上下文的操作
module_exit(chr_dev_exit);

以下代码加入了设备类和设备实例的创建


/*以下代码加入了设备类和设备实例的创建*/
#include <linux/module.h>   // 包含模块相关的宏和函数
#include <linux/init.h>     // 包含模块初始化和退出相关的宏
#include <linux/fs.h>       // 包含文件系统相关的函数和结构体
#include <linux/cdev.h>     // 包含字符设备相关的函数和结构体
#include <linux/uaccess.h>  // 包含用户空间和内核空间数据传输的函数
#include <linux/device.h>   // 包含设备类和设备实例相关的函数// 设备号
static dev_t dev_num;// 字符设备结构体
static struct cdev chr_dev;// 设备缓冲区
static char buffer[1024];// 缓冲区大小
static int buffer_size = 0;// 设备类
static struct class *chr_dev_class;// 设备实例
static struct device *chr_dev_device;// 模块许可证
MODULE_LICENSE("GPL");// 模块作者
MODULE_AUTHOR("gopher");// 模块描述
MODULE_DESCRIPTION("A simple character device driver");// 打开设备文件时的回调函数
static int chr_dev_open(struct inode *inode, struct file *file) {printk(KERN_INFO "chr_dev: Device opened\n");return 0;
}// 关闭设备文件时的回调函数
static int chr_dev_release(struct inode *inode, struct file *file) {printk(KERN_INFO "chr_dev: Device closed\n");return 0;
}// 从设备读取数据时的回调函数
static ssize_t chr_dev_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) {int ret;if (*ppos >= buffer_size)return 0;ret = copy_to_user(user_buf, buffer + *ppos, count);if (ret)return -EFAULT;printk(KERN_INFO "chr_dev: Read %zu bytes from device\n", count);*ppos += count;return count;
}// 向设备写入数据时的回调函数
static ssize_t chr_dev_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) {int ret;if (count > sizeof(buffer) - buffer_size)return -ENOSPC;ret = copy_from_user(buffer + buffer_size, user_buf, count);if (ret)return -EFAULT;printk(KERN_INFO "chr_dev: Wrote %zu bytes to device\n", count);buffer_size += count;return count;
}// 文件操作结构体
static struct file_operations chr_dev_fops = {.owner = THIS_MODULE,.open = chr_dev_open,.release = chr_dev_release,.read = chr_dev_read,.write = chr_dev_write,
};// 模块初始化函数
static int __init chr_dev_init(void) {int ret;// 分配设备号ret = alloc_chrdev_region(&dev_num, 0, 1, "chr_dev");if (ret < 0) {printk(KERN_ERR "chr_dev: Failed to allocate device number\n");return ret;}// 初始化字符设备cdev_init(&chr_dev, &chr_dev_fops);chr_dev.owner = THIS_MODULE;// 添加字符设备ret = cdev_add(&chr_dev, dev_num, 1);if (ret < 0) {printk(KERN_ERR "chr_dev: Failed to add character device\n");unregister_chrdev_region(dev_num, 1);return ret;}// 创建设备类// 用于管理设备实例// @param struct module *owner 模块所有者// @param const char *name 设备类名// @return struct class * 设备类结构体chr_dev_class = class_create(THIS_MODULE, "chr_dev_class");if (IS_ERR(chr_dev_class)) {// 出错printk(KERN_ERR "chr_dev: Failed to create device class\n");cdev_del(&chr_dev);// 删除字符设备unregister_chrdev_region(dev_num, 1);// 释放设备号return PTR_ERR(chr_dev_class);// 返回错误码}// 创建设备实例// 用于管理设备文件// @param struct class *class 设备类结构体// @param struct device *parent 父设备实例// @param dev_t dev 设备号// @param void *drvdata 设备数据// @param const char *fmt 设备名格式// @return struct device * 设备实例结构体chr_dev_device = device_create(chr_dev_class, NULL, dev_num, NULL, "chr_dev");if (IS_ERR(chr_dev_device)) {printk(KERN_ERR "chr_dev: Failed to create device\n");class_destroy(chr_dev_class);// 删除设备类cdev_del(&chr_dev);// 删除字符设备unregister_chrdev_region(dev_num, 1);// 释放设备号return PTR_ERR(chr_dev_device);// 返回错误码}printk(KERN_INFO "chr_dev: Device initialized\n");// 打印调试信息printk(KERN_INFO "chr_dev: Major number: %d\n", MAJOR(dev_num));// 打印设备号printk(KERN_INFO "chr_dev: Minor number: %d\n", MINOR(dev_num));// 打印设备号return 0;
}// 模块退出函数
static void __exit chr_dev_exit(void) {// 删除设备实例device_destroy(chr_dev_class, dev_num);// 删除设备类class_destroy(chr_dev_class);// 删除字符设备cdev_del(&chr_dev);// 释放设备号unregister_chrdev_region(dev_num, 1);printk(KERN_INFO "chr_dev: Device removed\n");
}// 注册模块初始化函数
module_init(chr_dev_init);// 注册模块退出函数
module_exit(chr_dev_exit);

相关文章:

Linux驱动开发-字符设备驱动开发

linux 驱动开发1. 驱动程序的类型2. 驱动开发流程字符设备驱动 1. 基本概念2. 字符设备驱动的基本结构 架构字符设备驱动开发中常用的 API示例以下代码加入了设备类和设备实例的创建 linux 驱动开发 1. 驱动程序的类型 在 Linux 中&#xff0c;驱动程序主要有以下几种类型&am…...

好用的电脑录屏软件有哪些?推荐4款专业工具。

不同系统的电脑上面带有的录屏功能不一样&#xff0c;比如win10上面有Xbox game bar,Mac系统则用的是QuickTime Player&#xff0c;或者是使用快捷键“CommandShift5”。但更方便的&#xff0c;我自己认为是使用一些专业的录屏软件&#xff0c;他门的录制模式多&#xff0c;兼容…...

web基础之XSS

一、搭建XSS平台 安装 1、我这里安装在本地的Phpstudy上&#xff0c;安装过程就是一路下一步&#xff08;可以改安装路径&#xff09;&#xff0c;附上下载链接&#xff1a; # 官网&#xff1a;https://www.xp.cn/download.html# 蓝莲花 - github下载 https://github.com/fi…...

目标检测-小目标检测方法

小目标检测是计算机视觉中的一个挑战性问题&#xff0c;因为小目标往往在图像中占据的像素较少&#xff0c;容易被背景或其他物体干扰。为了有效地进行小目标检测&#xff0c;研究人员和工程师提出了多种方法和算法来提高检测精度。以下是一些针对小目标检测的有效方式和算法&a…...

连接数据库(以MySQL为例)

文章目录 前言一、数据库是什么&#xff1f;二、连接步骤 1.手动导入驱动包2.连接数据库总结 前言 面对应用程序的开发&#xff0c;普遍需要保存用户的海量数据。保存粮的库叫粮库&#xff0c;保存水的库叫水库&#xff0c;那么保存数据的库自然叫数据库。有了数据库&#xff0…...

Mysql高级教程

1.安装部署 安装依赖性&#xff1a; [rootmysql-node10 ~]# dnf install cmake gcc-c openssl-devel ncurses-devel.x86_64 libtirpc-devel-1.3.3-8.el7_4.x86_64.rpm rpcgen.x86_64 下载并解压源码包 [rootmysql-node10 ~]# tar zxf mysql-boost-5.7.44.tar.gz [rootmysql-no…...

基于Ubuntu2404搭建mysql8配置远程访问

使用系统为Ubuntu2404&#xff0c;mysql8版本为8.0.36 安装mysql apt install -y mysql-server设置开机自启动 systemctl enable --now mysql修改密码&#xff0c;似乎是bug&#xff0c;修改密码第一次不成功&#xff0c;第二次可以 mysql use mysql; update user set Host…...

前端工程师职业发展路线图

在前端开发领域&#xff0c;从一个新手成长为一名资深工程师需要经过一系列的学习和实践。以下是一份详细的前端工程师职业发展路线图&#xff0c;包括了从基础到高级的各个阶段。 入门阶段 1. 学习基础技术 HTML/HTML5&#xff1a;掌握网页结构和语义化标签的使用。CSS/CSS…...

人工智能(AI)正在以前所未有的速度融入我们生活的方方面面

人工智能将融入我们生活的方方面面 人工智能&#xff08;AI&#xff09;正在以前所未有的速度融入我们生活的方方面面&#xff0c;这种趋势在未来几年乃至几十年内将会持续加速。以下是一些人工智能已经或即将在各个领域产生深远影响的例子&#xff1a; 智能家居&#xff1a;…...

OpenCV-模板匹配多个目标

文章目录 一、基本概念二、基本步骤1.图像准备2.图像预处理3.执行模板匹配4.定位匹配区域5.处理多个匹配6.优化和验证 三、代码实现1.图片读取2.图像预处理3.模板匹配4.绘制矩形框 三、总结 模型匹配&#xff08;Model Matching&#xff09;是一个广泛应用的概念&#xff0c;其…...

uniapp 原生插件开发 UI

前言&#xff1a; 在集成某些特定 原生SDK的时候&#xff0c;它本身是带UI控件的。当我们使用 uniapp 开发app的时候实是 可以使使用 nvue 页面&#xff0c;以 weex 的方式嵌入原生的UI控件。 我这边的场景是 接入连连app的支付&#xff0c;它有个自己的密码键盘 控件是原生的页…...

性能测试-性能分析与调优原理总结

性能分析与调优如何下手&#xff0c;先从硬件开始&#xff0c;还是先从代码或数据库。 从操作系统&#xff08;CPU调度&#xff0c;内存管理&#xff0c;进程调度&#xff0c;磁盘I/O&#xff09;、网络、协议&#xff08;HTTP&#xff0c; TCP/IP &#xff09;&#xff0c;还是…...

【机器学习】4 ——熵

机器学习4 ——熵 文章目录 机器学习4 ——熵前言 前言 熵衡量随机变量不确定性&#xff0c;由克劳德香农&#xff08;Claude Shannon&#xff09;在1948年提出&#xff0c;称为香农熵。反映了一个系统中信息的混乱程度或信息量。 其定义为&#xff1a; H ( P ) − ∑ x P …...

linux命令用于删除文本文件中的重复行的命令uniq详解

目录 一、概述 二、基本用法 1、uniq 命令的基本语法 2、常用选项 3、获取帮助 三、主要功能 1. 识别并删除相邻重复行 2. 保留重复行的第一个实例 3. 统计重复次数 4. 忽略指定列的比较 四、示例 1. 删除相邻重复行 2. 显示每一行及其重复次数 3. 只显示重复行 4. …...

PHP智驭未来悦享生活智慧小区物业管理小程序系统源码

智驭未来&#xff0c;悦享生活 —— 探索智慧小区物业管理小程序 一、引言&#xff1a;智慧生活的新篇章 在这个日新月异的时代&#xff0c;科技正以前所未有的速度改变着我们的生活。从智能家居到智慧城市&#xff0c;每一处都闪耀着智慧的光芒。而今天&#xff0c;我要带大家…...

深度学习:怎么看pth文件的参数

.pth 文件是 PyTorch 模型的权重文件&#xff0c;它通常包含了训练好的模型的参数。要查看或使用这个文件&#xff0c;你可以按照以下步骤操作&#xff1a; 1. 确保你有模型的定义 你需要有创建这个 .pth 文件时所用的模型的代码。这意味着你需要有模型的类定义和架构。 2. …...

MMLU-Pro 基准测试数据集上线,含 12k 个跨学科复杂问题,难度提升,更具挑战性!DeepSeek 数学模型一键部署

在大语言模型 (LLM) 蓬勃发展的时代&#xff0c;诸如大规模多任务语言理解 (MMLU) 之类的基准测试&#xff0c;在推动 AI 于不同领域的语言理解与推理能力迈向极限方面&#xff0c;发挥着至关重要的关键作用。 然而&#xff0c;伴随模型的持续改进与优化&#xff0c;LLM 在这些…...

Vue | Vue深入浅出——Vue中的render函数详解

1.render函数 在编写vue单文件的大多数情况下&#xff0c;我们都是使用template模板来创建HTML。然而在一些条件判断比较复杂的场景下&#xff0c;使用JavaScript去描绘HTML的生成逻辑会显得更加的简洁直观。 使用Vue官网的例子来简单说明&#xff1a; 如果自己在开发的时候…...

数学基础 -- 线性代数之奇异值

奇异值与其应用 1. 奇异值定义 对于任意的矩阵 A A A&#xff08;可以是方阵或非方阵&#xff09;&#xff0c;存在三个矩阵 U U U、 Σ \Sigma Σ 和 V V V&#xff0c;使得&#xff1a; A U Σ V T A U \Sigma V^T AUΣVT 其中&#xff1a; U U U 是一个 m m m \ti…...

Python爬虫使用实例-wallpaper

1/ 排雷避坑 &#x1f95d; 中文乱码问题 print(requests.get(urlurl,headersheaders).text)出现中文乱码 原因分析&#xff1a; <meta charset"gbk" />解决方法&#xff1a; 法一&#xff1a; response requests.get(urlurl,headersheaders) response.en…...

探索Go语言中的随机数生成、矩阵运算与数独验证

1. Go中的随机数生成 在许多编程任务中&#xff0c;随机数的生成是不可或缺的。Go语言通过 math/rand 包提供了伪随机数生成方式。伪随机数由种子(seed)决定&#xff0c;如果种子相同&#xff0c;生成的数列也会相同。为了确保每次程序运行时产生不同的随机数&#xff0c;我们…...

无线安全(WiFi)

免责声明:本文仅做分享!!! 目录 WEP简介 WPA简介 安全类型 密钥交换 PMK PTK 4次握手 WPA攻击原理 网卡选购 攻击姿态 1-暴力破解 脚本工具 字典 2-Airgeddon 破解 3-KRACK漏洞 4-Rough AP 攻击 5-wifi钓鱼 6-wifite 其他 WEP简介 WEP是WiredEquivalentPri…...

牛客练习赛128:Cidoai的平均数对(背包dp)

题目描述 给定 nnn 对数 (ai,bi)(a_i,b_i)(ai​,bi​) 和参数 kkk&#xff0c;你需要选出一些对使得在满足 bib_ibi​ 的平均值不超过 kkk 的同时&#xff0c;aia_iai​ 的和最大&#xff0c;求出这个最大值。 输入描述: 第一行两个整数分别表示 n,kn,kn,k。 接下来 nnn 行&…...

Python世界:简易地址簿增删查改算法实践

Python世界&#xff1a;简易地址簿增删查改算法实践 任务背景编码思路代码实现本文小结 任务背景 该任务来自简明Python教程中迈出下一步一章的问题&#xff1a; 编写一款你自己的命令行地址簿程序&#xff0c; 你可以用它浏览、 添加、 编辑、 删除或搜索你的联系人&#xff…...

网络安全-intigriti-0422-XSS-Challenge Write-up

目录 一、环境 二、解题 2.1看源码 一、环境 Intigriti April Challenge 二、解题 要求&#xff1a;弹出域名就算成功 2.1看源码 我们看到marge方法&#xff0c;肯定是原型链污染题目 接的是传参&#xff0c;我们可控的点在于qs.config和qs.settings&#xff0c;这两个可…...

Debian Linux 11 使用crash

文章目录 前言一、环境安装1.1 安装debug package1.2 安装crash 二、使用crash 前言 # cat /etc/os-release PRETTY_NAME"Debian GNU/Linux 11 (bullseye)" NAME"Debian GNU/Linux" VERSION_ID"11" VERSION"11 (bullseye)" VERSION_C…...

python列表 — 按顺序找出b表中比a表多出的元素

目录 一、功能描述 二、适用场景 三、代码实现 一、功能描述 有a、b两个列表&#xff0c;a列表有3个元素&#xff1b;b列表有7个元素。b列表多出的一个元素可能在随机的位置&#xff0c;在不影响其他元素的情况下&#xff0c;找到b列表多出的那四个元素&#xff0c;并按照在…...

如何使用Python创建目录或文件路径列表

在 Python 中&#xff0c;创建目录或生成文件路径列表通常涉及使用 os、os.path 或 pathlib 模块。下面是一些常见的任务和方法&#xff0c;用于在 Python 中创建目录或获取文件路径列表。 问题背景 在初始阶段的 Python 学习过程中&#xff0c;可能遇到这样的问题&#xff1a…...

领夹麦克风哪个品牌好,哪种领夹麦性价比高,无线麦克风推荐

在音频录制需求日益多样化的今天&#xff0c;无线领夹麦克风作为提升音质的关键设备&#xff0c;其重要性不言而喻。市场上鱼龙混杂&#xff0c;假冒伪劣、以次充好的现象屡见不鲜。这些产品往往以低价吸引消费者&#xff0c;却在音质、稳定性、耐用性等方面大打折扣&#xff0…...

苍穹外卖学习笔记(五)

文章目录 二.新增菜品1.图片上传2.具体新增菜品 二.新增菜品 1.图片上传 这里采用了阿里云oss对象存储服务 application.yml alioss:endpoint: ${sky.alioss.endpoint}access-key-id: ${sky.alioss.access-key-id}access-key-secret: ${sky.alioss.access-key-secret}bucket…...