Linux字符设备驱动-详解与实操:驱动架构、设备树、Pinctrl子系统和GPIO子系统、platform、设备树下的platform

如何编写一个驱动程序:

(1)确定主设备号

(2)定义自己的file_operations结构体:

                        包含对应的open(drv_open)/read(drv_read)等设备操作函数,需要到内核中去注册

(3)实现对应的open/read/write等函数,填入file_operations结构体

(4)把file_operations结构体告诉内核:注册驱动程序

                        要把file_operations结构体注册到内核中,应用程序才能调用对应的操作函数

(5)谁来注册驱动程序啊?得由一个入口函数:安装设备驱动程序时,就会去调用这个入口函数

                        用register_chrdev(major(设备号),file_operations)

                        注册的设备放入chrdevs[ ]数组里记录

(6)有入口函数就应该有出口函数:卸载驱动程序时,会去调用这个出口函数

                        用unregister_chardev(    )

                        从chrdevs[ ]数组中去掉该设备

(7)其他完善“提供设备信息,自动创建设备节点

                        设备节点创建:class_create、device_create

每一步的代码详解

驱动框架:最简单的驱动程序-单个LED

1、字符设备名字在注册设备号时指定:“100ask_led

字符设备驱动注册

register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)

        第一个参数:主设备号,0由系统分配

        第二个参数:字符设备名字

        第三个参数:要注册的file_operation结构体

返回值:major为0时,返回一个自动分配的设备号

设备号分配为240

2、类和设备节点创建

类:同一个类别的设备,比如led中的led0、led1

设备节点:具体有几个设备,通过对具体的设备节点操作

每个设备节点都是一个文件,对设备的操作也是对文件的读取和写入

(1)设备类创建:

class_create(struct module *owner, const char *name)        

        第一个参数:拥有这个类的指针,‘THIS_MODULE’宏,代表当前加载的模块

        第二参数:设备类名字。该名字位于/sys/class目录下。

是udev等用户空间工具用来识别和操作设备的关键标识。

sys/class中可以查看已经创建的类

(2)设备节点创建:

device_create(struct class *class, struct device *parent,

     dev_t devt, void *drvdata, const char *fmt, ...)

                第一个参数:设备类的指针

                第二个参数:父设备的指针,一般为NULL

                第三个参数:设备类型和编号的组成,MKDEV宏生成,代表设备的主设备号和次设备号

                第四个参数:用于传递数据,可设为NULL。

                第五个参数:设备节点的名字

/dev/中可查看创建的设备

3、字符设备驱动、设备类、设备节点的销毁

(1)设备节点销毁

device_destroy():第一个参数,设备类指针,哪个设备类。第二个参数:MKDEV宏,指定设备的主设备号和次设备号

(2)设备类销毁

class_destroy():第一个参数,设备类指针。

(3)设备驱动销毁

unregister_chrdev():第一个参数,要销毁的主设备号,第二个参数,字符设备名字

4、寄存器变量:

(1)寄存器变量定义

volatile防止编译器优化

定义的变量为指针(地址),赋值的值就是指向一个地址

(2)寄存器变量映射

映射到实际的地址空间中

ioremap():

        第一个参数:需要映射的物理地址起始点

        第二个参数:要映射的内存区域大小,单位字节

(3)对寄存器的操作

5、file_operation函数

(1)结构体成员初始化

操作符(.)用于访问结构体的成员,.fb_fillrect = cfb_fillrect这行代码是在初始化一个结构体时使用的语法,这种语法特别用于结构体的直接初始化。

在C语言中,当你使用一个函数的名称而不加任何括号时,你实际上是在引用那个函数的地址。(即函数名作为地址使用)

结构体在内核中的定义:

struct file_operations {

    struct module *owner;

    ssize_t            (*write)(struct file *, const char __user *, size_t, loff_t *);

    int                 (*open)(struct inode *, struct file *);

    // 其他成员...

};

(2)成员变量初始化的函数:

a、设备打开操作函数

led_open():

        第一个参数:指向‘inode’结构体

                              ‘inode’中记录了文件数据本身外的所有信息,比如文件权限、文件大小等

                              ‘inode’结构体可以获取主、次设备号

                               通过‘inode’的‘i_cdev’字段,驱动可以访问与设备相关联的‘cdev’结构,该结构                                 包含设备的核心信息和操作

        第二个参数:指向‘struct file’结构体的指针,表示用户打开的文件描述符。

                              该结构体是用户程序与内核之间的关键接口。

