嵌入式 LINUX 驱动开发 day02 字符设备驱动 字符设备驱动 虚拟串口, 一个驱动支持多个设备

news/2024/10/19 6:26:28/

1. 驱动开发 字符设备驱动

代码: 

vser.c

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>#include <linux/fs.h>   
/***** 设备相关信息 ******/
static unsigned int VSER_MAJOR = 256;      //主设备号
static unsigned int VSER_MINOR = 0;        //次设备号
#define VSER_DEV_CNT   1        //个数为1个
#define VSER_DEV_NAME  "vser"   //设备名称/***** 字符设备注册步骤 ****************************************************        初始化函数* 1.将主设备和次设备组合成设备号   MKDEV(主设备号,次设备号)* 2.将该设备号注册到内核         register_chrdev_region(设备号,设备个数,设备名称) 或 *        注销函数* 1.将申请的设备号注销           unregister_chrdev_region(设备号,设备个数) * * 跟文件系统下创建 设备文件 cat /proc/devices  * 查看到设备信息  Character devices:1 mem256 vser   我们自己申请的设备信息4 /dev/vc/04 tty4 ttyS5 /dev/tty5 /dev/console  * ***********************************************************************/static int __init vser_init(void)
{int ret;    //用于获取函数返回值,并该函数是否执行成功dev_t dev;  //用于存储设备号dev = MKDEV(VSER_MAJOR,VSER_MINOR); //组合成设备号 (VSER_MAJOR << 20) | VSER_MINORret = register_chrdev_region(dev,VSER_DEV_CNT,VSER_DEV_NAME);if(ret != 0){printk("静态申请设备号失败\n");ret = alloc_chrdev_region(&dev,VSER_MINOR,VSER_DEV_CNT,VSER_DEV_NAME);if(ret != 0){printk("动态申请设备号失败\n");goto register_err;}VSER_MAJOR = MAJOR(dev);    //从设备号中提取主设备号VSER_MINOR = MINOR(dev);    //从设备号中提取次设备号}return 0;   //表示成功,直接结束函数并返回0register_err:   /* 设备号申请失败 */return ret;}static void __exit vser_exit(void)
{dev_t dev;  //用于存储设备号dev = MKDEV(VSER_MAJOR,VSER_MINOR); //组合成设备号 (VSER_MAJOR << 20) | VSER_MINORunregister_chrdev_region(dev,VSER_DEV_CNT);
}/****** 加入到内核 *******/
module_init(vser_init);
module_exit(vser_exit);
/****** 模块说明 ********/
MODULE_LICENSE("GPL");  /*开元许可协议:GPL协议*/

Makefile

#动态编译内核驱动生成.ko文件的Makeifle#自己的模块代码名
obj-m = vser.o	#就会生成一个 vser.ko 文件#内核源代码路径
ifeq ($(ARCH),arm)KERNELDIR ?= /home/student/linux-5.4.31
elseKERNELDIR ?= /lib/modules/${shell uname -r}/build
endif#当前模块路径
PWD ?= $(shell pwd)#编译源码生成 .ko 文件 make all
all:${MAKE} -C ${KERNELDIR} M=${PWD} modules
#伪代码之清除垃圾
clean:rm Module.* modules.* *.mod *.ko


1.  命令:  make    (编译形成vser .ko 文件)


2.  命令 :  mknod  /dev/vser0  c  256 0        (创建一个字符设备)

解释
mknod  == make node  //创建一个节点
/dev/vser0  设备名称
c   字符设备
256  主设备号
0    次设备号

 


3. 命令  :  ls -l  /dev/vser0(设备查看存在)


4.命令:  sudo rmmod vser  (卸载模块   防止之前的模块没有卸载)

 


5. 命令 :  sudo  insmod vser.ko   (加载  模块)


 6. 命令 :  lsmod   ( 查看模块是否加载)

  


7. 命令:  dmesg  (查看  内核模块的输出   比如说  printk)(这个没有内核信息打印)


 8. 过程多差不多!!!!    (下面的 我将不写过程了,,除了一个驱动 多个设备)


2. 字符设备注册

代码: 

vser.c

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>#include <linux/fs.h>   
#include <linux/cdev.h>
/***** 设备相关信息 ******/
static unsigned int VSER_MAJOR = 256;      //主设备号
static unsigned int VSER_MINOR = 0;        //次设备号
#define VSER_DEV_CNT   1        //个数为1个
#define VSER_DEV_NAME  "vser"   //设备名称/**** 字符设备结构体 ******/
static struct cdev vser_cdev;   //字符设备结构体/**** 设备操作相关函数 到时候就可以使用 open 打开该设备文件 *****/
int vser_open(struct inode *p_inode, struct file *p_file)
{printk("vser设备打开了\n");return 0;
}/**** 设备操作集 *****/
static struct file_operations vser_ops = {  //设备操作集结构体.owner = THIS_MODULE,.open = vser_open,
};/***** 字符设备注册步骤 ****************************************************        初始化函数* 1.将主设备和次设备组合成设备号     MKDEV(主设备号,次设备号)* 2.将该设备号注册到内核           register_chrdev_region(设备号,设备个数,设备名称) 或 * 3.初始化字符设备                cdev_init(字符设备结构体地址,操作集结构体地址)* 4.将字符设备添加到内核散列表map中  cdev_add(字符设备结构体地址,设备号,设备个数)*        注销函数* 1.从内核散链表map中删除字符设备    cdev_del(字符设备结构体地址)* 2.将申请的设备号注销              unregister_chrdev_region(设备号,设备个数) * * 跟文件系统下创建 设备文件 cat /proc/devices  * 查看到设备信息  Character devices:1 mem256 vser   我们自己申请的设备信息4 /dev/vc/04 tty4 ttyS5 /dev/tty5 /dev/console  * ***********************************************************************/static int __init vser_init(void)
{int ret;    //用于获取函数返回值,并该函数是否执行成功/*********  申请设备号 **************/dev_t dev;  //用于存储设备号dev = MKDEV(VSER_MAJOR,VSER_MINOR); //组合成设备号 (VSER_MAJOR << 20) | VSER_MINORret = register_chrdev_region(dev,VSER_DEV_CNT,VSER_DEV_NAME);if(ret != 0){printk("静态申请设备号失败\n");ret = alloc_chrdev_region(&dev,VSER_MINOR,VSER_DEV_CNT,VSER_DEV_NAME);if(ret != 0){printk("动态申请设备号失败\n");goto register_err;}VSER_MAJOR = MAJOR(dev);    //从设备号中提取主设备号VSER_MINOR = MINOR(dev);    //从设备号中提取次设备号}/******** 申请字符设备 *********/cdev_init(&vser_cdev,&vser_ops);vser_cdev.owner = THIS_MODULE;ret = cdev_add(&vser_cdev,dev,VSER_DEV_CNT);if(ret != 0){printk("申请字符设备失败\n");goto cdev_err;}return 0;   //表示成功,直接结束函数并返回0cdev_err:       /* 申请字符设备失败 */unregister_chrdev_region(dev,VSER_DEV_CNT); //释放设备号register_err:   /* 设备号申请失败 */return ret;}static void __exit vser_exit(void)
{dev_t dev;  //用于存储设备号dev = MKDEV(VSER_MAJOR,VSER_MINOR); //组合成设备号 (VSER_MAJOR << 20) | VSER_MINORcdev_del(&vser_cdev);                       //1.从内核散列表中删除字符设备unregister_chrdev_region(dev,VSER_DEV_CNT); //2.释放设备号
}/****** 加入到内核 *******/
module_init(vser_init);
module_exit(vser_exit);
/****** 模块说明 ********/
MODULE_LICENSE("GPL");  /*开元许可协议:GPL协议*/

main.c     (记得使用  gcc   编译   )

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{if(argc != 2){return -1;}int fd = open(argv[1],O_RDWR); //会调用内核的 struct file_operations 结构体中的 open 指针,指针指向 vser_open 函数if(fd < 0){perror("打开设备失败:");return -2;}close(fd);return 0;
}

Makefile 

#动态编译内核驱动生成.ko文件的Makeifle#自己的模块代码名
obj-m = vser.o	#就会生成一个 vser.ko 文件#内核源代码路径
ifeq ($(ARCH),arm)KERNELDIR ?= /home/student/linux-5.4.31
elseKERNELDIR ?= /lib/modules/${shell uname -r}/build
endif#当前模块路径
PWD ?= $(shell pwd)#编译源码生成 .ko 文件 make all
all:${MAKE} -C ${KERNELDIR} M=${PWD} modules
#伪代码之清除垃圾
clean:rm Module.* modules.* *.mod *.ko


1. 过程:  gcc  mian.c    (把main.c  编译成  a.out)

 


2.  sudo  rmmod  vser  (卸载之前加载的模块)


3. make  (编译  makefile  文件)


4. sudo insmod vser.ko   (加载 模块)


5.命令: lsmod  (查看是否加载)

 



6. dmesg  (查看内核打印的信息, 这个没有内核信息打印)


3.虚拟串口 

代码: 

main_read.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc,char *argv[])
{if(argc != 2){return -1;}int fd = open(argv[1],O_RDWR); //会调用内核的 struct file_operations 结构体中的 open 指针,指针指向 vser_open 函数if(fd < 0){perror("打开设备失败:");return -2;}char buf[10];while(1){memset(buf,0,sizeof(buf));read(fd,buf,10);printf("读取到:%s\n",buf);sleep(1);}close(fd);return 0;
}

 main_write.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc,char *argv[])
{if(argc != 2){return -1;}int fd = open(argv[1],O_RDWR); //会调用内核的 struct file_operations 结构体中的 open 指针,指针指向 vser_open 函数if(fd < 0){perror("打开设备失败:");return -2;}char buf[10];while(1){printf("请输入要写入的数据:");scanf("%s",buf);write(fd,buf,strlen(buf));}close(fd);return 0;
}

