3、字符设备驱动框架和开发步骤
一、Linux内核对文件的分类
Linux的文件种类
- 1、-:普通文件
- 2、d:目录文件
- 3、p:管道文件
- 4、s:本地socket文件
- 5、l:链接文件
- 6、c:字符设备
- 7、b:块设备
Linux内核按驱动程序实现模型框架的不同,将设备分为三类
- 1、字符设备:按字节流形式读取的设备,一般情况下按顺序访问,数据量不大,一般不设缓存
- 2、块设备:按块进行读写的设备,最小的块大小为256字节(一个扇区),块的大小必须是扇区的整数倍。Linux块的大小一般为4096字节,随机访问,设有缓存以提高效率
- 3、网络设备:针对网络数据收发的设备
框图
二、设备号
- 用于区分内核中的同类设备
内核中用设备号来区分同类里不同的设备,设备号是一个无符号的32位整数,数据类型为dev_t,设备号分为两部分:
- 1、主设备号:占高12位,用来表示驱动程序相同的一类设备
- 2、次设备号:占低20位,用来表示被操作的具体设备
应用程序打开一个设备时,通过设备号来查找定位内核中管理的具体设备
MKDEV - 宏
- 将主设备号和次设备号组合成32位的完整设备号,用法:
dev_t id;
int major = 251;//主设备号
int minor = 2;//次设备号
id = MKDEV(251, 2);
MAJOR - 宏
- 将主设备号从设备号中分离出来,用法:
dev_t id = MKDEV(251, 2);
int major = MAJOR(id);
MINOR - 宏
- 将次设备号从设备号中分离出来,用法:
dev_t id = MKDEV(251, 2);
int minor = MINOR(id);
如果已知一个设备的主次设备号,应用指定好设备文件名,可以用mknod
命令在/dev目录下创建代表这个设备的文件,即此后应用程序对此文件的操作就是代表对此设备的操作,用法:
cd /dev
mknod 设备文件名 设备种类(c位字符设备,b为块设备) 主设备号 次设备号
在应用程序中如果要创建设备可以调用系统调用函数mknod
,用法:
int mknod(const char *pathname,mode_t mode,dev_t dev);
pathname:带路径的设备文件名,无路径默认为当前目录,一般都创建在/dev下
mode:文件权限或S_IFCHR/S_IFBLK
dev:32位设备号
三申请和注册设备号
字符设备驱动的第一步是通过模块的入口函数__init向内核添加设备驱动的代码框架
- 1、申请设备号
- 2、定义、初始化、向内核添加代表本设备的结构体元素
int register_chrdev_region(dev_t from, unsigned count, const char *name)
功能:手动分配设备号,先验证设备号是否被使用,如果没有就申请该设备号
参数:from:自己指定的设备号count:申请的设备数量name:/proc/devices文件中与该设备对应的名字,方便用户层查询主设备号
返回值:成功为0,失败是负数,绝对值为错误码
int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count, const char *name)
功能:自动分配设备号,查询内核里未被占用的设备号,如果找到则占用该设备号
参数:dev:分配设备号成功后用来存放分配到的设备号baseminor:起始的次设备号,一般为0count:申请设备号的数量name:/proc/devices文件中与该设备对应的名字,方便用户层查询主次设备号
返回值:成功后为0,失败为负数,绝对值为错误码
-
成功分配后在
/proc/devices
可以查看到申请到的主设备号和设备名 -
3、释放设备号
void unregister_chrdev_region(dev_t from, unsigned count)
功能:释放设备号
参数:from:已经成功分配到的设备号将被释放count:申请成功的设备数量
- 释放后
/proc/devices
路径下对应的设备文件记录消失
四:注册字符设备
- 1、字符设备结构体
#include <linux/cdev.h>
struct cdev
{struct kobject kobj;//表示该类型实体是一种内核对象(kernel object)struct module *owner;//填THIS_MODULE,表示还字符设备属于哪个内核模块const struct file_operations *ops;//指向内核空间中存放该设备的各种操作函数地址struct list_head list;//链表指针域dev_t dev;//设备号unsigned int count;//设备数量
};
自己定义的结构体中必须要有一个成员为struct cdev cdev ,两种定义方法:
-
1、直接定义:定义结构体全局变量
-
2、动态申请:
struct cdev * cdev_alloc()
-
初始化
void cdev_init(struct cdev *cdev,const struct file_operations *fops)
功能:初始化字符设备结构体
参数:cdev:设备号fops:操作函数
- 字符设备结构体与设备号绑定
int cdev_add(struct cdev *p,dev_t dev,unsigned int count)
功能:将指定字符设备添加到内核
参数:p:指向被添加的设备dev:设备号count:设备数量
- 移除字符设备
void cdev_del(struct cdev *p)
功能:从内核中移除一个字符设备
参数:p:设备号
- 系统调用函数结构体
#include <linux/fs.h>
struct file_operations
{struct module *owner; //填THIS_MODULE,表示该结构体对象从属于哪个内核模块int (*open) (struct inode *, struct file *); //打开设备int (*release) (struct inode *, struct file *); //关闭设备ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); //读设备ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); //写设备loff_t (*llseek) (struct file *, loff_t, int); //定位long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);//读写设备参数,读设备状态、控制设备unsigned int (*poll) (struct file *, struct poll_table_struct *); //POLL机制,实现多路复用的支持int (*mmap) (struct file *, struct vm_area_struct *); //映射内核空间到用户层int (*fasync) (int, struct file *, int); //信号驱动//......
};
该对象的函数指针成员对应相应的系统调用函数,应用层通过系统调用函数来间接调用这些函数指针指向的设备驱动函数
一般定义一个struct file_operations
类型的全局变量,并用自己实现的各种操作函数名对其初始化
总结:
字符设备开发步骤:
- 如果设备有自己的控制数据,则定义一个包含
struct cdev cdev
成员的结构体(方便管理),其他成员根据设备需求。设备简单直接用struct cdev
- 2、定义一个
struct cdev
的全局变量来表示该设备 - 3、定义三个全局变量分别来表示:主设备号、次设备号、设备数量
- 4、定义一个
struct file_operations
结构体变量,其owner成员赋值为THIS_MODULE - 5、module init函数流程:a. 申请设备号 b. 如果是全局设备指针则动态分配代表本设备的结构体元素 c. 初始化
struct cdev
成员,设置struct cdev
的owner成员为THIS_MODULE e. 添加字符设备到内核 - 6、module exit函数:a. 注销设备号 b. 从内核中移除
struct cdev
. 如果如果是全局设备指针则释放其指向空间 - 7、编写各个操作函数并将函数名初始化给struct file_operations结构体变量
验证步骤:
- 1、编写驱动代码
- 2、make生成ko文件
- 3、insmod内核模块
- 4、查阅字符设备用到的设备号(主设备号):cat /proc/devices | grep 申请设备号时用的名字
- 5、创建设备文件(设备节点) : mknod /dev/??? c 上一步查询到的主设备号 代码中指定初始次设备号
- 6、编写app验证驱动(testmychar_app.c)
- 7、编译运行app,dmesg命令查看内核打印信息
示例:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>//设备结构体
struct cdev cdev;//设备信息
dev_t id;
int major, minor;//驱动操作函数
//打开设备
int char_open (struct inode * inode, struct file * f) //打开设备
{return 0;
}//读
ssize_t char_read (struct file * f, char __user * user, size_t size, loff_t * loff) //读设备
{return size;
}//写
ssize_t char_write (struct file * d , const char __user * user, size_t size, loff_t * loff) //写设备
{return size;
}//关闭
int char_close (struct inode * inod, struct file * f) //关闭设备
{return 0;
}struct file_operations fops = {.owner = THIS_MODULE,.open = char_open,.release = char_close,.write = char_write,.read = char_read,
};int __init init_test(void)
{int ret;//申请设备号(自动分配)alloc_chrdev_region(&id, 0, 1, "char");if(ret){printk("alloc err \r\n");return -EIO;}//初始化设备结构体cdev_init(&cdev, &fops);//设置cdev的owner成员为THIS_MODULEcdev.owner = THIS_MODULE;//添加字符设备到内核cdev_add(&cdev, id, 1);return 0;
}void __exit exit_test(void)
{//移除设备cdev_del(&cdev);//注销设备号unregister_chrdev_region(id, 1);
} MODULE_LICENSE("GPL");
module_init(init_test);
module_exit(exit_test);
五、字符设备驱动框架解析
设备的操作函数如果比喻为桩的话,则:
- 驱动实现操作函数:做桩
- insmod调用init函数:钉桩
- 应用层通过系统调用函数间接调用这些设备的操作函数:用桩
- rmmod调用exit函数:拔桩
操作函数中常用的结构体
- 1、内核中记录文件信息的结构体
struct inode{//....dev_t i_rdev;//设备号struct cdev *i_cdev;//如果是字符设备才有此成员,指向对应设备驱动程序中的加入系统的struct cdev对象//....
}
/*1、该结构体对象对应着一个实际的设备,一对一2、open一个文件时,如果内核中存在该文件对应的inode对象就不再创建,不存在则创建3、内核中用此类型关联到此文件的操作函数集(对设备而言就是关联到具体的驱动代码)
*/
- 2、读写文件内容过程中用到的一些控制数据组合的对象
struct file
{//...mode_t f_mode;//不同用户的操作权限,驱动一般不用loff_t f_pos;//数据位置指示器,需要控制数据开始读写位置的设备有用unsigned int f_flags;//open是的第二个参数flags存放在此,驱动中常用struct file_operations *f_op;//open时从struct inode中i_cdev的对应成员获得地址。驱动开发中用来协助理解工作原理,内核中使用void *private_data;//本次打开文件的私有数据驱动中常用来在几个操作函数中传递数据struct dentry *f_dentry;//驱动中一般不用,除非需要访问对应文件的inode,用法flip->f_dentry->d_inodeint refcnt;//引用计数,保存着该对象地址的位置个数,close时发现refcnt为0才会销毁该struct file对象//...
};
/*1、open函数被调用一次,则创建一个该对象,可以认为一个该对象对应一次指定文件的操作2、open一个文件多次,每次open都会创建一个该对象3、文件描述符中存放的地址指向该类型的对象4、每个文件描述符都对应一个struct file对象的地址
*/
字符设备驱动程序框架
-
驱动实现端:
-
驱动使用端:
参考原理图
常用操作函数
int (*open) (struct inode *, struct file *); //打开设备
/*指向函数一般用来对设备进行硬件上的初始化,对于一些简单的设备该函数只需要return 0,对应open系统调用,是open系统调用函数实现过程中调用的函数,
*/int (*release) (struct inode *, struct file *); //关闭设备
/*,指向函数一般用来对设备进行硬件上的关闭操作,对于一些简单的设备该函数只需要return 0,对应close系统调用,是close系统调用函数实现过程中调用的函数
*/ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); //读设备
/*指向函数用来将设备产生的数据读到用户空间,对应read系统调用,是read系统调用函数实现过程中调用的函数
*/ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); //写设备
/*指向函数用来将用户空间的数据写进设备,对应write系统调用,是write系统调用函数实现过程中调用的函数
*/loff_t (*llseek) (struct file *, loff_t, int); //数据操作位置的定位
/*指向函数用来获取或设置设备数据的开始操作位置(位置指示器),对应lseek系统调用,是lseek系统调用函数实现过程中调用的函数
*/long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);//读写设备参数,读设备状态、控制设备
/*指向函数用来获取、设置设备一些属性或设备的工作方式等非数据读写操作,对应ioctl系统调用,是ioctl系统调用函数实现过程中调用的函数
*/unsigned int (*poll) (struct file *, struct poll_table_struct *);//POLL机制,实现对设备的多路复用方式的访问
/*指向函数用来协助多路复用机制完成对本设备可读、可写数据的监控,对应select、poll、epoll_wait系统调用,是select、poll、epoll_wait系统调用函数实现过程中调用的函数
*/int (*fasync) (int, struct file *, int); //信号驱动
/*指向函数用来创建信号驱动机制的引擎,对应fcntl系统调用的FASYNC标记设置,是fcntl系统调用函数FASYNC标记设置过程中调用的函数
*/
六、读操作实现
ssize_t xxx_read(struct file *filp, char __user *pbuf, size_t count, loff_t *ppos);
完成功能:读取设备产生的数据
参数:filp:指向open产生的struct file类型的对象,表示本次read对应的那次openpbuf:指向用户空间一块内存,用来保存读到的数据count:用户期望读取的字节数ppos:对于需要位置指示器控制的设备操作有用,用来指示读取的起始位置,读完后也需要变更位置指示器的指示位置返回值:本次成功读取的字节数,失败返回-1
- 由于内核不能直接和用户空间进行数据传输,因此要使用函数实现:
- 从内核中复制数据到用户空间
#include <asm-generic/uaccess.h>
unsigned long copy_to_user (void __user * to, const void * from, unsigned long n)
成功为返回0,失败非0
七、写操作实现
ssize_t xxx_write (struct file *filp, const char __user *pbuf, size_t count, loff_t *ppos);
完成功能:向设备写入数据
参数:filp:指向open产生的struct file类型的对象,表示本次write对应的那次openpbuf:指向用户空间一块内存,用来保存被写的数据count:用户期望写入的字节数ppos:对于需要位置指示器控制的设备操作有用,用来指示写入的起始位置,写完后也需要变更位置指示器的指示位置返回值:本次成功写入的字节数,失败返回-1
- 从用户空间复制数据到内核中
#include <asm-generic/uaccess.h>
unsigned long copy_from_user (void * to, const void __user * from, unsigned long n)成功为返回0,失败非0
八、ioctl的实现
- 可以已知结构体成员获得所在结构体变量的地址
container_of(成员地址,结构体类名,成员的名称)
long xxx_ioctl (struct file *filp, unsigned int cmd, unsigned long arg);
功能:对相应设备做指定的控制操作(各种属性的设置获取等等)
参数:filp:指向open产生的struct file类型的对象,表示本次ioctl对应的那次opencmd:用来表示做的是哪一个操作arg:和cmd配合用的参数
返回值:成功为0,失败-1
- cmd的组成
1. dir(direction),ioctl 命令访问模式(属性数据传输方向),占据 2 bit,可以为 _IOC_NONE、_IOC_READ、_IOC_WRITE、_IOC_READ | _IOC_WRITE,分别指示了四种访问模式:无数据、读数据、写数据、读写数据;
2. type(device type),设备类型,占据 8 bit,在一些文献中翻译为 “幻数” 或者 “魔数”,可以为任意 char 型字符,例如 ‘a’、’b’、’c’ 等等,其主要作用是使 ioctl 命令有唯一的设备标识;
3. nr(number),命令编号/序数,占据 8 bit,可以为任意 unsigned char 型数据,取值范围 0~255,如果定义了多个 ioctl 命令,通常从 0 开始编号递增;
4. size,涉及到 ioctl 函数 第三个参数 arg ,占据 13bit 或者 14bit(体系相关,arm 架构一般为 14 位),指定了 arg 的数据类型及长度,如果在驱动的 ioctl 实现中不检查,通常可以忽略该参数;
九、创建多个次设备
每一个具体设备(次设备不一样),必须有一个struct cdev
来代表它
并且需要以下三个操作:
- cdev_init
- cdev.owner赋值
- cdev_add
十、(多个次设备同时获取设备号、同时创建设备)在驱动中创建设备
内核中 实现 创建 设备文件 /dev/xxx
1、创建设备
- 手动方式 创建设备文件
sudo mknod /dev/xxx c 243 0 设备节点名 字符型 主设备号 次设备号 在Linux内核 3.0后 引入了 udev服务 该服务可以用于 检查驱动的 设备文件添加请求, 然后添加对应的设备文件
- 自动创建设备文件
#include <linux/device.h>
struct class * class_create(owner, name);
owner 所有者 THIS_MODULE
name 类的名字 创建之后可以看到该名字 `cat /proc/devices | grep 类的名字`返回值: struct class * 出差 返回 错误指针
2、关联设备节点与设备文件名
#include <linux/device.h>
struct device *device_create(struct class *cls, struct device *parent,dev_t devt, void *drvdata,const char *fmt, ...);
例子:dev = device_create(cls, NULL, id, NULL, "char%d", 1);
3、卸载驱动时销毁设备节点
void device_destroy(struct class *cls, dev_t devt);
4、销毁类
void class_destroy(struct class *cls);
示例:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>//设备结构体
struct cdev cdev;//设备信息
dev_t id;
int major, minor;//class类型
struct class *cls;struct device *dev;//驱动操作函数
//打开设备
int char_open (struct inode * inode, struct file * f) //打开设备
{return 0;
}//读
ssize_t char_read (struct file * f, char __user * user, size_t size, loff_t * loff) //读设备
{return size;
}//写
ssize_t char_write (struct file * d , const char __user * user, size_t size, loff_t * loff) //写设备
{return size;
}//关闭
int char_close (struct inode * inod, struct file * f) //关闭设备
{return 0;
}struct file_operations fops = {.owner = THIS_MODULE,.open = char_open,.release = char_close,.write = char_write,.read = char_read,
};int __init init_test(void)
{int ret;int i;//申请设备号(自动分配)四个设备alloc_chrdev_region(&id, 0, 4, "char_dev");if(ret){printk("alloc err \r\n");return -EIO;}//初始化设备结构体cdev_init(&cdev, &fops);//设置cdev的owner成员为THIS_MODULEcdev.owner = THIS_MODULE;//添加字符设备到内核ret = cdev_add(&cdev, id, 4);if(ret){printk("cdev add err \r\n");goto add_err;}//生成设备文件cls = class_create(THIS_MODULE, "char_class");if(IS_ERR(cls)){printk("class create err\r\n");goto class_err; }//关联设备节点和设备名 循环创建4个for(i =0 ;i < 4; i++){dev = device_create(cls, NULL, id + i, NULL, "char_dev%d", i);if(IS_ERR(dev)){printk("device create err\r\n");goto device_err; } }return 0;//回滚
device_err:for(i--; i >= 0; i--){device_destroy(cls, id + i);}//销毁classclass_destroy(cls);class_err://移除设备cdev_del(&cdev);add_err://注销设备号unregister_chrdev_region(id, 4);return -EIO;
}void __exit exit_test(void)
{int i;//取消设备节点for(i =0 ;i < 4; i++){device_destroy(cls, id + i);}//销毁classclass_destroy(cls);//移除设备cdev_del(&cdev);//注销设备号unregister_chrdev_region(id, 4);
} MODULE_LICENSE("GPL");
module_init(init_test);
module_exit(exit_test);
相关文章:

