lv15 input子系统框架、外设驱动开发 5

news2025/1/20 3:40:56

一、input子系统基本框架

 在我们日常的Linux系统中,存在大量的输入设备,例如按键、鼠标、键盘、触摸屏、摇杆等,他们本身就是字符设备,linux内核将这些字符设备的共同性抽象出来,简化驱动开发建立了一个input子系统。

Linux内核为了两个目的:

  1. 简化纯输入类外设(如:键盘、鼠标、游戏杆、轨迹球、触摸屏。。。等等)的驱动开发

  2. 统一输入类外设产生的数据格式(struct input_event),更加方便应用层编程

设计了输入子系统框架

Linux 内核驱动可以都是遵循一个逐层抽象的架构: 最上层的抽象层便于系统软件的访问,中间层的实现硬件协议细节,同时提供上下两层连接的接口,对于最下层的 driver 来说就是要定义底层驱动要实现的接口和实际的设备控制,由于 Linux 内核各类驱动的框架支持,driver 可以更加关注设备本身的特性。
Linux输入子系统(linux input subsystem)也不例外,从上到下可以分为三层实现,分别为:输入子系统事件处理层(EventHandler)、输入子系统核心层(InputCore)和输入子系统设备驱动层。

事件处理层:接收来自核心层上报的事件,并选择对应的handler(事件处理器 struct input_handler)去处理。内核维护着多个事件处理器对象,每个input_handler对象专门处理一类事件,所有产生同类事件的设备驱动共用同一个handler。

设备驱动层:主要实现获取硬件设备的数据信息(包括触摸屏被按下、按下位置、鼠标移动、键盘按下等等),并转换为核心层定义的规范事件后提交给核心层,该层每个设备对应一个struct input_dev对象,

核心层:负责连接设备驱动层和事件处理层,为设备驱动层提供输入设备驱动的接口(struct input_dev)以及输入设备驱动的注册函数(input_register_device),为事件处理层提供输入事件驱动的接口;通知事件处理层对事件进行处理。

二、驱动开发步骤

/*init或probe函数中:
1. 创建struct input_dev对象input_allocate_device
2. 设置事件类型以及相关参数set_bit
3. 注册struct input_dev对象input_register_device
*/
​
/*exit或remove函数中:
1. 注销struct input_dev对象input_unregister_device
2. 销毁struct input_dev对象input_free_device
*/
​
/*上报事件
    两种事件上报方式:
    1. 对有中断支持的输入设备:在其中断处理函数(上半部或下半部)中上报事件
    2. 对无中断支持的输入设备:使用workqueue循环定时上报(struct delayed_work)
    主要函数:
    input_event       //通用上报,上报的是分量,如x轴分量
    input_report_abs  //上报绝对坐标
    input_sync        //完整数据检查统一上报在这边处理,如x、y、z轴数据统一上报
*/
​

相关接口:

/*_init*/
struct input_dev *input_allocate_device(void)//创建对象
​
void set_bit(struct input_dev *dev,unsigned long whichbits)//设置事件类型
​
void input_set_abs_params(struct input_dev *dev,unsigned int axis,int min,int max,int fuzz,int flat)
​
int input_register_device(struct input_dev *dev)//注册input设备到内核
​
/*_exit*/
void input_unregister_device(struct input_dev *dev)
void input_free_device(struct input_dev *dev)
​
/*上报事件*/
void input_event(struct input_dev *,unsigned int t,unsigned int c,int v)
​
void input_report_key(struct input_dev *,unsigned int c,int v) //上报按键事件
void input_report_abs(struct input_dev *,unsigned int c,int v)//上报绝对坐标事件
    
void input_sync(struct input_dev *)//上报完成后需要调用这些函数来通知系统处理完整事件
​
/*应用层数据类型*/
struct input_event {
    struct timeval time;       // 时间戳
    __u16 type;             // 事件类型
    __u16 code;             // 哪个分值
    __s32 value;            // 具体值      
};