vser.c

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>#include <linux/fs.h>   
#include <linux/cdev.h>
/***** 设备相关信息 ******/
static unsigned int VSER_MAJOR = 256;      //主设备号
static unsigned int VSER_MINOR = 0;        //次设备号
#define VSER_DEV_CNT   1        //个数为1个
#define VSER_DEV_NAME  "vser"   //设备名称/**** 字符设备结构体 ******/
static struct cdev vser_cdev;   //字符设备结构体/**** 虚拟串口 ******/
#include <linux/kfifo.h>
DEFINE_KFIFO(vser_fifo,char,32);    //创建一个名为 vser_fifo 的 char 类型 大小为 32 个的队列管道/**** 设备操作相关函数 到时候就可以使用 open 打开该设备文件 *****/
int vser_open(struct inode *p_inode, struct file *p_file)
{printk("vser设备打开了\n");return 0;
}int vser_close(struct inode *p_inode, struct file *p_file)
{printk("vser设备关闭了\n");return 0;
}ssize_t vser_read(struct file *p_file, char __user *user_buf, size_t user_size, loff_t *ps)
{   int copied = 0;kfifo_to_user(&vser_fifo,user_buf,user_size,&copied); //将内核队列管道数据拷贝到用户空间return copied;
}ssize_t vser_write(struct file *p_file, const char __user *user_buf, size_t user_size, loff_t *ps)
{int copied = 0;kfifo_from_user(&vser_fifo,user_buf,user_size,&copied);//将用户空间数据拷贝到内核队列管道中return copied;
}/**** 设备操作集 *****/
static struct file_operations vser_ops = {  //设备操作集结构体.owner = THIS_MODULE,.release = vser_close,.open = vser_open,.read = vser_read,.write = vser_write,
};/***** 字符设备注册步骤 ****************************************************        初始化函数* 1.将主设备和次设备组合成设备号     MKDEV(主设备号,次设备号)* 2.将该设备号注册到内核           register_chrdev_region(设备号,设备个数,设备名称) 或 * 3.初始化字符设备                cdev_init(字符设备结构体地址,操作集结构体地址)* 4.将字符设备添加到内核散列表map中  cdev_add(字符设备结构体地址,设备号,设备个数)*        注销函数* 1.从内核散链表map中删除字符设备    cdev_del(字符设备结构体地址)* 2.将申请的设备号注销              unregister_chrdev_region(设备号,设备个数) * * 跟文件系统下创建 设备文件 cat /proc/devices  * 查看到设备信息  Character devices:1 mem256 vser   我们自己申请的设备信息4 /dev/vc/04 tty4 ttyS5 /dev/tty5 /dev/console  * ***********************************************************************/static int __init vser_init(void)
{int ret;    //用于获取函数返回值,并该函数是否执行成功/*********  申请设备号 **************/dev_t dev;  //用于存储设备号dev = MKDEV(VSER_MAJOR,VSER_MINOR); //组合成设备号 (VSER_MAJOR << 20) | VSER_MINORret = register_chrdev_region(dev,VSER_DEV_CNT,VSER_DEV_NAME);if(ret != 0){printk("静态申请设备号失败\n");ret = alloc_chrdev_region(&dev,VSER_MINOR,VSER_DEV_CNT,VSER_DEV_NAME);if(ret != 0){printk("动态申请设备号失败\n");goto register_err;}VSER_MAJOR = MAJOR(dev);    //从设备号中提取主设备号VSER_MINOR = MINOR(dev);    //从设备号中提取次设备号}/******** 申请字符设备 *********/cdev_init(&vser_cdev,&vser_ops);vser_cdev.owner = THIS_MODULE;ret = cdev_add(&vser_cdev,dev,VSER_DEV_CNT);if(ret != 0){printk("申请字符设备失败\n");goto cdev_err;}return 0;   //表示成功,直接结束函数并返回0cdev_err:       /* 申请字符设备失败 */unregister_chrdev_region(dev,VSER_DEV_CNT); //释放设备号register_err:   /* 设备号申请失败 */return ret;}static void __exit vser_exit(void)
{dev_t dev;  //用于存储设备号dev = MKDEV(VSER_MAJOR,VSER_MINOR); //组合成设备号 (VSER_MAJOR << 20) | VSER_MINORcdev_del(&vser_cdev);                       //1.从内核散列表中删除字符设备unregister_chrdev_region(dev,VSER_DEV_CNT); //2.释放设备号
}/****** 加入到内核 *******/
module_init(vser_init);
module_exit(vser_exit);
/****** 模块说明 ********/
MODULE_LICENSE("GPL");  /*开元许可协议:GPL协议*/

Makefile

#动态编译内核驱动生成.ko文件的Makeifle#自己的模块代码名
obj-m = vser.o	#就会生成一个 vser.ko 文件#内核源代码路径
ifeq ($(ARCH),arm)KERNELDIR ?= /home/student/linux-5.4.31
elseKERNELDIR ?= /lib/modules/${shell uname -r}/build
endif#当前模块路径
PWD ?= $(shell pwd)#编译源码生成 .ko 文件 make all
all:${MAKE} -C ${KERNELDIR} M=${PWD} modules
#伪代码之清除垃圾
clean:rm Module.* modules.* *.mod *.ko


1. 编译所有的  .c 文件

gcc main_write.c -o write    (写文件)

gcc main_read.c -o  read    (读文件)

make    (编译  vser.ko 文件)


2. 命令: sudo rmmod  vser   (卸载之前加载的模块)


3. 命令:  sudo vser.ko     (加载现在的模块)


4. 命令: lsmod   (查看模块是否加载)


5. 命令:  sudo ./read   /dev/vser0    (运行读文件     后面的/dev/vser0   是之前创建的字符设备)

              sudo ./write  /dev/vser0     (运行写文件    后面的/dev/vser0   是之前创建的字符设备)


6. 命令 : dmesg  (查看内核信息)




4. 一个驱动支持多个设备

代码: 

vser.c

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>#include <linux/fs.h>   
#include <linux/cdev.h>
/***** 设备相关信息 ******/
static unsigned int VSER_MAJOR = 256;      //主设备号
static unsigned int VSER_MINOR = 0;        //次设备号
#define VSER_DEV_CNT   2        //个数为1个
#define VSER_DEV_NAME  "vser"   //设备名称// mknod /dev/vser0 c 256 0 
// mknod /dev/vser1 c 256 1/**** 字符设备结构体 ******/
static struct cdev vser_cdev;   //字符设备结构体/**** 虚拟串口 ******/
#include <linux/kfifo.h>
DEFINE_KFIFO(vser_fifo0,char,32);    //创建一个名为 vser_fifo0 的 char 类型 大小为 32 个的队列管道
DEFINE_KFIFO(vser_fifo1,char,32);    //创建一个名为 vser_fifo1 的 char 类型 大小为 32 个的队列管道
/**** 设备操作相关函数 到时候就可以使用 open 打开该设备文件 *****/
int vser_open(struct inode *p_inode, struct file *p_file)
{int minor = MINOR(p_inode->i_rdev); //获取设备的次设备号printk("vser设备打开了\n");printk("当前的次设备号;%d\n",minor);switch (minor){case 0:p_file->private_data = &vser_fifo0;break;case 1:p_file->private_data = &vser_fifo1;break;}printk("操作的FIFO = %p\n",p_file->private_data);return 0;
}int vser_close(struct inode *p_inode, struct file *p_file)
{printk("vser设备关闭了\n");return 0;
}ssize_t vser_read(struct file *p_file, char __user *user_buf, size_t user_size, loff_t *ps)
{   int copied = 0;struct kfifo *fifo = p_file->private_data;kfifo_to_user(fifo,user_buf,user_size,&copied); //将内核队列管道数据拷贝到用户空间return copied;
}ssize_t vser_write(struct file *p_file, const char __user *user_buf, size_t user_size, loff_t *ps)
{int copied = 0;struct kfifo *fifo = p_file->private_data;printk("操作的FIFO = %p\n",p_file->private_data);kfifo_from_user(fifo,user_buf,user_size,&copied);//将用户空间数据拷贝到内核队列管道中return copied;
}/**** 设备操作集 *****/
static struct file_operations vser_ops = {  //设备操作集结构体.owner = THIS_MODULE,.release = vser_close,.open = vser_open,.read = vser_read,.write = vser_write,
};/***** 字符设备注册步骤 ****************************************************        初始化函数* 1.将主设备和次设备组合成设备号     MKDEV(主设备号,次设备号)* 2.将该设备号注册到内核           register_chrdev_region(设备号,设备个数,设备名称) 或 * 3.初始化字符设备                cdev_init(字符设备结构体地址,操作集结构体地址)* 4.将字符设备添加到内核散列表map中  cdev_add(字符设备结构体地址,设备号,设备个数)*        注销函数* 1.从内核散链表map中删除字符设备    cdev_del(字符设备结构体地址)* 2.将申请的设备号注销              unregister_chrdev_region(设备号,设备个数) * * 跟文件系统下创建 设备文件 cat /proc/devices  * 查看到设备信息  Character devices:1 mem256 vser   我们自己申请的设备信息4 /dev/vc/04 tty4 ttyS5 /dev/tty5 /dev/console  * ***********************************************************************/static int __init vser_init(void)
{int ret;    //用于获取函数返回值,并该函数是否执行成功/*********  申请设备号 **************/dev_t dev;  //用于存储设备号dev = MKDEV(VSER_MAJOR,VSER_MINOR); //组合成设备号 (VSER_MAJOR << 20) | VSER_MINORret = register_chrdev_region(dev,VSER_DEV_CNT,VSER_DEV_NAME);if(ret != 0){printk("静态申请设备号失败\n");ret = alloc_chrdev_region(&dev,VSER_MINOR,VSER_DEV_CNT,VSER_DEV_NAME);if(ret != 0){printk("动态申请设备号失败\n");goto register_err;}VSER_MAJOR = MAJOR(dev);    //从设备号中提取主设备号VSER_MINOR = MINOR(dev);    //从设备号中提取次设备号}/******** 申请字符设备 *********/cdev_init(&vser_cdev,&vser_ops);vser_cdev.owner = THIS_MODULE;ret = cdev_add(&vser_cdev,dev,VSER_DEV_CNT);if(ret != 0){printk("申请字符设备失败\n");goto cdev_err;}return 0;   //表示成功,直接结束函数并返回0cdev_err:       /* 申请字符设备失败 */unregister_chrdev_region(dev,VSER_DEV_CNT); //释放设备号register_err:   /* 设备号申请失败 */return ret;}static void __exit vser_exit(void)
{dev_t dev;  //用于存储设备号dev = MKDEV(VSER_MAJOR,VSER_MINOR); //组合成设备号 (VSER_MAJOR << 20) | VSER_MINORcdev_del(&vser_cdev);                       //1.从内核散列表中删除字符设备unregister_chrdev_region(dev,VSER_DEV_CNT); //2.释放设备号
}/****** 加入到内核 *******/
module_init(vser_init);
module_exit(vser_exit);
/****** 模块说明 ********/
MODULE_LICENSE("GPL");  /*开元许可协议:GPL协议*/

main_write.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc,char *argv[])
{if(argc != 2){return -1;}int fd = open(argv[1],O_RDWR); //会调用内核的 struct file_operations 结构体中的 open 指针,指针指向 vser_open 函数if(fd < 0){perror("打开设备失败:");return -2;}char buf[10];while(1){printf("请输入要写入的数据:");scanf("%s",buf);write(fd,buf,strlen(buf));}close(fd);return 0;
}

main_read.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc,char *argv[])
{if(argc != 2){return -1;}int fd = open(argv[1],O_RDWR); //会调用内核的 struct file_operations 结构体中的 open 指针,指针指向 vser_open 函数if(fd < 0){perror("打开设备失败:");return -2;}char buf[10];while(1){memset(buf,0,sizeof(buf));read(fd,buf,10);printf("读取到:%s\n",buf);sleep(1);}close(fd);return 0;
}