3、字符设备驱动框架和开发步骤
一、Linux内核对文件的分类 Linux的文件种类 1、-:普通文件2、d:目录文件3、p:管道文件4、s:本地socket文件5、l:链接文件6、c:字符设备7、b:块设备 Linux内核按驱动程序实现模型框架的不同&…...

[MySQL]基础篇
文章目录 1. MySQL基本使用1.1 MySQL的启动和登录1.1.1 MySQL的启动1.1.2 MySQL的客户端连接 1.2 数据模型 2. SQL2.1 SQL类型2.1.1 数值类型2.1.2 字符串类型2.1.3 日期类型 2.2 DDL2.2.1 数据库操作2.2.2 表操作 - 查询2.2.3 表操作 - 创建表2.2.4 表操作 - 修改 2.3 DML2.3.…...
Meta Semantic Template for Evaluation of Large Language Models
本文是LLM系列文章,针对《Meta Semantic Template for Evaluation of Large Language Models》的翻译。 大型语言模型评估的元语义模板 摘要1 引言2 相关工作3 方法4 实验5 结论 摘要 大型语言模型(llm)是否真正理解语言的语义,或者只是记住训练数据?…...

Git相关知识(1)
目录 1.初识Git 1.基础知识 2.centos中下载 2.基本操作 1.创建本地仓库 2.配置本地仓库 3.版本库、工作区、暂存区 4.添加文件 5.add和commit对git文件的作用 6.修改文件 7.版本回退 8.撤销修改 9.删除文件 3.分支操作 1.HEAD与分支 2.创建分支 3.删除分支 …...