三、key2-input版代码解析

借用直接按键驱动的代码,按键中所有根字符设备相关的input子系统已经帮我们实现好了,都可以不需要。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/mm.h>
#include <linux/input.h>   //<------------
#include <linux/delay.h>
#include <linux/slab.h>
#include <asm/uaccess.h>


struct fs4412key2_dev
{
	struct input_dev *pdev;
	
	int gpio;
	int irqno;
};

struct fs4412key2_dev *pgmydev = NULL;

irqreturn_t key2_irq_handle(int no,void *arg)  
{
	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)arg;
	int status1 = 0;
	int status2 = 0;

	status1 = gpio_get_value(pmydev->gpio);
	mdelay(1);
	status2 = gpio_get_value(pmydev->gpio);

	if(status1 != status2)
	{
		return IRQ_NONE;
	}

	if(status1)
	{
		input_event(pmydev->pdev,EV_KEY,KEY_2,0);    //<--------------------上报事件,0按下
		input_sync(pmydev->pdev);                    //往核心层上报
	}
	else
	{
		input_event(pmydev->pdev,EV_KEY,KEY_2,1);    //<--------------------上报事件,1抬起
		input_sync(pmydev->pdev);                    //往核心层上报
	}

	return IRQ_HANDLED;
}

int __init fs4412key2_init(void)
{
	int ret = 0;

	struct device_node *pnode = NULL;

	pnode = of_find_node_by_path("/fs4412-key2");
	if(NULL == pnode)
	{
		printk("find node failed\n");
		return -1;
	}


	pgmydev = (struct fs4412key2_dev *)kmalloc(sizeof(struct fs4412key2_dev),GFP_KERNEL);
	if(NULL == pgmydev)
	{
		printk("kmallc for struct fs4412key2_dev failed\n");
		return -1;
	}

	pgmydev->gpio = of_get_named_gpio(pnode,"key2-gpio",0);

	pgmydev->irqno = irq_of_parse_and_map(pnode,0);
                                              //<---------------------
	pgmydev->pdev = input_allocate_device();  //分配pdev空间

	set_bit(EV_KEY,pgmydev->pdev->evbit);     //设置事件
	set_bit(KEY_2,pgmydev->pdev->keybit);     //上报的哪个按键

	ret = input_register_device(pgmydev->pdev);//注册到系统
	
	ret = request_irq(pgmydev->irqno,key2_irq_handle,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,"fs4412key2",pgmydev);
	if(ret)
	{
		printk("request_irq failed\n");
		input_unregister_device(pgmydev->pdev);  //反操作
		input_free_device(pgmydev->pdev);        //反操作
		kfree(pgmydev);
		pgmydev = NULL;
		return -1;
	}
	return 0;
}

void __exit fs4412key2_exit(void)
{

	free_irq(pgmydev->irqno,pgmydev);

	input_unregister_device(pgmydev->pdev);    //反操作
	input_free_device(pgmydev->pdev);          //反操作
 
	kfree(pgmydev);
	pgmydev = NULL;
}


MODULE_LICENSE("GPL");

module_init(fs4412key2_init);
module_exit(fs4412key2_exit);

测试

testkey.2

#include <sys/types.h>
#include <sys/stat.h>
#include <linux/input.h>
#include <fcntl.h>
#include <unistd.h>

#include <stdio.h>

int main(int argc,char *argv[])
{
	int fd = -1;
	struct input_event evt;
	if(argc < 2)
	{
		printf("Argument is too few\n");
		return 1;
	}

	/*open*/
	fd = open(argv[1],O_RDONLY);
	if(fd < 0)
	{
		printf("open %s failed\n",argv[1]);
		return 2;
	}

	/*init mpu6050*/

	while(1)
	{
		read(fd,&evt,sizeof(evt));
		if(evt.type == EV_KEY && evt.code == KEY_2)
		{
			if(evt.value)
			{
				printf("KEY2 DOWN\n");
			}
			else
			{
				printf("KEY2 UP\n");
			}
		}
	}


	/*close*/
	close(fd);
	fd = -1;
	return 0;
}