Makefile

#动态编译内核驱动生成.ko文件的Makeifle#自己的模块代码名
obj-m = vser.o	#就会生成一个 vser.ko 文件#内核源代码路径
ifeq ($(ARCH),arm)KERNELDIR ?= /home/student/linux-5.4.31
elseKERNELDIR ?= /lib/modules/${shell uname -r}/build
endif#当前模块路径
PWD ?= $(shell pwd)#编译源码生成 .ko 文件 make all
all:${MAKE} -C ${KERNELDIR} M=${PWD} modules
#伪代码之清除垃圾
clean:rm Module.* modules.* *.mod *.ko


1. 命令: sudo /dev/vser*     (卸载之前创建的字符设备模块  )


2.  sudo mknod  /dev/vser1  c  256 0   ( 创建字符设备)

    sudo  mknod  /dev/vser2  c  256 1    (设备名称不同,  次设备号不同 ,  自己看 vser.c  里面的  switch   的数字 决定次设备号)


3.命令:  su   (进入超级用户)

 


4. 命令:sudo rmmod  vser


5.命令 : make


6. 命令: sudo insmod  vser.ko 


7. 命令: gcc main_read.c  -o read


8. 命令:  gcc main_write.c  -o  write


9. 命令:  echo "我是" >  /dev/vser1


