【linux驱动开发】在linux内核中注册一个杂项设备与字符设备以及内核传参的详细教程
文章目录
- 注册杂项设备
- 驱动模块传参
- 注册字符设备
开发环境: windows + ubuntu18.04 + 迅为rk3568开发板
注册杂项设备
相较于字符设备,杂项设备有以下两个优点:
- 节省主设备号:杂项设备的主设备号固定为 10,在系统中注册多个 misc 设备驱动时,只需使用子设备号进行区分即可。
- 使用简单:相比如普通的字符设备驱动, misc驱动只需要将基本信息通过结构体传递给相应处理函数即可。
在linxu系统中可使用 cat /proc/misc 命令查看系统中的杂项设备。注册杂项设备的步骤:
-
1.填充设备操作集结构体
struct file_operations; -
2.填充杂项设备结构体
struct miscdevice; -
3.使用函数
misc_register注册杂项设备; -
4.使用函数
misc_deregister卸载杂项设备;
上面三步可使用下面函数直观用表达,即:
static struct file_operations xxx_fops{.owner = THIS_MODULE, .read = xxx_read, ....
};
struct miscdevice xxx_dev{.minor = MISC_DYNAMIC_MINOR, .name = "xxx", .fops = &xxx_fops
};
static int __init xxx_init(void) //驱动入口函数
{int ret;printk(KERN_EMERG "xxx_init\r\n");ret = misc_register(&xxx_dev);//注册杂项设备if(ret<0){printk( "misc_register failed\r\n");return -1;}printk( "misc_register ok\r\n");return 0;
}
static void __exit xxx_exit(void) //驱动出口函数
{printk(KERN_EMERG "xxx_exit\r\n");misc_deregister(&xxx_dev); //卸载杂项设备
}
module_init(xxx_init); //注册入口函数
module_exit(xxx_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xxx");
具体实现注册一个杂项设备的示例代码如下:
#include <linux/kernel.h>
#include <linux/init.h> //初始化头文件
#include <linux/module.h> //最基本的文件,支持动态添加和卸载模块。
#include <linux/miscdevice.h> //注册杂项设备头文件
#include <linux/fs.h> //注册设备节点的文件结构体
#include <linux/uaccess.h>// 打开杂项设备
int _open(struct inode *inode,struct file*file)
{printk(KERN_EMERG"hello misc");return 0;
}// 关闭杂项设备
int close(struct inode * inode, struct file *file)
{printk(KERN_EMERG"close");return 0;
}// 读取杂项设备中的数据
ssize_t misc_read (struct file *file, char __user *buff, size_t size, loff_t *loff)
{char kbuff[32] = "kernel";if(copy_to_user(buff,kbuff,strlen(kbuff)) != 0) // 将内核中的数据给应用{printk("copy_to_user error\r\n");return -1;} printk(KERN_EMERG"copy_to_user is successful\r\n");return size;
}// 写入数据到杂项设备中
ssize_t misc_write (struct file *file, const char __user *buff, size_t size, loff_t *loff)
{char kbuff[32] ;if(copy_from_user(kbuff,buff,size)!= 0) // 从应用那儿获取数据{printk("copy_from_user error\r\n");return -1;} printk(KERN_EMERG"copy_from_user data:%s\r\n",kbuff);return size;
}// 设备文件描述集
struct file_operations misc_fops ={.owner = THIS_MODULE,.open = misc_open,.release = close,.read = misc_read,.write = misc_write
};struct miscdevice misc_dev = {.minor = MISC_DYNAMIC_MINOR,.name = "hello_misc", // 杂项设备名 注册成功后会在 /dev目录下显示.fops = &misc_fops
};// 驱动的入口函数
static int __init misc_init(void)
{int ret = 0;ret = misc_register(&misc_dev);if(ret < 0)printk(KERN_EMERG"misc register is error\r\n.");elseprintk(KERN_EMERG"misc register is seccussful\r\n.");return 0;
}//驱动的出口函数
static void __exit misc_exit(void)
{misc_deregister(&misc_dev);printk(KERN_EMERG"baibai\r\n");
}module_init(misc_init);
module_exit(misc_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("zhouxianjie0716@qq.com");
编译传送到开发板上后,先试用insmod +驱动名.ko挂载驱动,其结果为:

上述驱动代码的测试代码如下:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main(int argc,char *argv[])
{char filePath[] = "/dev/hello_misc";// 打开文件int fd = open(filePath,O_RDWR);if(fd < 0){printf("opening is failed.\n");return -1;}elseprintf("opening is successful.\n");// 读取char buff1[32],buff2[32] = "hello this is app";read(fd,buff1,sizeof(buff1));printf("buff1 is %s.\n",buff1);// 写入write(fd,buff2,sizeof(buff2)); close(fd);return 0;
}
使用./+程序名运行测试代码后,得结果如下:

驱动模块传参
总所周知,应用程序传参是通过shell终端传,只要将main函数按照下面格式书写即可完成传参操作
int main(int argc ,char *argv[])
{return 0;
}
相比之下,驱动模块传递参数需要借助其他函数完成传参操作:
1. 传递单个参数给内核
module_param(name, type, perm)
参数解释:
-
name:参数名,既是外部参数名,又是内部参数名。
-
type:参数的数据类型,可取int、charp等。
-
perm:访问权限。八进制,如:0777。0表示该参数在文件系统中不可见。
注意:传递字符作为参数时数据类为charp,而不是char.
2.传递数组给内核
module_param_array(name, type, nump, perm)
- module_param_array(name,type,nump,perm)
- name:数组参数名,既是外部参数,又是内部参数
- type:参数的数据类型
- nump:终端传给数组的实际元素个数(指针变量)
- perm:访问权限,0644。0表示该参数在文件系统中不可见
3.传递字符串给内核
module_param_string(name, string, len, perm)
- name:参数名,外部参数名
- string:内部参数名(内部字符数组名)
- len:数组长度
- perm:访问权限,0644。0表示该参数在文件系统中不可见
传递参给内核的作用:
- 1.设置驱动的相关参数,如:设备数量、设备缓冲区大小等等。
- 2.可进行安全校验,放置驱动被他人盗用。
说明:
参数传递的适用时机为 加载驱动到内核时,命令形式为:insmod +驱动名.ko +参数1名=参数值 参数2名=参数值 ……。例如下面示例中使用insmod file.ko date=12传递参数:
#include <linux/module.h>
#include <linux/init.h>// 保存参数
static int date;
static int date1;
static int data[5];
static int count;
static char str[32];
static char *strData;// 传递单个参数
module_param(date,int,S_IRUGO|S_IWUSR); // 可读可写
module_param(date1,int,S_IRUGO); // 可读可写
module_param(strData,charp,S_IRUGO); // 可读// 传递多个参数
module_param_array(data,int,&count,S_IRUGO); // 可读
module_param_string(str,str,sizeof(str),S_IRUGO); // 可读// 驱动的入口函数
static int __init dev_init(void)
{int i=0;if(strcmp(str,"myTest")!=0){printk("dev_init error\r\n");return -1;}printk("---------------------------------------\r\n");printk(KERN_EMERG"dev_init is successful!\r\n");for(i=0;i<count;++i)printk("data[%d] = %d \r\n",i,data[i]);printk("str:%s strData:%s\r\n",str,strData);printk("date:%d count:%d\r\n",date,count);data[1] = 111;date = 10;date1 = 111;return 0;
}//驱动的出口函数
static void __exit dev_exit(void)
{int i=0;for( i=0;i<count;++i)printk("data[%d] = %d \r\n",i,data[i]);printk("date:%d count:%d\r\n",date,count);printk(KERN_EMERG"dev_exit is successful\r\n");
}module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("zhouxianjie0716@qq.com");
注册字符设备
步骤一:驱动初始化,需要申请设备号,初始化并且注册cdev结构体,初始化硬件;
可使用动态申请或静态申请设备号,其中动态申请设备号一般在235-255,静态申请则一般由用户手动输入。
静态申请的函数原型为:
int register_chrdev_region(dev_t, unsigned, const char *);
参数含义:
- from: 自定义的 dev_t 类型设备号。
- count: 申请设备的数量。
- name: 申请的设备名称。
函数返回值:申请成功返回 0,申请失败返回负数。
动态申请的函数原型为:
int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);
参数含义:
- dev : 会将申请完成的设备号保存在 dev 变量中。
- baseminor: 次设备号可申请的最小值。
- count: 申请设备的数量。
- name: 申请的设备名称。
函数返回值:申请成功返回 0,申请失败返回负
Linux 内核中将字符设备抽象成一个具体的数据结构 (struct cdev), 我们可以理解为字符设备对象,cdev 记录了字符设备号、内核对象、文件操作 file_operations 结构体(设备的打开、读写、关闭等操作接口)等信息:
struct cdev {struct kobject kobj; //内嵌的内核对象.struct module *owner; //该字符设备所在的内核模块的对象指针. const struct file_operations *ops; //该结构描述了字符设备所能实现的方法,是极为关键的一个结构体.struct list_head list; //用来将已经向内核注册的所有字符设备形成链表. dev_t dev; //字符设备的设备号,由主设备号和次设备号构成. unsigned int count; //隶属于同一主设备号的次设备号的个数.
};
初始化设备描述集cdev结构体的函数原型为:
void cdev_init(struct cdev *, const struct file_operations *);
参数含义:
- 参数1:表示是抽象设备结构体;
- 参数2:表示文件操作集;
注册设备到内驱使用下面函数
int cdev_add(struct cdev *, dev_t, unsigned);
参数含义:
- 参数1:为要添加的 struct cdev 类型的结构体
- 参数2:为申请的字符设备号
- 参数3:为和该设备关联的设备编号的数量
若函数在内核中添加成功返回 0,添加失败返回负数。
步骤二:构建设备文件操作描述集file_operations
也就是read、write、open、close等函数。
步骤三:生成并且添加设备节点
- 手动添加设备节点:也就是在加载驱动到内核时添加,即
mknod + 路径/设备名 +设备类型 + 主设备号 + 次设备号 - 自动添加设备节点:初始化内核时,使用函数自动添加。即先试用函数
class_create创建一个类,在使用函数device_create创建并且添加设备节点。
class_create函数说明:
#define class_create(owner, name) \
({ \
static struct lock_class_key __key; \ __class_create(owner, name, &__key); \
})
函数作用:
用于动态创建设备的逻辑类,并完成部分字段的初始化,然后将其添加进 Linux 内核系统。
参数含义:
- owner:指向函数即将创建的这个 struct class 的模块。一般为 THIS_MODULE。
- name:代表即将创建的 struct class 变量的名字。
返回值:struct class * 类型的类。
device_create函数说明:
struct device *device_create(struct class *cls, struct device *parent, dev_t devt, void *drvdata, const char *fmt,...);
函数作用:
用来在 class 类中下创建一个设备属性文件,udev 会自动识别从而进行设备节点的创建。
参数含义:
- cls:指定所要创建的设备所从属的类。
- parent:指定该设备的父设备,如果没有就指定为 NULL。
- devt:指定创建设备的设备号。
- drvdata:被添加到该设备回调的数据,没有则指定为 NULL。
- fmt:添加到系统的设备节点名称。
返回值:struct device * 类型结构体的设备
步骤四:注销字符设备驱动,
- 步骤一:使用函数
unregister_chrdev_region释放设备号
void unregister_chrdev_region(dev_t, unsigned)
该函数只有一个参数,为要删除设备的设备号,并且函数无返回值。
- 步骤二:使用函数
cdev_del步骤二:删除设备操作集卸载cdev
void cdev_del(struct cdev *);
该函数只有一个参数,为要删除的 struct cdev 类型的结构体,并且函数无返回值。
- 步骤三:使用函数
device_destroy卸载设备
void device_destroy(struct class *cls, dev_t devt);
用来删除 cls 类中的devt设备属性文件,udev 会自动识别从而进行设备节点的删除。
- 步骤四:使用函数
class_destroy删除类class
void class_destroy(struct class *cls);
该函数只有一个参数,为要删除的类,并且函数无返回值。
示例代码
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>#define DEVICE_NUMBER 1 // 设备数量
#define DEVICE_SNAME "schrdev" // 静态申请时设备名
#define DEVICE_ANAME "achrdev" // 动态申请时设备名
#define DEVICE_MINOR_NUM 0 // 次设备号起始地址
#define DEVICE_CLASS_NAME "myTestClass" // 类名
#define DEVICE_MYNAME "mytest"// 打开设备
int chrdev_open(struct inode*inode,struct file*file)
{printk("chrdev_open is opened\r\n");return 0;
}// 保存设备号 其中前12位为主设备号 后20位为次设备号
static dev_t dev_num;// 定义主设备号 次设备号
static int major_num,minor_num; // 设备信息描述集
struct cdev cdev;// 类描述集
struct class *cls;// 设备描述集
struct device*device;// 传递单个参数
module_param(major_num,int,S_IRUGO); // 可读
module_param(minor_num,int,S_IRUGO); // 可读// 文件操作集
struct file_operations chrdev_opr = {.owner = THIS_MODULE,.open = chrdev_open
};// 驱动的入口函数
static int __init dev_init(void)
{int ret;/* 步骤一:申请设备号 */// 有传递主设备号就静态申请if(major_num){ dev_num = MKDEV(major_num, minor_num);//将主设备号 次设备号合并成设备号//参数分别表示 设备号 设备数量 设备名称ret = register_chrdev_region(dev_num, DEVICE_NUMBER, DEVICE_SNAME); if(ret < 0){printk("dev_init error\r\n");return -1;}} // 否则就动态申请else{// 参数:设备号 次设备号起始地址 设备数量 设备名称ret = alloc_chrdev_region(&dev_num , DEVICE_MINOR_NUM, DEVICE_NUMBER, DEVICE_ANAME);if(ret < 0){printk("dev_init error\r\n");return -1;}// 获取主设备号 次设备号major_num = MAJOR(dev_num);minor_num = MINOR(dev_num);}printk("---------------------------------------\r\n");printk("dev_num:%d major_num:%d minor_num:%d\r\n",dev_num,major_num,minor_num);/* 步骤二:初始化设备 */// 初始化cdevcdev.owner = THIS_MODULE;// 初始化设备 cdev_init(&cdev, &chrdev_opr);/* 步骤三:注册设备到内核 *///添加(注册)到内核 参数: 设备 设备号 设备数量cdev_add(&cdev, dev_num, DEVICE_NUMBER);/* 步骤四:先创建类再自动创建添加设备名称*/// 创建类 参数1: 类的归属 参数2: 类名cls = class_create(THIS_MODULE,DEVICE_CLASS_NAME);// 创建设备 参数1: 归属到类 参数2:设备的父设备 参数3:设备号 参数4:添加到设备的回调数据 参数5:设备名device = device_create(cls,NULL,dev_num,NULL,DEVICE_MYNAME);printk("auto add device name\r\n");return 0;
}//驱动的出口函数
static void __exit dev_exit(void)
{/* 步骤一:注销设备号 参数:设备号 设备数量 */ unregister_chrdev_region(MKDEV(major_num, minor_num), DEVICE_NUMBER);/* 步骤二:删除设备操作集 */cdev_del(&cdev);/* 步骤三:删除设备*/device_destroy(cls,dev_num);/* 步骤四: 删除类*/class_destroy( cls);printk(KERN_EMERG"dev_exit is successful\r\n");
}module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("zhouxianjie0716@qq.com");
使用加载驱动到内核insmod file.ko命令,得结果

在测试驱动前需要使用mknod /dev/mytest c 236 0创建设备文件,命令格式为:mknod + /路径/设备名称 +设备类型+主设备号 + 次设备号。
然后就可写一个应用程序来测试我们的字符驱动是否成功注册,测试源码如下:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main(int argc,char* argv[])
{char name[] = "/dev/mytest";int fd = open(name,O_RDONLY); // 仅读if(fd < 0){printf("open is failed\n");return -1;}close(fd);return 0;
}
执行测试程序后结果如下:

注意:在运行测试程序时,一定要先创建驱动文件,否则就会报段错误,具体如下:

杂项设备与字符设备的比较:
- 杂项设备的主设备号固定为10,而字符设备的主设备号需要创建。
- 杂项设备的创建相对简单,只需要填充设备操作集结构体、杂项设备结构体再注册即可。而字符设备需要经过 申请设备号、初始化并且注册cdev结构体、初始化硬件、构建设备文件操作描述集、生成并且添加设备节点。
- 杂项设备与字符设备都需要构建文件操作描述集。(应用层调用驱动的核心)
相关文章:
【linux驱动开发】在linux内核中注册一个杂项设备与字符设备以及内核传参的详细教程
文章目录 注册杂项设备驱动模块传参注册字符设备 开发环境: windows ubuntu18.04 迅为rk3568开发板 注册杂项设备 相较于字符设备,杂项设备有以下两个优点: 节省主设备号:杂项设备的主设备号固定为 10,在系统中注册多个 misc 设备驱动时&…...
Golang条件编译 | 获取系统的磁盘空间内存占用demo | gopsutil/disk库(跨平台方案)
文章目录 一、Golang条件编译1. 构建标签( Build tags)2. 文件后缀(File suffixes) 二、GO golang 获取磁盘空间 条件编译思路 三、【推荐】使用github.com/shirou/gopsutil/disk这个库,如何获取机器下不同磁盘分区的内容 一、Golang条件编译…...
22/76-池化
池化(最大池化层:选每个kernel中最大的数) 填充、步幅、多个通道: 池化层与卷积层类似,都具有填充和步幅。 没有可学习的参数。 在每个输入通道应用池化层以获得相应的输出通道。 输出通道数输入通道数。 平均池化层…...
江科大STM32 下
目录 ADC数模转换器DMA直接存储器存取USART串口9-2 串口发送接受9-3 串口收发HEX数据包 I2C(mpu6050陀螺仪和加速度计)SPI协议10.1 SPI简介W25Q64简介10.3 SPI软件读写W25Q6410.4 SPI硬件读写W25Q64 BKP、RTC11.0 Unix时间戳11.1 读写备份寄存器BKP11.2 RTC实时时钟 十二、PWR1…...
利用HTML和CSS实现的浮动布局
代码如下 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><style>*{m…...
2024年第十届控制、自动化与机器人国际会议(ICCAR 2024)即将召开!
2024年4月27~29日 新加披 会议官网:10th-ICCAR 2024https://iccar.org/index.html 第十届控制、自动化和机器人国际会议将于2024年4月27-29日在新加坡举办。本次会议由新加坡电子学会,IEEE机器人和自动控制协会和IEEE联合主办,并得到北京航空…...
基于python集成学习算法XGBoost农业数据可视化分析预测系统
文章目录 基于python集成学习算法XGBoost农业数据可视化分析预测系统一、项目简介二、开发环境三、项目技术四、功能结构五、功能实现模型构建封装类用于网格调参训练模型系统可视化数据请求接口模型评分 0.5*mse 六、系统实现七、总结 基于python集成学习算法XGBoost农业数据可…...
第29集《佛法修学概要》
丁三、声闻乘 分二:戊一、释义;戊二、四谛法;戊三、结示 请大家打开讲义第八十二页。我们看丁三,声闻乘。 在祖师大德的判教当中,把我们整个大乘的成佛之道分成了三个部分:第一个是安乐道,第…...
奥伦德光电耦合器5G通信领域及其相关领域推荐
光电耦合器是以光为媒介传输电信号的一种电-光-电转换器件。由于该器件使用寿命长、工作温度范围宽,所以在过程控制、工业通信、家用电器、医疗设备、通信设备、计算机以及精密仪器等方面有着广泛应用在当前工艺技术持续发展与提升的过程中,其工作速度、…...
机器学习算法 - 马尔可夫链
马尔可夫链(Markov Chain)可以说是机器学习和人工智能的基石,在强化学习、自然语言处理、金融领域、天气预测、语音识别方面都有着极其广泛的应用 > The future is independent of the past given the present 未来独立于过去ÿ…...
Linux下防火墙相关命令整理
目录 一.前言二.相关命令整理 一.前言 这篇文章简单整理一下Linux系统中防火墙相关命令。 二.相关命令整理 开启防火墙 systemctl start firewalld关闭防火墙 systemctl stop firewalld重启防火墙 systemctl restart firewalld开机启用防火墙 systemctl enable firewall…...
Python八股文总结
一. Python基本数据结构有哪四种?区别是什么? 列表(List)元组(Tuple)字典(Dictionary)集合(Set) 区别主要在于它们的可变性(是否可以修改&#x…...
计算机导论05-计算机网络
文章目录 计算机网络基础计算机网络概述计算机网络的概念计算机网络的功能计算机网络的组成 计算机网络的发展计算机网络的类型 网络体系结构网络互联模型OSI/RM结构与功能TCP/IP结构模型TCP/IP与OSI/RM的比较 网络地址与分配IP地址构成子网的划分IPv6 传输介质与网络设备网络传…...
sentinel熔断与限流
文章目录 一、sentinel简介Sentinel 是什么?Sentinel安装 二、sentinel整合工程新建cloudalibaba-sentinel-service8401微服务引入依赖yml配置主启动类添加EnableDiscoveryClient业务类测试 三、sentinel流控规则基本介绍流控模式直接(默认)关…...
vi/vim 编辑器 --基本命令
1 vi/vim编辑器介绍 vi 是visual interface 的简称,是Linux中最经典的文本编辑器 vim是vi的加强版。兼容了vi的所有指令,不仅能编辑文本,而且具有shell程序编辑的功能,可以通过不同颜色的字体辨别语法的正确性,极大…...
C++——STL标准模板库——容器详解——set
一、基本概念 set容器是一种具备自动排序功能的集合,默认递增排序;元素无法直接修改,且不能重复;另一个版本叫做multiset,允许存在重复元素,其他功能和性质一样。 set容器底层结构一般为自平衡二叉搜索树…...
Vim一键配置指南,打造高效率C++开发环境
文章目录 前言安装与卸载功能演示gcc/g升级问题 前言 Vim作为当下最受欢迎的文本编译器之一,不仅具有强大的文本编辑功能,还提供了高度的可定制性。用户可以根据自己的喜好自定义配置,并且通过自己编写插件或者使用现有的插件来扩展Vim的功能…...
新航向,新生态: Michael在出海业务圆桌会议分享HyperBDR全球业务拓展之道
1月15日-16日,以“领航新开局,共赢新生态”为主题的华为云生态大会2024在华为云贵安数据中心云上屯盛大举行。本次会议聚焦于华为云全国生态伙伴与开发者,旨在共同见证华为云生态战略的最新进展和伙伴政策的新升级。与会者将分享来自优秀生态…...
SpringBoot异步处理
Spring boot异步处理 业务场景: 如执行数据库备份任务,前端发起请求到后端,后端备份数据库的处理逻辑需要很长一段时间,此时前端会一直等待后端返回结果,给用户给等待时间过长,这是就要考虑异步处理了&…...
2024年甘肃省职业院校技能大赛信息安全管理与评估 样题一 模块二
竞赛需要完成三个阶段的任务,分别完成三个模块,总分共计 1000分。三个模块内容和分值分别是: 1.第一阶段:模块一 网络平台搭建与设备安全防护(180 分钟,300 分)。 2.第二阶段:模块二…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...
vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...
mac:大模型系列测试
0 MAC 前几天经过学生优惠以及国补17K入手了mac studio,然后这两天亲自测试其模型行运用能力如何,是否支持微调、推理速度等能力。下面进入正文。 1 mac 与 unsloth 按照下面的进行安装以及测试,是可以跑通文章里面的代码。训练速度也是很快的。 注意…...
车载诊断架构 --- ZEVonUDS(J1979-3)简介第一篇
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 做到欲望极简,了解自己的真实欲望,不受外在潮流的影响,不盲从,不跟风。把自己的精力全部用在自己。一是去掉多余,凡事找规律,基础是诚信;二是…...
2025年低延迟业务DDoS防护全攻略:高可用架构与实战方案
一、延迟敏感行业面临的DDoS攻击新挑战 2025年,金融交易、实时竞技游戏、工业物联网等低延迟业务成为DDoS攻击的首要目标。攻击呈现三大特征: AI驱动的自适应攻击:攻击流量模拟真实用户行为,差异率低至0.5%,传统规则引…...
【java面试】微服务篇
【java面试】微服务篇 一、总体框架二、Springcloud(一)Springcloud五大组件(二)服务注册和发现1、Eureka2、Nacos (三)负载均衡1、Ribbon负载均衡流程2、Ribbon负载均衡策略3、自定义负载均衡策略4、总结 …...