Makefile 

ifeq ($(KERNELRELEASE),)

ifeq ($(ARCH),arm)
KERNELDIR ?= /home/linux/Linux_4412/kernel/linux-3.14
ROOTFS ?= /opt/4412/rootfs
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
endif
PWD := $(shell pwd)


modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules INSTALL_MOD_PATH=$(ROOTFS) modules_install

clean:
	rm -rf  *.o  *.ko  .*.cmd  *.mod.*  modules.order  Module.symvers   .tmp_versions

else

CONFIG_MODULE_SIG=n

obj-m += fs4412_key2.o

endif

 编译拷贝到跟文件系统测试

 

四、mpu6050-input版代码解析

mpu6050_drv_input.c

注:绝对坐标的上报事件,如角速度加速度无变化核心层会屏蔽掉,除非有变化

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/input.h>
#include <linux/io.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>

#define SMPLRT_DIV 0x19
#define CONFIG 0x1A
#define GYRO_CONFIG 0x1B
#define ACCEL_CONFIG 0x1C

#define ACCEL_XOUT_H 0x3B
#define ACCEL_XOUT_L 0x3C
#define ACCEL_YOUT_H 0x3D
#define ACCEL_YOUT_L 0x3E
#define ACCEL_ZOUT_H 0x3F
#define ACCEL_ZOUT_L 0x40
#define TEMP_OUT_H 0x41
#define TEMP_OUT_L 0x42
#define GYRO_XOUT_H 0x43
#define GYRO_XOUT_L 0x44
#define GYRO_YOUT_H 0x45
#define GYRO_YOUT_L 0x46
#define GYRO_ZOUT_H 0x47
#define GYRO_ZOUT_L 0x48

#define PWR_MGMT_1  0x6B


struct mpu6050_dev
{
	struct input_dev * pinput;   //<---------------------

	struct i2c_client *pclient;

	struct delayed_work work;    //可设置时间,到时间work回调函数会被调用
};

struct mpu6050_dev *pgmydev = NULL;

int mpu6050_read_byte(struct i2c_client *pclt,unsigned char reg)
{
	int ret = 0;
	char txbuf[1] = {reg};
	char rxbuf[1] = {0};

	struct i2c_msg msg[2] = 
	{
		{pclt->addr,0,1,txbuf},
		{pclt->addr,I2C_M_RD,1,rxbuf}
	};

	ret = i2c_transfer(pclt->adapter,msg,ARRAY_SIZE(msg));
	if(ret < 0)
	{
		printk("ret = %d,in mpu6050_read_byte\n",ret);
		return ret;
	}

	return rxbuf[0];
}


int mpu6050_write_byte(struct i2c_client *pclt,unsigned char reg,unsigned char val)
{
	int ret = 0;
	char txbuf[2] = {reg,val};

	struct i2c_msg msg[1] = 
	{
		{pclt->addr,0,2,txbuf},
	};

	ret = i2c_transfer(pclt->adapter,msg,ARRAY_SIZE(msg));
	if(ret < 0)
	{
		printk("ret = %d,in mpu6050_write_byte\n",ret);
		return ret;
	}

	return 0;
}