b、写操作函数:

led_write():

        第一个参数:指向‘struct file’结构体的指针,表示打开的文件对象

                                其中的‘private_data’字段可用来存储特定于设备的数据

        第二个参数:要写入设备的数据缓冲区

        第三个参数:buf的数据字节数

        第四个参数:文件当前位置的偏移量(不支持寻址的设备不重要)

6、应用层:

argc:传递给程序的参数总数

argv:指针数组,每个元素指向一个字符数组的指针(字符串)

argv[1]:/dev/myled  设备节点名字

fd:文件描述符,整数,标识要写入的文件或设备

argv[2]:on

fd:文件描述符

&status:指向数据缓冲区的指针

Count:要写入的字节数,指定‘buf’中有多少数据

驱动代码:led_drv.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/mutex.h>
#include <linux/wait.h>
#include <asm/uaccess.h>
#include <linux/device.h>
#include <asm/io.h>


static int major;
static struct class *led_class;

/* registers */
//IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3地址:0x02290000 + 0x14
static volatile unsigned int *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;

//GPIO5_GDIR地址:0x020AC004
static volatile unsigned int *GPIO5_GDIR;

//GPIO5_DR地址:0x020AC000
static volatile unsigned int *GPIO5_DR;

static ssize_t led_write(struct file *filp, const char __user *buf,
			 size_t count, loff_t *ppos)
{
	char val;
	int ret;
	/* copy_from_user : get data from app */
	ret = copy_from_user(&val, buf, 1);
	/* to set gpio register: out 1/0 */
	if(val)
	{
		/* set gpio to let led on  */
		*GPIO5_DR &= ~(1<<3);				//输出低电平,根据原理图低电平点亮
	}
	else
	{
		/* set gpio to let led off  */
		*GPIO5_DR |= (1<<3);                //输出高电平
	}
	return 1;
}


 static int led_open(struct inode *inode, struct file *filp)
 {
 	/* enable gpio5
 	 * configure gpio5_io3 as gpio
 	 * configure gpio5_io3 as output 
 	 */
	*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 &= ~0xf;     //先清零低4位
	*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 |= 0x05;     //低4位配置101

	*GPIO5_GDIR |= (1<<3);
	
 	return 0;
 }


static struct file_operations led_fops = {
	.owner		= THIS_MODULE,
	.write		= led_write,
	.open		= led_open,
};
/* 入口函数 */
static int __init led_init(void)
{
	printk("%s %s %d/n", __FILE__, __FUNCTION__, __LINE__);

	/* 加载字符驱动 */
	major = register_chrdev(0, "100ask_led", &led_fops);

	/* ioremap */
	//IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3地址:0x02290000 + 0x14
	IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = ioremap(0x02290000 + 0x14, 4);     //映射4个字节

	//GPIO5_GDIR地址:0x020AC004
	GPIO5_GDIR = ioremap(0x020AC004, 4);

	//GPIO5_DR地址:0x020AC000
	GPIO5_DR = ioremap(0x020AC000, 4);

	/* 创建设备节点 */
	led_class = class_create(THIS_MODULE, "myled");                     //要在THIS_MODULE下创建类
	device_create(led_class, NULL, MKDEV(major, 0), NULL, "myled0");
	//device_create(led_class, NULL, MKDEV(major, 1), NULL, "myled1");
		
	return 0; 
}