pytorch中nn.DataParallel多次使用
pytorch中nn.DataParallel多次使用 import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader# 定义模型 class MyModel(nn.Module):def __init__(self):super(MyModel, self).__init__()self.fc nn.Linear(10, 1)def forwa…...

制作电商页面(Html)
任务 制作一个电商页面,要求所卖物品清晰,页面色调清晰,要有主页和详情页。 网站所买物品:书籍 色调:#FF2400 橙红色 代码 主页HTML代码: <html><head><meta charset"utf-8"…...
Android Sutdio依赖Snapshot版本,无法同步最新的包
起因 局域网中搭建了Nexus托管本地打包的aar,正常情况下,把修改完成的库推送到仓库后,其他项目引用Snapshot版本的依赖,同步后会马上下载最新的包,但是当第二次推送后,就没有重新下载最新的包,…...
Feign调用异常触发降级捕获异常
通过配置fallbackFactory来捕获异常信息,代码如下 FeignClient(name "user", fallbackFactory UserFallBackFactory.class) public interface UserFeign {PostMapping("/get/list")Map getList();}Component public class UserFallBackFacto…...

Springboot 音乐网站管理系统idea开发mysql数据库web结构java编程计算机网页源码maven项目
一、源码特点 springboot 音乐网站管理系统是一套完善的信息系统,结合springboot框架和bootstrap完成本系统,对理解JSP java编程开发语言有帮助系统采用springboot框架(MVC模式开发),系统 具有完整的源代码和数据库&…...