void mpu6050_work_func(struct work_struct *pwk)
{
	struct mpu6050_dev *pmydev = container_of((struct delayed_work *)pwk,struct mpu6050_dev,work);
	unsigned short ax = 0;
	unsigned short ay = 0;
	unsigned short az = 0;
	unsigned short gx = 0;
	unsigned short gy = 0;
	unsigned short gz = 0;
	unsigned short temp = 0;

	ax = mpu6050_read_byte(pmydev->pclient,ACCEL_XOUT_L);
	ax = mpu6050_read_byte(pmydev->pclient,ACCEL_XOUT_H) << 8;
	input_report_abs(pmydev->pinput,ABS_X,ax);    //<-----上报分量
			
	ay = mpu6050_read_byte(pmydev->pclient,ACCEL_YOUT_L);
	ay = mpu6050_read_byte(pmydev->pclient,ACCEL_YOUT_H) << 8;
	input_report_abs(pmydev->pinput,ABS_Y,ay);

	az = mpu6050_read_byte(pmydev->pclient,ACCEL_ZOUT_L);
	az = mpu6050_read_byte(pmydev->pclient,ACCEL_ZOUT_H) << 8;
	input_report_abs(pmydev->pinput,ABS_Z,az);
			
	gx = mpu6050_read_byte(pmydev->pclient,GYRO_XOUT_L);
	gx = mpu6050_read_byte(pmydev->pclient,GYRO_XOUT_H) << 8;
	input_report_abs(pmydev->pinput,ABS_RX,gx);
			
	gy = mpu6050_read_byte(pmydev->pclient,GYRO_YOUT_L);
	gy = mpu6050_read_byte(pmydev->pclient,GYRO_YOUT_H) << 8;
	input_report_abs(pmydev->pinput,ABS_RY,gy);

	gz = mpu6050_read_byte(pmydev->pclient,GYRO_ZOUT_L);
	gz = mpu6050_read_byte(pmydev->pclient,GYRO_ZOUT_H) << 8;
	input_report_abs(pmydev->pinput,ABS_RZ,gz);
			
	temp = mpu6050_read_byte(pmydev->pclient,TEMP_OUT_L);
	temp = mpu6050_read_byte(pmydev->pclient,TEMP_OUT_H) << 8;
	input_report_abs(pmydev->pinput,ABS_MISC,temp);

	input_sync(pmydev->pinput);        //<----------绝对坐标真正上报,如角速度加速度无变化核心层会屏蔽掉,除非有变化
	schedule_delayed_work(&pgmydev->work,msecs_to_jiffies(1000));  //<---------延时1秒调用
}

void init_mpu6050(struct i2c_client *pclt)
{
	mpu6050_write_byte(pclt,PWR_MGMT_1,0x00);
	mpu6050_write_byte(pclt,SMPLRT_DIV,0x07);
	mpu6050_write_byte(pclt,CONFIG,0x06);
	mpu6050_write_byte(pclt,GYRO_CONFIG,0xF8);
	mpu6050_write_byte(pclt,ACCEL_CONFIG,0x19);
}

static int mpu6050_probe(struct i2c_client *pclt,const struct i2c_device_id *pid)
{
	int ret = 0;

	pgmydev = (struct mpu6050_dev *)kmalloc(sizeof(struct mpu6050_dev),GFP_KERNEL);
	if(NULL == pgmydev)
	{
		printk("kmalloc failed\n");
		return -1;
	}
	memset(pgmydev,0,sizeof(struct mpu6050_dev));

	pgmydev->pclient = pclt;

	init_mpu6050(pgmydev->pclient);

	pgmydev->pinput = input_allocate_device();     //<-----------------

	set_bit(EV_ABS,pgmydev->pinput->evbit);        //<-----------------设置绝对坐标类事件
	input_set_abs_params(pgmydev->pinput,ABS_X,-32768,32767,0,0);   //<--------32768,32767代表取值范围,0误差范围,0代表不使用平滑参数值
	input_set_abs_params(pgmydev->pinput,ABS_Y,-32768,32767,0,0);
	input_set_abs_params(pgmydev->pinput,ABS_Z,-32768,32767,0,0);
	input_set_abs_params(pgmydev->pinput,ABS_RX,-32768,32767,0,0);
	input_set_abs_params(pgmydev->pinput,ABS_RY,-32768,32767,0,0);
	input_set_abs_params(pgmydev->pinput,ABS_RZ,-32768,32767,0,0);
	input_set_abs_params(pgmydev->pinput,ABS_MISC,-32768,32767,0,0); //温度

	ret = input_register_device(pgmydev->pinput);  //<-----------------

	if(ret)
	{
		printk("input_register_device failed\n");

		input_free_device(pgmydev->pinput);
		pgmydev->pinput = NULL;

		kfree(pgmydev);
		pgmydev = NULL;
		return -1;
	}

	INIT_DELAYED_WORK(&pgmydev->work,mpu6050_work_func);   //<-----------------


	schedule_delayed_work(&pgmydev->work,msecs_to_jiffies(1000));  //<-----------------1s后函数会被调用
	return 0;
}