static void __exit led_exit(void)
{
	/* iounmap */
	iounmap(IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3);
	iounmap(GPIO5_GDIR);
	iounmap(GPIO5_DR);
	
	/* 销毁设备节点 */
	device_destroy(led_class, MKDEV(major, 0));
	class_destroy(led_class);
	
	/* 销毁字符设备驱动 */
	unregister_chrdev(major, "100ask_led");
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

应用代码:ledtest.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>


// ledtest /dev/myled0 on
// ledtest /dev/myled0 off

int main(int argc, char **argv)
{
	int fd;
	char status = 0;

	if(argc != 3)

	{
		printf("Usage: %s <dev> <on/off>\n", argv[0]);
		printf("   eg: %s /dev/myled0 on\n", argv[0]);
		printf("   eg: %s /dev/myled0 off\n", argv[0]);
		return -1;
	}
	
	//open
	fd = open(argv[1], O_RDWR);
	if (fd < 0)
	{
		printf("can not open %s \n", argv[0]);
	}

	
	//write
	if(strcmp(argv[2], "on")==0)
	{
		status = 1;
	}

	write(fd, &status, 1);
	return 0;
}

多个按键:

要对GPIO5_1GPIO4_14配置(key1和key2

1定义GPIO通用设置的结构体(按照手册中寄存器的位置)

(1)GPIO_DR:Data register                    

数据寄存器(读取或写入GPIO引脚当前状态)

(2)GPIO_GDIR:GPIO direction             

方向寄存器(配置GPIO输入或输出)

(3)GPIO_PSR:Pad sample register        

 读取GPIO引脚当前状态(高电平或低电平) 

(4)GPIO_ICR1, GPIO_ICR2:Interrupt control registers

终端控制寄存器(配置GPIO引脚终端触发方式和其他中断相关设置)

(5)GPIO_EDGE_SEL:Edge select register

边沿选择寄存器(配置GPIO引脚的边沿触发类型)

(6)GPIO_IMR:Interrupt mask register

中断屏蔽寄存器(配置哪些GPIO引脚中断应该被屏蔽)

(7)GPIO_ISR:Interrupt status register

中断状态寄存器(显示当前GPIO引脚的中断状态)

2配置过程:

(1)定义要寄存器变量(要修改的寄存器)

GPIO时钟使能、GPIO模式配置、GPIO模式下引脚配置

(2)手册中找到相应的地址,重映射

CCM_CCGR1中30-31为默认保留位,默认使能GPIO5

CCM_CCGR3中12-13位为gpio_4的时钟配置

对GPIO各寄存器的映射(按照地址顺序下来,只需要找到首个寄存器GPIOxx_DR的地址就好)

(3)给寄存器赋值

时钟配置11,第30、31位:(3<<30),对gpio的访问,从结构体中找到对应的成员变量即可

Linux内核中因为MMU的存在不能直接访问物理地址,需要通过ioremap把虚拟地址映射到对应的物理地址,我们对虚拟地址的访问,会自动修改到对应的物理地址。而结构体寄存器会映射整块对应大小的内存,里面的虚拟地址和物理地址也是一一对应的关系

3、如何指定操作哪个设备

(1)通过传参which,指定该类下要操作的设备

init进行寄存器的初始化,read实现Key状态的读取(状态寄存器的读取)

CCM_CCGR1若未有值(未映射),则先完成寄存器的映射。

(2)operation操作函数中,获取要操作的次设备号,调用init或read函数

从inode设备节点中,获取次设备号,在调用操作函数

若无inode,则先从file结构体中获取inode结构体,再从中获取次设备号。

(3)设备的创建和销毁:

创建

销毁

驱动代码:

设备树:

在设备树中指定硬件资源的描述(对LED操作)

设备树加载后,可以进入/proc/device-tree/目录下查看根节点下的节点。

1、设备树dts文件:添加led节点

设备树中的节点由一堆属性组成,不同设备需要的属性不同

(1)#address-cells、#size-cells属性:

                cell指一个32位的数值,

                #address-cells决定子节点reg属性中地址信息所占用的字长(32字节)

                #size-cells决定子节点reg属性中长度信息所占的字长

该节点用1个数表示地址,1个数表示大小

(2)compatible属性:

                值为字符串列表,可能有多个驱动支持它(多个字符串)

                将设备和驱动绑定起来,通过compatible中的名字找到要加载probe的驱动模块

(3)status属性:

设备状态

                “okay”:      表明设备是可操作的

                “disabled”:表示当前设备不可操作(禁用掉),但在未来可变为可操作的

                “fail”:         表明设备不可操作,检测到错误,不大可能变为可操作

                “fail-sss”:   含义与“fail”相同,后面的sss部分是检测到的错误内容

(4)reg属性:

                reg属性的值一般是(address,length)对。

                一般用于描述设备地址空间资源信息,一般是某个外设的寄存器地址范围信息。

2、驱动程序:获取硬件资源(获取设备树中的属性数据)

led_init()函数中

(1)获取设备节点:alphaled

获取设备节点后,才能获取属性内容

of_find_node_by_path():

用于操作设备树的函数,主要作用是根据给定的设备树路径来查找并返回对应的设备树节点。

                参数:设备树中的一个路径,根节点开始的绝对路径。

                dtsled.nd为device_node结构体的指针

(2)获取设备节点属性,包括reg

获取compatible属性内容

获取status属性内容

获取reg属性内容,必须要先获取设备节点dtsled.nd

of_property_read_u32_array( );

从设备节点中读取一个包含多个'u32'元素的属性到一个用户提供的数组中。

                第一个参数:指向设备树节点的指针

                第二个参数:要读取的设备树属性的名称

                第三个参数:用户提供的数组,用于存储读取到的数据。

                第四个参数:希望读取到的元素的数量。

(3)寄存器地址映射

其他对寄存器的操作一样

Pinctrl和GPIO子系统:

省去了对硬件的寄存器的复杂配置(对LED操作)

Pinctrl:设置pin的复用和电气属性

GPIO子系统:若pinctcl子系统将一个pin复用为GPIO的话,接下里就会用到gpio子系统了

用于初始化gpio并提供相应的API函数,对gpio操作

设备树:

1、pinctrl节点:

在 iomuxc 节点的 imx6ul-evk 子节点下创建一个名为“pinctrl_led”的子节点

(1)label:node-name@unit-address

                label:方便访问节点,可以通过&label来访问这个节点。

                node-name:节点名字。

                unit-address:设备的地址或寄存器首地址,若节点没有地址或寄存器,可以不要。

(2)MX6UL_PAD_GPIO1_IO03__GPIO1_IO03

                MX6UL                   - 表示芯片或处理器的型号

                PAD_GPIO1_IO03   - 引脚的物理位置或名称

                GPIO1_IO03            - 引脚的复用功能,复用为GPIO1的第03号引脚

(3)0x10B0:引脚配置寄存器的值,定义了引脚的各种电气属性。

                如上拉/下拉电阻、驱动能力、速度等

                在这一步已经完成了引脚的配置

2、LED设备节点

在根节点“/”下创建LED灯节点,节点名为“gpioled”

(1)pinctrl-0属性,6行:设置LED灯所使用的PIN对应的pinctrl节点。

要检测PIN是否被其他外设使用,要将其关掉(status="disabled"等)。

(2)led-gpio属性,7行:指定LED灯所使用的GPIO,GPIO1的IO03,低电平有效。

驱动程序中会获取led-gpio属性内容得到GPIO编号,因为gpio子系统的API操作函数需要GPIO编号。

驱动程序:
1、设备节点结构体定义:

2、gpio子系统控制引脚

(1)获取设备节点:

(2)获取设备树中的gpio属性,得到LED所使用的GPIO编号

of_get_named_gpio():

读取设备树中定义的GPIO属性,将其转换为GPIO编号。通过此编号可以在驱动中请求、配置和控制GPIO。

                第一个参数:指向‘device_node’的指针,表示当前设备的设备树节点

                第二个参数:节点中GPIO属性的名字。

                第三个参数:索引,表示有多个GPIO同一属性下定义是,应选择哪一个。

(3)设置gpio为输出,并且输出高电平,默认关闭LED灯

gpio_direction_output():

Linux内核中提供的操作GPIO的函数,设置指定引脚为输出模式。

                第一个参数:GPIO引脚的编号,标识要设置哪个引脚

                第二个参数:设置GPIO引脚的初始电平,“1”高电平

(4)设置gpio的值

dev是指向gpioled_dev结构体的指针

gpio_set_value():

设置指定GPIO引脚的输出电平。

                第一个参数:GPIO引脚编号。

                第二个参数:设置GPIO引脚的初始电平。

Platform设备驱动(无设备树程序)

驱动的分层和分离。

将驱动中的硬件部分分离出来为platform_device,保留的驱动为platform_driver,通过总线匹配注册probe。设备树实质上就是platform_device这部分,替代了原先需要每次编写和加载的.c文件,每次自动加载和生成platform_device。

platform结构体详解、定义和框架可参考上篇博客:
Linux驱动进化:传统模型、设备总线驱动模型、设备树-CSDN博客

1、leddevice.c硬件资源(paltform_device):

(1)resource

struct resource:

                start:资源的起始地址

                end:资源的结束地址

                name:资源的名称

                flags:资源类型和属性标志

(2)platform_device结构体

名字为“imx6ul-led”以和platform_deriver配对

(3)注册platform_device结构体

便于通过总线bus和platform_driver配对

2、platform_driver:

(1)从platform_device中获取资源(led_probe函数)

platform_get_resource():
用于从平台设备中获取特定类型和序号的资源。        

                第一个参数:指向“platform_device”结构的指针,表示正在被操作的平台设备。

                第二个参数:资源类型标志,指定要获取的资源类型。IORESOURCE_MEM表示一个内                                         存类型的资源。

                第三个参数:资源的索引,设备的资源有多个,要获取哪一个。

(2)初始化LED

设置寄存器的初始值:···省略

(3)platform_driver结构体

通过名字"imx6ul-led"和device匹配。

(4)注册platform_driver

通过bus总线和platform_driver配对后会自动调用probe函数完成资源初始化

3、匹配过程:

驱动模块加载完后,到/sys/bus/platform/drivers/目录下

led的设备文件在/sys/bus/platform/devices/目录下

驱动和设备匹配

设备树下的Platform设备驱动:

前面已经实现了设备树(加载设备树时会自动生成platform_device),这里只看platform_driver

leddriver.c

1、初始化IO

(1)从设备树中获取设备节点

(2)获取设备树中的gpio属性,得到LED所使用的GPIO编号

(3) Linux 内核中用于请求一个 GPIO(通用输入输出)引脚的使用权。之后操作gpio引脚

gpio_request():

Linux内核中用于请求一个GPIO引脚的使用权

                第一个参数:要申请的GPIO引脚的编号。

                第二个参数:请求的GPIO引脚相关联的标签或名称,可自定义。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

OpenAI最大对手推出iOS版APP 以期与ChatGPT展开竞争 | 最新快讯

财联社 5 月 2 日讯&#xff08;编辑牛占林&#xff09;美东时间周三&#xff0c;人工智能(AI)初创公司 Anthropic 宣布推出一款免费的移动端应用程序(APP)&#xff0c;不过目前仅有 iOS 版本。 这款应用名为 Claude&#xff0c;与 Anthropic 的大模型系列名字相同。Anthropic …

基于Python的在线学习与推荐系统设计与实现(论文+源码)-kaic

题目&#xff1a;在线学习与推荐系统设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本在线学习与推荐系统就是在这样的大环境下诞生&#xff0…

macbook文件管理,这款神器不得不说 macbook文件管理技巧 macbookpro文件管理软件

使用MacBook办公就是看重高效的特点&#xff0c;但很多时候随着使用时间的增长&#xff0c;MacBook上的文件也会越来越多&#xff0c;如果没有很好的管理方式&#xff0c;只会给日常的使用造成诸多不便。如何更好的搞定macbook文件管理呢&#xff0c;且看下面几个方法&#xff…

新势力4月交付量比拼:理想超问界夺冠,小米首月交付超七千辆 | 最新快讯

理想汽车超越问界夺下 4 月新势力交付量冠军。 5 月 1 日&#xff0c;各大造车新势力纷纷亮出最新成绩。新入局者小米汽车也准时发布了交付数据&#xff0c;在交付首月&#xff0c;同时又是非完整交付月&#xff0c;小米就交出了超七千辆的好成绩&#xff0c;在造车新势力中尚属…

ngrinder项目-本地调试遇到的坑

前提-maven mirrors配置 <mirrors><!--阿里公有仓库--><mirror><id>nexus-aliyun</id><mirrorOf>central</mirrorOf><name>Nexus aliyun</name><url>http://maven.aliyun.com/nexus/content/groups/public</ur…

【计算机网络】网络层总结

目录 知识梗概 IP地址 子网划分 IP包头格式 路由 网络层协议 ARP病毒/ARP欺骗 知识梗概 IP地址 IP相关介绍&#xff1a;机器之间需要交流&#xff0c;必须要一个地址才能找到对应的主机&#xff0c;IP地址是主机的一种表示&#xff0c;保证主机之间的正常通信&#xff…

1083 是否存在相等的差

solution 输出的是重复的差值&#xff0c;而非全部差值 #include<iostream> #include<algorithm> using namespace std; const int maxn 1e4 10; int flag[maxn] {0}; int main(){int n, x;scanf("%d", &n);for(int i 1; i < n; i){scanf(&…

STM32 串口IDLE接收空闲中断+DMA

参考 http://t.csdnimg.cn/fAV38 1.基础知识 STM32 IDLE 接收空闲中断 功能&#xff1a; 在使用串口接受字符串时&#xff0c;可以使用空闲中断&#xff08;IDLEIE置1&#xff0c;即可使能空闲中断&#xff09;&#xff0c;这样在接收完一个字符串&#xff0c;进入空闲状态时&…

分布式与一致性协议之Raft算法(一)

Raft算法 概述 Raft算法属于Multi-Paxos算法&#xff0c;它在兰伯特Multi-Paxos思想的基础上做了一些简化和限制&#xff0c;比如日志必须是连续的&#xff0c;只支持领导者(Leader)、跟随者(Follwer)和候选人(Candidate)3种状态。在理解和算法实现上&#xff0c;Raft算法相对…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-6.5--I.MX6U启动方式

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

想要吃瓜,就要学会,在不必要的冲突发生时,沉默就是一种智慧——早读(逆天打工人爬取热门微信文章解读)

练习一下怼人的本事 引言Python 代码第一篇 洞见 养生的尽头&#xff0c;是养格局第二篇 人民日报 来啦 早班新闻车要闻社会 政策结尾 沉默是智者的选择 在不必要的冲突面前 选择沉默是一种智慧 引言 昨天下午睡醒 看到群里有些言论 遂 battle了一波 给大家吃吃瓜 到中午 车…

Codeforces Round 941 (Div. 2) D. Missing Subsequence Sum

题目 思路&#xff1a; #include <bits/stdc.h> using namespace std; #define int long long #define pb push_back #define fi first #define se second #define lson p << 1 #define rson p << 1 | 1 const int maxn 1e6 5, inf 1e18, maxm 4e4 5; c…

网络安全之弱口令与命令爆破(中篇)(技术进阶)

目录 一&#xff0c;什么是弱口令&#xff1f; 二&#xff0c;为什么会产生弱口令呢&#xff1f; 三&#xff0c;字典的生成 四&#xff0c;使用Burpsuite工具验证码爆破 总结 笔记改错 一&#xff0c;什么是弱口令&#xff1f; 弱口令就是容易被人们所能猜到的密码呗&a…

MyBatis中的#{} 和 ${}

目录 #{} 和 ${} 预编译 SQL 和 即时 SQL SQL注入 ${}的使用 #{} 和 ${}的使用 MyBatis参数赋值有两种方式&#xff0c;在上一篇文章中&#xff0c;一直使用 #{} 进行赋值&#xff0c;接下来&#xff0c;我们来使用 ${} 进行赋值&#xff0c;并观察 #{} 和 ${} 的区别 使用…

2024年外贸企业邮箱最新排名

外贸企业在与客户沟通中的重要工具就是企业邮箱&#xff0c;那么外贸企业邮箱哪个比较好呢&#xff1f;本文聚焦2024年外贸企业邮箱市场的最新动态&#xff0c;通过对五个领先品牌的深度对比&#xff0c;旨在为企业决策者提供详尽参考。首当其冲的是备受瞩目的Zoho Mail企业邮箱…

JMeter性能压测脚本录制

第一步&#xff1a;电脑打开控制面板设置代理服务器 第二步&#xff1a;jmeter的测试计划添加一个HTTP&#xff08;S&#xff09;脚本记录器 在脚本记录器里配置好信息&#xff0c;然后保存为脚本文件&#xff08;.*表示限定&#xff09; 此方框内容为项目地址&#xff08;可改…

字符串函数与字符函数运用(1)

字符串与字符函数介绍1 前言一、字符分类函数字符函数练习 二、字符函数转换1.引入库2.代码改进 字符串函数strlen函数strcpy 结尾 前言 字符串函数大概有以下这几种 strcpy、strcat 、strcmp、strncpy、strncat、strncmp、strstr、strtok、strerror 这些函数可以很好的解决你…

DRF中的请求入口分析及request对象分析

DRF中的请求入口分析及request对象分析 django restframework框架是在django的基础上又给我们提供了很多方便的功能&#xff0c;让我们可以更便捷基于django开发restful API 1 drf项目 pip install django pip install djangorestframework1.1 核心配置 INSTALLED_APPS [d…

神经网络中常见的激活函数:理解与实践

神经网络中常见的激活函数&#xff1a;理解与实践 在神经网络中&#xff0c;激活函数是一个非常重要的组成部分&#xff0c;它为神经元引入了非线性特性&#xff0c;使得神经网络可以拟合各种复杂的函数关系。本文将介绍9种常见的激活函数&#xff0c;包括它们的概述、公式以及…

《百图解码支付系统设计与实现》电子书_V20240503

《百图解码支付系统设计与实现》这本书的底稿已经完成一半&#xff0c;从2023.12.24发布专栏第一篇文章“跟着图走&#xff0c;学支付&#xff1a;在线支付系统设计的图解教程”算起&#xff0c;陆续写了30来篇支付相关的干货。 本书是我的专栏《百图解码支付系统设计与实现》…
最新文章