10 . 命令: cat /dev/vser1

 

 



作业: 

 

 1.字符设备和块设备的区别不包括()
[A]字符设备按字节流进行访问,块设备按块大小进行访问

[B]字符设备只能处理可打印字符,块设备可以处理二进制数据

[C]多数字符设备不能随机访问,而块设备一定能随机访问

[D]字符设备通常没有页高速缓存,而块设备有


2.在3.14.25版本的内核中,主设备号占———位,次设备号占———位。

[A]8
[B] 16
[C] 12
[D]20


3.用于分配主次设备号的函数是()。
[A] register_chrdev_region
[B] MKDEV
[C]alloc_chrdev_region
[D] MAJOR


4、在字符设备驱动中,struct file_operations 结构中的函数指针成员不包含$,

[A] open
[B] close
[C] read
[D] show_fdinfo

 


http://www.ppmy.cn/news/11126.html

相关文章

Redis哨兵模式搭建

以下配置机器部署ip为 a、b、c&#xff0c;其中a为master节点 需提前创建 /app/user/oms/redis/data 目录 1.1上传 redis-5.0.5.zip 到对应目录&#xff0c;解压 unzip redis-5.0.5.zip # 生成 redis-5.0.5 目录 1.2 修改配置文件 maxclients 10000 #20000 &#xff0…