static int mpu6050_remove(struct i2c_client *pclt)
{
	cancel_delayed_work(&pgmydev->work);        //<-----------------取消每隔1秒的函数

	input_unregister_device(pgmydev->pinput);   //<-----------------

	input_free_device(pgmydev->pinput);
	pgmydev->pinput = NULL;

	kfree(pgmydev);
	pgmydev = NULL;

	return 0;
}

struct of_device_id mpu6050_dt[] = 
{
	{.compatible = "invensense,mpu6050"},
	{}
};


struct i2c_device_id mpu6050_ids[] = 
{
	{"mpu6050",0},
	{}
};


struct i2c_driver mpu6050_driver = 
{
	.driver = {
		.name = "mpu6050",
		.owner = THIS_MODULE,
		.of_match_table = mpu6050_dt,
	},
	.probe = mpu6050_probe,
	.remove = mpu6050_remove,
	.id_table = mpu6050_ids,
};

#if 0
int __init mpu6050_driver_init(void)
{
	i2c_add_driver(&mpu6050_driver);
}

void __exit mpu6050_driver_exit(void)
{
	i2c_del_driver(&mpu6050_driver);
}
module_init(mpu6050_driver_init);
module_exit(mpu6050_driver_exit);
#else
module_i2c_driver(mpu6050_driver);
#endif

MODULE_LICENSE("GPL");

testmpu6050_input.c

#include <sys/types.h>
#include <sys/stat.h>
#include <linux/input.h>
#include <fcntl.h>
#include <unistd.h>

#include <stdio.h>

int main(int argc,char *argv[])
{
	int fd = -1;
	struct input_event evt;
	if(argc < 2)
	{
		printf("Argument is too few\n");
		return 1;
	}

	/*open*/
	fd = open(argv[1],O_RDONLY);
	if(fd < 0)
	{
		printf("open %s failed\n",argv[1]);
		return 2;
	}

	/*init mpu6050*/

	while(1)
	{
		read(fd,&evt,sizeof(evt));
		if(evt.type == EV_ABS)
		{
			switch(evt.code)
			{
				case ABS_X:
					printf("Accel-x:%d\n",evt.value);
					break;
				case ABS_Y:
					printf("Accel-y:%d\n",evt.value);
					break;
				case ABS_Z:
					printf("Accel-z:%d\n",evt.value);
					break;
				case ABS_RX:
					printf("Gyro-x:%d\n",evt.value);
					break;
				case ABS_RY:
					printf("Gyro-y:%d\n",evt.value);
					break;
				case ABS_RZ:
					printf("Gyro-z:%d\n",evt.value);
					break;
				case ABS_MISC:
					printf("Temp:%d\n",evt.value);
					break;
			}
		}
	}


	/*close*/
	close(fd);
	fd = -1;
	return 0;
}

Makefile

ifeq ($(KERNELRELEASE),)

ifeq ($(ARCH),arm)
KERNELDIR ?= /home/linux/Linux_4412/kernel/linux-3.14
ROOTFS ?= /opt/4412/rootfs
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
endif
PWD := $(shell pwd)


modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules INSTALL_MOD_PATH=$(ROOTFS) modules_install

clean:
	rm -rf  *.o  *.ko  .*.cmd  *.mod.*  modules.order  Module.symvers   .tmp_versions

else

CONFIG_MODULE_SIG=n

obj-m += mpu6050_drv_input.o

endif

编译拷贝到rootfs 

验证 

有数值变化才会上传值

拓展:

网络设备、块设备 的开发套路设计思想也类似。

网络设备面向的是协议栈

块设备面向的是文件系统

字符设备面向的是应用层

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

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

相关文章

Springboot+vue的物流管理系统(有报告)。Javaee项目,springboot vue前后端分离项目

演示视频&#xff1a; Springbootvue的物流管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot vue前后端分离项目 项目介绍&#xff1a; 本文设计了一个基于Springbootvue的前后端分离的物流管理系统&#xff0c;采用M&#xff08;model&#xff09;…

【COMP337 LEC 5-6】

LEC 5 Perceptron &#xff1a; Binary Classification Algorithm 8 感应器是 单个神经元的模型 突触连接的强度取决于接受外部刺激的反应 X input W weights a x1*w1x2*w2....... > / < threshold Bias MaxIter is a hyperparameter 超参数 which has to be chosen…

Android系统app开发

Android系统app开发 系统app阔以使用很多系统源码中隐藏的api 首先先编译出jar包 整编源码后&#xff0c;在这个目录下&#xff0c;这个就是包含系统源码隐藏api的jar包 系统app的标志 拷贝jar文件到app模块下 在编译之前把这个jar添加到classpath路径里面去&#xff0c;这样…

【机构vip教程】Selenium(2):selenium IDE工具

Selenium IDE工具&#xff1a; 该工具是一个用于构建脚本的初级工具&#xff0c;其实是FireFox的一个插件&#xff0c;拥有一个易于使用的界面。它拥有记录功能&#xff0c;能够记录用户执行的操作&#xff0c;并可以导出为可重复使用的脚本。如果没有编程经验&#xff0c;也可…

JAVA常见IO模型 BIO、NIO、AIO总结

BIO Blocking IO 同步阻塞型IO。当系统进行IO读写的时候&#xff0c;会阻塞&#xff0c;直到IO读写完毕。比如调用系统Read后&#xff0c;需要将内核空间的数据读取到用户空间。需要等待内核空间 数据准备&#xff0c;数据就绪&#xff0c;拷贝数据&#xff0c;线程一直处于阻…

Java面试第一站:计算机网络基础知识

该系列会持续更新&#xff0c;关注我&#xff0c;第一时间获取我的最新动态哟 Java面试中&#xff0c;经常会问到跟计算机网络知识相关的考点&#xff0c;有的小伙伴不是很明白。考察网络知识有什么意义&#xff1f; 因为编程的时候&#xff0c;多数的情况下是不用我们来编写 …

微前端(qiankun)vue3+vite

目录 一、什么是微前端 二、主应用接入 qiankun 1.按照qiankun插件 2.注册微应用引用 3.挂载容器 三、微应用接入 qiankun 1.vite.config.ts 2.main.ts ps&#xff1a;手动加载微应用方式 ps&#xff1a;为什么不用 iframe 一、什么是微前端 微前端是一种多个团队通过独…

探索API测试的奇妙世界:总结与思考!

本文主要是关于 API 测试的方法论探讨。 什么是 API 测试&#xff1f; API 测试是一种软件测试&#xff0c;涉及验证和确认应用程序接口 ( API ) 及其与其他服务组件的交互。测试重点关注软件架构的业务逻辑层&#xff0c;确保API按预期运行、数据准确交换、服务在各种条件下…

嵌入式调试工具之GDB

在单片机开发中&#xff0c;我们可以通过集成式的IDE 来进行调试&#xff0c;比如 MDK、IAR 等。 GDB 工具是 GNU 项目调试器&#xff0c;基于命令行使用。和其他的调试器一样&#xff0c;可使用 GDB工具单步运行程序、单步执行、跳入/跳出函数、设置断点、查看变量等等&#…

SHERlocked93 的 2023 年终总结

工作之后感觉一年一年过的太快&#xff0c;没有个记录连回忆都无从回忆起&#xff0c;之前的年终总结&#xff1a; SHERlocked93 的 2022 年终总结SHERlocked93 的 2021 年终总结SHERlocked93 的 2020 年终总结SHERlocked93 的 2019 年终总结SHERlocked93 的 2018 年终总结SHER…