微信支付v2-02
...

企业的销售活动是什么?CRM销售管理系统给你答案
在企业业务中,销售活动是实现企业业绩目标的基本单元,起着奠基石的作用。CRM销售管理系统是销售活动管理的必备工具,帮助企业更好地开展销售活动。下面来说说企业的销售活动是什么? 什么是销售活动 简单来说,销售人员…...
【PG】PostgreSQL参数格式 配置文件格式
目录 一 PG参数格式 二 通过配置文件修改参数 postgresql.auto.conf文件 三 通过SQL修改参数 四 通过shell修改参数 五 管理配置文件内容 一 PG参数格式 所有参数名都是大小写不敏感的。每个参数都可以接受五种类型之一的值: 布尔、字符串、整数、 浮点数或枚…...

应用层协议 HTTP
一、应用层协议 我们已经学过 TCP/IP , 已然知道数据能从客户端进程经过路径选择跨网络传送到服务器端进程。 我们还需要知道的是,我们把数据从 A 端传送到 B 端, TCP/IP 解决的是顺丰的功能,而两端还要对数据进行加工处理或者使用…...

Springboot+vue的应急救援物资管理系统,Javaee项目,springboot vue前后端分离项目。
演示视频: Springbootvue的应急救援物资管理系统,Javaee项目,springboot vue前后端分离项目。 项目介绍: 本文设计了一个基于Springbootvue的前后端分离的应急救援物资管理系统,采用M(model)V&…...
创建properties资源文件,并由spring组件类获取资源文件
1.1 创建资源文件file-upload-dev.properties #文件上传地址 file.imageUserFaceLocation=/workspaces/images/foodie/faces #图片访问地址 file.imageServerUrl=http://localhost:8088/foodie/faces1.2 创建spring组件获取资源文件类FileUpload import org.springframework.…...
你知道npm、yarn、pnpm的区别吗?
npm 嵌套的 node_modules 结构 npm 在早期采用的是嵌套的 node_modules 结构,“node_modules” 文件夹通常包含项目依赖的模块。在项目中使用多个依赖并且这些依赖本身也有它们自己的依赖时,就会出现嵌套的 “node_modules” 结构。 嵌套的 “node_mo…...