MATLAB-样条插值运算

MATLAB中不仅提供了一维插值、二维插值和三维插值方法&#xff0c;还提供了样条插值的方法。其主要思想是:假定有一组已知的数据点&#xff0c;希望找到该组数据的拟合多项式。在多项式的拟合过程中&#xff0c;对于每组相邻的样本数据点&#xff0c;存在一条曲线&#xff0c;该…

jvm运行过程

VM是Java程序运行的环境,同时是一个操作系统的一个应用程序进程,因此它有自己的生命周期,也有自己的代码和数据空间. JVM体系主要是两个JVM的内部体系结构分为三个子系统和两大组件&#xff0c;分别是&#xff1a;类装载器&#xff08;ClassLoader&#xff09;子系统、执行引擎…

仿抖音视频双指缩放和单指滑动效果

最近刷抖音看视频时&#xff0c;对一个视频某个位置比较感兴趣&#xff0c;采用双指放大查看细节&#xff0c;然后还可以随意滑动到任何位置&#xff0c;比较感兴趣&#xff0c;决定自己来实现此效果&#xff1b;分析效果&#xff1a;ViewPager左右滑动&#xff0c;视频列表上下…

FFmpeg功能命令汇总

前言 如此强大的FFmpeg&#xff0c;能够实现视频采集、视频格式转化、视频截图、视频添加水印、视频切片、视频录制、视频推流、更改音视频参数功能等。通过终端命令如何实现这些功能&#xff0c;Richy在本文做一记录&#xff0c;以备之后查阅。 注意&#xff1a;下面一一列举…

Linux常用命令——nethogs命令

在线Linux命令查询工具(http://www.lzltool.com/LinuxCommand) nethogs 终端下的网络流量监控工具 补充说明 有很多适用于Linux系统的开源网络监视工具。比如说&#xff0c;你可以用命令iftop来检查带宽使用情况。netstat用来查看接口统计报告&#xff0c;还有top监控系统当…

【自学Python】Python input()函数

Python input()函数 Python input()函数教程 在 Python 中&#xff0c;input() 函数用于获取用于的输入&#xff0c;并给出提示。input() 函数&#xff0c;总是返回 string 类型&#xff0c;因此&#xff0c;我们可以使用 input() 函数&#xff0c;获取用户输入的任何数据类型…

Python3,我把新年祝福写在“雨“中,你看,雨一直下,气氛还算融洽,在同个屋檐下....

新年愿望写在"雨"中1、引言2、代码实战2.1 模块介绍2.1.1 Pygame 介绍2.1.2 Pygame的display介绍2.1.3 Pygame的event介绍2.1.4 Pygame的font介绍2.2 代码示例3、总结1、引言 小屌丝&#xff1a;鱼哥&#xff0c;2023年了&#xff0c; 你有啥愿望啊&#xff1f; 小鱼…