使用C++,实现高精度加减乘除法运算!

我的个人主页 {\large \mathsf{{\color{Red} 我的个人主页} } } 我的个人主页 我的专栏&#xff1a; \mathcal{{\color{Green} 我的专栏&#xff1a;} } 我的专栏&#xff1a; 《精选文章》《算法》《每日一道编程题》《高精度算法》 文章目录 前言高精度计算初始模版string 转…

【python】网络爬虫与信息提取--正则表达式

一、正则表达式 正则表达式是用来简洁表达一组字符串的表达式。是通用的字符串表达框架&#xff0c;简洁表达一组字符串的表达式&#xff0c;针对字符串表达“简洁”和“特征”思想的工具&#xff0c;判断某字符串的特征归属。 用处&#xff1a;表达文本类型的特征&#xff1b;…

练习题解(关于最短路径)

目录 1.租用游艇 2.邮递员送信 3.【模板】单源最短路径&#xff08;标准版&#xff09; 1.租用游艇 P1359 租用游艇 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 输入数据&#xff1a; 3 5 15 7 因为这道题数据不大&#xff0c;所有我们直接使用Floyd 算法。 这道题大…

网络防御保护——防火墙综合实验

一.实验拓扑 二.实验要求 1.办公区设备可以通过电信和移动两条链路上网(多对多的nat&#xff0c;并且需要保留一个公网ip不能用来转换)。 2.分公司设备可以通过移动链路和电信链路访问到dmz区域的http服务器。 3.分公司内部客户端可以通过公网地址访问到内部服务器。 4.FW1和FW…

使用 cgroup 时踩过的坑

1 cgroup 介绍 1.1 cgroup 介绍 cgroup 全称 control group&#xff0c;控制组。通过 cgroup 可以限制应用使用的资源&#xff0c;资源包括 cpu、内存、磁盘 io、网络等。 工作中经常使用的 docker 容器就使用了 cgroup 进行资源限制和隔离&#xff0c;cgroup 是 docker 的基…

不坑盒子 助力高效办公的Office插件

不坑盒子简介 很多朋友在工作过程中需要对Word文档进行编辑处理&#xff0c;如果想让Word排版更有效率可以试试小编带来的这款不坑盒子软件&#xff0c;是一个非常好用的办公工具&#xff0c;拥有近百项功能的Word&#xff0c;wps插件&#xff0c;支持Office 2010以上的版本&a…

FLUENT Meshing Watertight Geometry工作流入门 - 9 生成体网格

本视频中学到的内容&#xff1a; 讨论体网格的重要性&#xff0c;并了解生成体网格的不同方法 了解体网格质量&#xff0c;以及如何改进 视频链接&#xff1a; FLUENT Meshing入门教程-9生成体网格_哔哩哔哩_bilibili 体网格生成是使用大量离散体积或单元来离散化/表示计算模…

网络模型及传输基本流程

1.OSI 七层模型 OSI &#xff08; Open System Interconnection &#xff0c;开放系统互连&#xff09;七层网络模型称为开放式系统互联参考模型&#xff0c;是一个逻辑上的定义和规范; 把网络从逻辑上分为了 7 层 . 每一层都有相关、相对应的物理设备&#xff0c;比如路由器…

单片机学习笔记---AD模数转换DA数模转换

目录 AD模数转换 XPT2046.c XPT2046.h main.c DA数模转换 main.c 上一篇博客讲了AD/DA转换的工作原理&#xff0c;也介绍了运算放大器的工作原理&#xff0c;这节开始代码演示&#xff01; AD模数转换 新创建一个工程&#xff1a;AD模数转换 第一个工程将用到LCD1602和…

入门者拿捏 Java 的必备小秘诀

本篇会加入个人的所谓‘鱼式疯言’ ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 我会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人能…