利用excel表格进行分包和组包
实际使用中,我们可能希望修改某几个数据之后,最终的数据包能够自动发动数据,类似于在给结构体变量修改数据,自动生成完整的结构体; excel语法 1:拆分数据 LEFT(A4,2) – 取A4单元格左边的两个数据 RIGHT(A4…...

Go 语言切片扩容规则是扩容2倍?1.25倍?到底几倍
本次主要来聊聊关于切片的扩容是如何扩的,还请大佬们不吝赐教 切片,相信大家用了 Go 语言那么久这这种数据类型并不陌生,但是平日里聊到关于切片是如何扩容的,很多人可能会张口就来,切片扩容的时候,如果老…...

突破封锁|华为芯片10年进化史:从K3V1到麒麟9000S
华为海思麒麟芯片过去10年研发历程回顾如下: 2009年:华为推出第一款手机芯片K3V1,采用65nm工艺制程,基于ARM11架构,主频600MHz,支持WCDMA/GSM双模网络。这款芯片搭载在华为U8800手机上,标志着华…...

vue建项目
vue3 create-vue 建vue3项目 vscode里改点东西,首先vetur禁用,这个是vue2的,下volar pinia持久化插件:npm i pinia-plugin-persistedstate 配eslint、prettier 在.eslintrc.cjs里配 rules: {// prettier专注于代码的美观度 (格…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...

pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)
目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 (1)输入单引号 (2)万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...
MFE(微前端) Module Federation:Webpack.config.js文件中每个属性的含义解释
以Module Federation 插件详为例,Webpack.config.js它可能的配置和含义如下: 前言 Module Federation 的Webpack.config.js核心配置包括: name filename(定义应用标识) remotes(引用远程模块࿰…...

五子棋测试用例
一.项目背景 1.1 项目简介 传统棋类文化的推广 五子棋是一种古老的棋类游戏,有着深厚的文化底蕴。通过将五子棋制作成网页游戏,可以让更多的人了解和接触到这一传统棋类文化。无论是国内还是国外的玩家,都可以通过网页五子棋感受到东方棋类…...
规则与人性的天平——由高考迟到事件引发的思考
当那位身着校服的考生在考场关闭1分钟后狂奔而至,他涨红的脸上写满绝望。铁门内秒针划过的弧度,成为改变人生的残酷抛物线。家长声嘶力竭的哀求与考务人员机械的"这是规定",构成当代中国教育最尖锐的隐喻。 一、刚性规则的必要性 …...