当前位置: 首页 > news >正文

MX6ULL学习笔记 (八) platform 设备驱动实验

前言:

什么是 Linux 下的 platform 设备驱动

Linux下的字符设备驱动一般都比较简单,只是对IO进行简单的读写操作。但是I2C、SPI、LCD、USB等外设的驱动就比较复杂了,需要考虑到驱动的可重用性,以避免内核中存在大量重复代码,为此人们提出了驱动的分离与分层的思路,演化并诞生出了platform设备驱动。

一、驱动的分层分离

1. 驱动的分离

以I2C接口的三轴加速度传感器为例,传统的设备驱动如下图示:每个平台都有一个ADXL345的驱动,因此设备驱动要重复编写三次。

各平台的主机驱动是不同的,但是ADXL345是一样的,因此上图可以精简为一个ADXL345驱动和统一的接口API。

 实际上,除了ADXL345还有AT24C02、MPU6050等I2C设备,因此实际的驱动框架图如下示

驱动的分离即将主机驱动和设备驱动分隔开来,实际开发中,主机驱动一般由半导体厂家提供,设备驱动也会由器件厂家写好,我们只需要提供设备信息即可。也就是将设备信息从设备驱动中剥离开来,设备驱动使用标准方法获取到设备信息,然后根据获取到的设备信息来初始化设备

因此驱动只负责驱动,设备只负责设备,想办法将两者进行匹配即可。这就是Linux中的总线-驱动-设备模型,也就是常说的驱动分离

如上图示,当向系统注册一个驱动时,总线会在右侧的设备中查找,看看有没有与之匹配的设备,有的话就将两者联系起来;当向系统中注册一个设备时,总线会在左侧的驱动中查找,看有没有与之匹配的驱动,有的话也联系起来。

2. 驱动的分层

Linux下的驱动也是分层的,分层的目的是为了在不同的层处理不同的内容。以input输入子系统为例,input子系统负责管理所有跟输入有关的驱动,包括键盘、鼠标、触摸等。

  • 驱动层:获取输入设备的原始值,获取到的输入事件上报给核心层
  • 核心层:处理各种IO模型,并且提供file_operations操作集合
  • 事件层:和用户空间进行交互

3. platform平台驱动模型

根据总线-驱动-设备驱动模型,IIC、SPI、USB这样实实在在的总线是完全匹配的,但是要有一些外设是没法归结为具体的总线:比如定时器、RTC、LCD等。为此linux内核创造了一个虚拟的总线:platform总线,以及platform驱动、platform设备模型。

platform总线Linux内核使用bus_type结构体表示总线。

其定义在文件include/linux/device.h中

platform驱动platform驱动由platform_driver结构体表示。

此结构体定义在文件include/linux/platform_device.h中。

        编写platform驱动时,先要定义一个platform_driver结构体变量,然后实现结构体中的各个成员变量,重点是实现匹配方法以及probe函数。当驱动和设备匹配成功以后probe函数就会执行,具体的驱动程序在probe函数里面编写。之后通过以下函数向内核注册platform驱动或卸载platform驱动

③platform设备:platform_device结构体用来表示platform设备。

        注意若内核支持设备树的话,就无需使用该结构体来描述设备,而改用设备树了。该结构体定义在文件include/linux/platform_device.h中,在不支持设备树的Linux版本中,用户需要编写platform_device变量来描述设备信息,然后使用以下函数将设备信息注册到Linux内核中或从内核中注销掉,这里我们使用的linux是新版本的了,也就直接使用设备树就好了。

 


二、platform框架分析

1.platform总线注册

  和字符型驱动一样,我们要使用platform总线之前,也需要告诉一下内核,也就是注册。使用platform_bus_init函数去进行注册,既然要注册,那么我们也得告诉内核我们的一些信息。

注册的内容就是:

struct bus_type platform_bus_type = {.name           = "platform",.dev_groups   = platform_dev_groups,.match           = platform_match,.uevent          = platform_uevent,.pm        = &platform_dev_pm_ops,}

对于platform平台而言,platform_match函数就是月老,负责驱动和设备的匹配。

2.platform驱动

在注册platform驱动之前要定义一个结构体,为platform_driver,结构体内容为:

struct platform_driver {int (*probe)(struct platform_device *);int (*remove)(struct platform_device *);void (*shutdown)(struct platform_device *);int (*suspend)(struct platform_device *, pm_message_t state);int (*resume)(struct platform_device *);struct device_driver driver;//-> const struct of_device_id    *of_match_table;//-> const char             *name;const struct platform_device_id *id_table;bool prevent_deferred_probe;};

 然后使用platform_driver_register函数向内核注册platform驱动。向内核注册platform驱动的时候,如果驱动和设备匹配成功,最终会执行platform_driver的probe函数。

3.platform设备

1、无设备树的时候,此时需要驱动开发人员编写设备注册文件,使用platform_device_register函数注册设备。使用platform_device_register函数注册设备也同样需要告诉内核一些注册信息。也就是需要定义个结构体:

结构体platform_device:

struct platform_device {const char	*name;int		id;bool		id_auto;struct device	dev;u32		num_resources;struct resource	*resource;const struct platform_device_id	*id_entry;char *driver_override; /* Driver name to force a match *//* MFD cell pointer */struct mfd_cell *mfd_cell;/* arch specific additions */struct pdev_archdata	archdata;
};

 

2,有设备树,修改设备树的设备节点即可。使用兼容性列表,当设备与platform的驱动匹配以后,就会执行platform_driver->probe函数。


 三、编写 platform 驱动流程

接下来我们就来学习一下如何在设备树下编写 platform 驱动流程:

1.在设备树中创建设备节点

由于我们使用的linux是支持设备树的,那么毫无疑问,我们肯定要先在设备树中创建设备节点来描述设备信息,重点是要设置好 compatible 属性的值,因为 platform 总线需要通过设备节点的 compatible 属性值来匹配驱动!这点要切记。

2、编写platfrom的驱动兼容表

1)建立of_device_id 表,也就是驱动的兼容表:

static const struct of_device_id leds_of_match[] = {{ .compatible = "atkalpha-gpioled" },     /* 兼容属性 */{ /* Sentinel */ }};

3、建立platform_driver结构体

static struct platform_driver leds_platform_driver = {.driver = {.name = "imx6ul-led",.of_match_table = leds_of_match,},.probe = leds_probe,.remove = leds_remove,};

1)设置 platform_driver 中的 of_match_table 匹配表为上面创建的 leds_of_match

2)向总线注册驱动的时候,会检查当前总线下的所有设备,有没有与此驱动匹配的设备,如果有的话就执行驱动里面的probe函数。

3)卸载驱动的时候,会执行驱动里面的remove函数。

4、编写probe函数:

函数原型:

static int led_probe(struct platform_device *dev);

5、编写remove函数:

函数原型

static int led_remove(struct platform_device *dev);


四、实验程序编写

1.在设备树中创建设备节点

2.引入字符设备框架

这里直接引入我们之前写过的字符设备框架,在这份驱动的基础上来进行修改,如果没有看过之前那篇也没关系,下面也会贴出完整代码。

3.编写platfrom的驱动兼容表

/* 匹配列表 */
static const struct of_device_id led_of_match[] = {{ .compatible = "led-gpio" },{ /* Sentinel */ }
};

4.建立platform_driver结构体

/*platform_driver结构体*/
static struct platform_driver led_driver = {.driver = {.name = "imx6ul-led",.of_match_table = led_of_match,},.probe  =  led_probe,.remove =  led_remove,
};

5.编写probe函数:

当设备和驱动兼容表匹配上的时候就会运行peobe函数:

/*当谁列表的设备和驱动匹配上后执行的peobe函数*/
static int led_probe(struct platform_device *dev)
{/* 动态注册字符设备的流程一般如下:1.调用 alloc_chrdev_region() 函数申请设备编号。2.使用 cdev_init() 函数初始化设备描述结构体。3.使用 cdev_add() 函数将设备号与设备描述结构体关联起来,注册字符设备驱动。4.使用 class_create() 函数创建一个设备类.5.使用 device_create() 函数创建一个设备*/int ret = 0;/*进入这个函数就表明匹配成功了*/printk("led driver and device was matched!\r\n");/*1 创建设备号根据是否定义了设备号,通过条件判断选择不同的创建方式。如果定义了设备号,则使用MKDEV宏将主设备号和次设备号合成为设备号,并调用register_chrdev_region()函数注册字符设备号。如果没有定义设备号,则使用alloc_chrdev_region()函数动态分配设备号,并通过MAJOR和MINOR宏获取分配得到的主设备号和次设备号。*/if(gpioled.major){gpioled.devid = MKDEV(gpioled.major,0);register_chrdev_region(gpioled.devid,DEVICE_CNT,DEVICE_NAME);}else{alloc_chrdev_region(&gpioled.devid,0,DEVICE_CNT,DEVICE_NAME);gpioled.major = MAJOR(gpioled.devid);gpioled.minor = MINOR(gpioled.devid);}/* 2 初始化cdev设置cdev结构体的拥有者为当前模块(THIS_MODULE),然后使用 cdev_init() 函数初始化cdev结构体。参数包括待初始化的cdev结构体和用于操作该设备的file_operations结构体(hello_drv) */gpioled.cdev.owner= THIS_MODULE;cdev_init(&gpioled.cdev,&gpioled_fops);/* 3、添加一个cdev */cdev_add(&gpioled.cdev,gpioled.devid,DEVICE_CNT);/*4 创建设备类使用 class_create() 函数创建一个设备类,设备类用于在/sys/class目录下创建子目录,以组织同一类设备的相关信息。该函数的参数包括所属的模块(THIS_MODULE)和设备类的名称(DEVICE_NAME)。如果创建失败,IS_ERR() 函数将返回true,表示出错,此时使用 PTR_ERR() 函数返回错误码。 */gpioled.class = class_create(THIS_MODULE,DEVICE_NAME);if(IS_ERR(gpioled.class)){printk("newchr fail!\r\n");return PTR_ERR(gpioled.class);}/*5 创建设备使用 device_create() 函数创建一个设备,并在/dev目录下创建相应的设备节点。参数包括设备所属的类(newchr.class)、父设备(NULL,如果没有父设备)、设备号(newchr.devid)、设备私有数据(NULL,一般为设备驱动程序提供一个指针)和设备名称(DEVICE_NAME)。如果创建失败,IS_ERR() 函数将返回true,表示出错,此时使用 PTR_ERR() 函数返回错误码。 */gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, DEVICE_NAME);if(IS_ERR(gpioled.device)){printk("newchr fail!\r\n");return PTR_ERR(gpioled.device);}ret = myled_init(&gpioled);   //初始化ledgpioreturn 0;
}

6.编写remove函数:

static int led_remove(struct platform_device *dev)
{gpio_set_value(gpioled.led_gpio,1);gpio_free(gpioled.led_gpio);printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);/*在模块卸载时,使用 cdev_del() 函数注销字符设备驱动,并使用 unregister_chrdev_region() 函数释放设备号资源。*//* 注销字符设备驱动 */cdev_del(&gpioled.cdev);/*  删除cdev */unregister_chrdev_region(gpioled.devid, DEVICE_CNT); /* 注销设备号 */device_destroy(gpioled.class, gpioled.devid);// 销毁设备,删除相应的设备节点class_destroy(gpioled.class);// 销毁设备类,释放相关资源printk("gpioled exit!\r\n");return 0;
}

完整代码:

/**************头文件区域*********************************************************/
#include <linux/ide.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/errno.h>#include <linux/of.h> 
#include <linux/of_address.h> 
#include <linux/of_gpio.h> 
#include <linux/irq.h>
#include <linux/poll.h>
#include <linux/platform_device.h>
#include <linux/fcntl.h>#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <linux/io.h>
/**********************************************************************************//************************函数定义-begin***********************************************/
static int gpioled_release(struct inode *inode, struct file *file);
static ssize_t gpioled_read(struct file *file, char __user *buf, size_t size, loff_t *ptr);
static ssize_t gpioled_write(struct file *file, const char __user *buf, size_t size, loff_t *ptr);
static int gpioled_open(struct inode *inode , struct file *file);
static int led_probe(struct platform_device *dev);
static int led_remove(struct platform_device *dev);
/************************函数定义-end********************************************//************************宏定义-begin***********************************************/
#define DEVICE_NAME "dtsplatled"
#define DEVICE_CNT  1
#define LED_ON     1
#define LED_OFF    0
/************************宏定义-end********************************************//************************结构体定义-begin***********************************************/
/* dtsled设备信息结构体 */
struct dtsled_dev
{dev_t devid;			/* 设备号 	 */struct cdev cdev;		/* cdev 	*/struct class *class;	/* 类 		*/struct device *device;	/* 设备 	 */int major;				/* 主设备号	  */int minor;				/* 次设备号   */struct device_node *nd; /* 设备节点 */int led_gpio; /* led 所使用的 GPIO 编号 */
};
struct dtsled_dev gpioled; /* led设备 *//* 设备操作函数结构体 */
static const struct file_operations gpioled_fops = {.owner		= THIS_MODULE,.open		= gpioled_open,.read		= gpioled_read,.write      = gpioled_write,.release    = gpioled_release
};/* 匹配列表 */
static const struct of_device_id led_of_match[] = {{ .compatible = "led-gpio" },{ /* Sentinel */ }
};static struct platform_driver led_driver = {.driver = {.name = "imx6ul-led",.of_match_table = led_of_match,},.probe  =  led_probe,.remove =  led_remove,
};
/************************结构体定义-end***********************************************//************************file_operations操作函数-begin***********************************************/
static int gpioled_release(struct inode *inode, struct file *file)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}
static ssize_t gpioled_read(struct file *file, char __user *buf, size_t size, loff_t *ptr)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}
static ssize_t gpioled_write(struct file *file, const char __user *buf, size_t size, loff_t *ptr)
{int ret;unsigned char databuf[1];unsigned char ledstate;struct dtsled_dev *dev = file->private_data;ret = __copy_from_user(databuf,buf,size);if(ret < 0){return -EFAULT;}ledstate = databuf[0];if(ledstate == LED_OFF){   gpio_set_value(dev->led_gpio,1);}else if(ledstate == LED_ON){gpio_set_value(dev->led_gpio,0);}return 0;
}
static int gpioled_open(struct inode *inode , struct file *file)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);file->private_data = &gpioled; /* 设置私有数据 */ return 0;
}
/************************file_operations操作函数-end***********************************************//*****************led初始化函数************************/
static int myled_init(struct dtsled_dev *dev)
{int ret = 0;/* 1、设置 LED 所使用的 GPIO */ dev->nd = of_find_node_by_path("/gpioled");if(dev->nd == NULL){printk("gpioled node cant not found!\r\n");return -EINVAL;}else{printk("gpioled node hase been found!\r\n");}/* 2、 获取设备树中的 gpio 属性,得到 LED 所使用的 LED 编号 */ dev->led_gpio =  of_get_named_gpio(dev->nd,"gpios",0);if(dev->led_gpio < 0){printk("can't get led-gpio\r\n");return -EINVAL;}printk("led-gpio num = %d\r\n", dev->led_gpio); /* 3、设置 GPIO1_IO03 为输出,并且输出高电平,默认关闭 LED 灯 */ ret = gpio_request(dev->led_gpio,"led0");if(ret < 0){printk("led-gpio request fail\r\n"); return -EINVAL;}gpio_direction_output(dev->led_gpio,1);return ret;
}/************************platfrom操作函数-begin***********************************************/
/*当谁列表的设备和驱动匹配上后执行的peobe函数*/
static int led_probe(struct platform_device *dev)
{/* 动态注册字符设备的流程一般如下:1.调用 alloc_chrdev_region() 函数申请设备编号。2.使用 cdev_init() 函数初始化设备描述结构体。3.使用 cdev_add() 函数将设备号与设备描述结构体关联起来,注册字符设备驱动。4.使用 class_create() 函数创建一个设备类.5.使用 device_create() 函数创建一个设备*/int ret = 0;/*进入这个函数就表明匹配成功了*/printk("led driver and device was matched!\r\n");/*1 创建设备号根据是否定义了设备号,通过条件判断选择不同的创建方式。如果定义了设备号,则使用MKDEV宏将主设备号和次设备号合成为设备号,并调用register_chrdev_region()函数注册字符设备号。如果没有定义设备号,则使用alloc_chrdev_region()函数动态分配设备号,并通过MAJOR和MINOR宏获取分配得到的主设备号和次设备号。*/if(gpioled.major){gpioled.devid = MKDEV(gpioled.major,0);register_chrdev_region(gpioled.devid,DEVICE_CNT,DEVICE_NAME);}else{alloc_chrdev_region(&gpioled.devid,0,DEVICE_CNT,DEVICE_NAME);gpioled.major = MAJOR(gpioled.devid);gpioled.minor = MINOR(gpioled.devid);}/* 2 初始化cdev设置cdev结构体的拥有者为当前模块(THIS_MODULE),然后使用 cdev_init() 函数初始化cdev结构体。参数包括待初始化的cdev结构体和用于操作该设备的file_operations结构体(hello_drv) */gpioled.cdev.owner= THIS_MODULE;cdev_init(&gpioled.cdev,&gpioled_fops);/* 3、添加一个cdev */cdev_add(&gpioled.cdev,gpioled.devid,DEVICE_CNT);/*4 创建设备类使用 class_create() 函数创建一个设备类,设备类用于在/sys/class目录下创建子目录,以组织同一类设备的相关信息。该函数的参数包括所属的模块(THIS_MODULE)和设备类的名称(DEVICE_NAME)。如果创建失败,IS_ERR() 函数将返回true,表示出错,此时使用 PTR_ERR() 函数返回错误码。 */gpioled.class = class_create(THIS_MODULE,DEVICE_NAME);if(IS_ERR(gpioled.class)){printk("newchr fail!\r\n");return PTR_ERR(gpioled.class);}/*5 创建设备使用 device_create() 函数创建一个设备,并在/dev目录下创建相应的设备节点。参数包括设备所属的类(newchr.class)、父设备(NULL,如果没有父设备)、设备号(newchr.devid)、设备私有数据(NULL,一般为设备驱动程序提供一个指针)和设备名称(DEVICE_NAME)。如果创建失败,IS_ERR() 函数将返回true,表示出错,此时使用 PTR_ERR() 函数返回错误码。 */gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, DEVICE_NAME);if(IS_ERR(gpioled.device)){printk("newchr fail!\r\n");return PTR_ERR(gpioled.device);}ret = myled_init(&gpioled);#if 0/* 5、设置 LED 所使用的 GPIO */ gpioled.nd = of_find_node_by_path("/gpioled");if(gpioled.nd == NULL){printk("gpioled node cant not found!\r\n");return -EINVAL;}else{printk("gpioled node hase been found!\r\n");}/* 2、 获取设备树中的 gpio 属性,得到 LED 所使用的 LED 编号 */ gpioled.led_gpio =  of_get_named_gpio(gpioled.nd,"gpios",0);if(gpioled.led_gpio < 0){printk("can't get led-gpio\r\n");return -EINVAL;}printk("led-gpio num = %d\r\n", gpioled.led_gpio); /* 3、设置 GPIO1_IO03 为输出,并且输出高电平,默认关闭 LED 灯 */ gpio_request(gpioled.led_gpio,"led0");gpio_direction_output(gpioled.led_gpio,1);
#endifreturn 0;
}
static int led_remove(struct platform_device *dev)
{gpio_set_value(gpioled.led_gpio,1);gpio_free(gpioled.led_gpio);printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);/*在模块卸载时,使用 cdev_del() 函数注销字符设备驱动,并使用 unregister_chrdev_region() 函数释放设备号资源。*//* 注销字符设备驱动 */cdev_del(&gpioled.cdev);/*  删除cdev */unregister_chrdev_region(gpioled.devid, DEVICE_CNT); /* 注销设备号 */device_destroy(gpioled.class, gpioled.devid);// 销毁设备,删除相应的设备节点class_destroy(gpioled.class);// 销毁设备类,释放相关资源printk("gpioled exit!\r\n");return 0;
}
/************************platfrom操作函数-endn***********************************************/static int __init gpioled_init(void)
{return platform_driver_register(&led_driver);
}static void __exit gpioled_exit(void)
{platform_driver_unregister(&led_driver);
}module_init(gpioled_init);
module_exit(gpioled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("oudafa");

五   、运行测试

1.编写 Makefile 文件

编写完使用make命令编译驱动程序。

KERN_DIR = /home/odf/linux-imx/linux-imxall:clearmake -C $(KERN_DIR) M=`pwd` modules $(CROSS_COMPILE)gcc -o dtsplatledApp dtsplatledApp.c clean:clearmake -C $(KERN_DIR) M=`pwd` modules cleanrm -rf modules.orderrm -f dtsplatledAppobj-m += dtsplatled.o 

 

2.使用nfs挂载到开发板上。

将编译出来 dtsplatled.ko 和dtsplatledApp 拷贝到 rootfs/lib/modules/4.1.15 目录中,

sudo cp dtsplatled.ko dtsplatledApp /home/odf/nfs_rootfs/rootfs/lib/modules/4.1.15/ 

    驱动模块加载完成以后到/sys/bus/platform/drivers/目录下查看驱动是否存在,我们在

dtsplatled.c 中设置 led_driver (platform_driver 类型)的 name 字段为“imx6ul-led”,因此会在

/sys/bus/platform/drivers/目录下存在名为“imx6ul-led”这个文件

重启开发板,进 入到目录 lib/modules/4.1.15 中,输入如下命令加载 dtsplatled.ko 这个驱动模块。

insmod dtsplatled.ko

 驱动和模块都存在,当驱动和设备匹配成功以后就会输出如图一行语句:

 3.测试:

驱动和设备匹配成功以后就可以测试 LED 灯驱动了,输入如下命令打开 LED 灯:  

./ledApp /dev/dtsplatled 1 

 输入如下命令关闭 LED 灯:  

./ledApp /dev/dtsplatled 0

相关文章:

MX6ULL学习笔记 (八) platform 设备驱动实验

前言&#xff1a; 什么是 Linux 下的 platform 设备驱动 Linux下的字符设备驱动一般都比较简单&#xff0c;只是对IO进行简单的读写操作。但是I2C、SPI、LCD、USB等外设的驱动就比较复杂了&#xff0c;需要考虑到驱动的可重用性&#xff0c;以避免内核中存在大量重复代码&…...

初识Linux:权限(2)

目录 权限 用户&#xff08;角色&#xff09; 文件权限属性 文件的权限属性&#xff1a; 有无权限的区别&#xff1a; 身份匹配&#xff1a; 拥有者、所属组的修改&#xff1a; 八进制的转化&#xff1a; 文件的类型&#xff1a; x可执行权限为什么不能执行&#xf…...

测试环境使用问题及其优化对策实践

1 背景及问题 G.J.Myers在<软件测试技巧>中提出&#xff1a;测试是为了寻找错误而运行程序的过程&#xff0c;一个好的测试用例是指很可能找到迄今为止尚未发现的错误的测试&#xff0c; 一个成功的测试是揭示了迄今为止尚未发现的错误的测试。 对于新手来说&#xff0…...

【力扣】206.反转链表

206.反转链表 这道题有两种解法&#xff0c;但不只有两种&#xff0c;嘿嘿。 法一&#xff1a;迭代法 就是按循序遍历将每一个指针的指向都给改了。比如说1——>2——>3改为null<——1<——2<——3这样。那这里以第二个结点为例&#xff0c;想一想。我想要指向…...

Python:核心知识点整理大全7-笔记

目录 4.2.5 遗漏了冒号 4.3 创建数值列表 4.3.1 使用函数 range() 4.3.2 使用 range()创建数字列表 结果如下&#xff1a; 4.3.3 对数字列表执行简单的统计计算 4.3.4 列表解析 4.4 使用列表的一部分 4.4.1 切片 4.4.2 遍历切片 4.4.3 复制列表 4.2.5 遗漏了冒号 fo…...

Hadoop学习笔记(HDP)-Part.15 安装HIVE

目录 Part.01 关于HDP Part.02 核心组件原理 Part.03 资源规划 Part.04 基础环境配置 Part.05 Yum源配置 Part.06 安装OracleJDK Part.07 安装MySQL Part.08 部署Ambari集群 Part.09 安装OpenLDAP Part.10 创建集群 Part.11 安装Kerberos Part.12 安装HDFS Part.13 安装Ranger …...

【力扣100】4.移动零

题目链接 我的题解&#xff1a; class Solution:def moveZeroes(self, nums: List[int]) -> None:"""Do not return anything, modify nums in-place instead."""# 思路是先计算共有几个0&#xff0c;然后remove几次&#xff0c;再末位加几个…...

Filebeat使用指南

Filebeat介绍主要优势主要功能配置日志的解析Kibana中设置日志解析安装步骤安装Filebeat安装监控通过prometheus监控 Filebeat和Logstash的主要区别 Filebeat介绍 Filebeat是使用Golang实现的轻量型日志采集器&#xff0c;也是Elasticsearch stack的一员。它可以作为一个agent…...

【Vue2】Vue的介绍与Vue的第一个实例

文章目录 前言一、为什么要学习Vue二、什么是Vue1.什么是构建用户界面2.什么是渐进式Vue的两种开发方式&#xff1a; 3.什么是框架 三、创建Vue实例四、插值表达式 {{}}1.作用&#xff1a;利用表达式进行插值&#xff0c;渲染到页面中2.语法3.错误用法 五、响应式特性1.什么是响…...

十五届蓝桥杯分享会(一)

注&#xff1a;省赛4月&#xff0c;决赛6月 一、蓝桥杯整体介绍 1.十四届蓝桥杯软件电子赛参赛人数&#xff1a;C 8w&#xff0c;java/python 2w&#xff0c;web 4k&#xff0c;单片机 1.8w&#xff0c;嵌入式/EDA5k&#xff0c;物联网 300 1.1设计类参赛人数&#xff1a;平…...

原生video设置控制面板controls显示哪些控件

之前我们学习了如何使用原生video播放视频 今天来一个进阶版的——设置控制面板controls显示哪些控件 先看一下当我们使用原生video时&#xff0c;controls属性为true时&#xff0c;相关代码如下&#xff1a; 正常的控制面板默认显示的控件有&#xff1a;播放、时间线、音量调…...

openlayers地图使用---跟随地图比例尺动态标绘大小的一种方式2

openlayers地图使用—跟随地图比例尺动态标绘大小的一种方式2 预期&#xff1a;随着地图比例尺放大缩小&#xff0c;地图上的标绘随着变化尺寸 思路&#xff1a;通过不断添加地图图层实现标绘的动态缩放 优点&#xff1a;标绘放大缩小非常流畅 缺点&#xff1a;标绘超过1000…...

C语言期末考试复习PTA数据类型及表达式-分支结构程序-循环结构-数组经典选择题

目录 第一章&#xff1a;C语言数据类型和表达式 第一题&#xff1a; 第二题&#xff1a; 第三题&#xff1a; 第四题&#xff1a; 第五题&#xff1a; 第六题&#xff1a; 第七题&#xff1a; 第八题&#xff1a; 第九题&#xff1a; 第二章&#xff1a;分支结构程序…...

RHEL8_Linux访问NFS存储及自动挂载

本章主要介绍NFS客户端的使用 创建FNS服务器并通过NFS共享一个目录在客户端上访问NFS共享的目录自动挂载的配置和使用 1.访问NFS存储 前面介绍了本地存储&#xff0c;本章就来介绍如何使用网络上的存储设备。NFS即网络文件系统&#xff0c;所实现的是 Linux 和 Linux 之间的共…...

python 使用 AppiumService 类启动appium server

一、前置说明 在Appium的1.6.0版本中引入了AppiumService类&#xff0c;可以很方便的通过该类来管理Appium服务器的启动和停止。 二、操作步骤 import osfrom appium.webdriver.appium_service import AppiumService as OriginalServerfrom libs import pathclass AppiumSer…...

HbuilderX使用Uniapp+Vue3安装uview-plus

如果你是vue2版本想使用uniapp去配置uviewui库可以参考之前的文章 小程序的第三方ui库推荐较多的还是uview的&#xff0c;看起来比较美观&#xff0c;功能也比较完善&#xff0c;下面将提一下Vue3安装uview-plus库的教程 创建项目 安装 首先进入官网 uView-Plus 直接下载并导…...

【Android】Java NIO(New I/O)的`Selector`类来实现非阻塞的Socket监听

如果你不想使用循环来监听客户端的连接和数据&#xff0c;你可以使用Java NIO&#xff08;New I/O&#xff09;的Selector类来实现非阻塞的Socket监听。Selector类提供了一种选择一组已经就绪的通道的机制&#xff0c;这样你就不需要使用循环来等待连接和数据。 以下是使用Sel…...

『亚马逊云科技产品测评』在当前飞速发展的AI人工智能时代云服务技术哪家强?

授权声明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 Developer Centre, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道 文章目录 引言一、亚马逊&阿里云发展历史介绍1.1 亚马逊发展历史1.2…...

经典神经网络——ResNet模型论文详解及代码复现

论文地址&#xff1a;Deep Residual Learning for Image Recognition (thecvf.com) PyTorch官方代码实现&#xff1a;vision/torchvision/models/resnet.py at main pytorch/vision (github.com) B站讲解&#xff1a; 【精读AI论文】ResNet深度残差网络_哔哩哔哩_bilibili …...

OpenCV-Python:DevCloud CodeLab介绍及学习

1.Opencv-Python演示环境 windows10 X64 企业版系统python 3.6.5 X64OpenCV-Python 3.4.2.16本地PyCharm IDE线上注册intel账号&#xff0c;使用DevCloud CodeLab 平台 2.DevCloud CodeLab是什么&#xff1f; DevCloud是一个基于云端的开发平台&#xff0c;提供了强大的计算…...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

基于Uniapp开发HarmonyOS 5.0旅游应用技术实践

一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架&#xff0c;支持"一次开发&#xff0c;多端部署"&#xff0c;可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务&#xff0c;为旅游应用带来&#xf…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现

摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序&#xff0c;以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务&#xff0c;提供稳定高效的数据处理与业务逻辑支持&#xff1b;利用 uniapp 实现跨平台前…...

Python爬虫(一):爬虫伪装

一、网站防爬机制概述 在当今互联网环境中&#xff0c;具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类&#xff1a; 身份验证机制&#xff1a;直接将未经授权的爬虫阻挡在外反爬技术体系&#xff1a;通过各种技术手段增加爬虫获取数据的难度…...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

Mysql中select查询语句的执行过程

目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析&#xff08;Parser&#xff09; 2.4、执行sql 1. 预处理&#xff08;Preprocessor&#xff09; 2. 查询优化器&#xff08;Optimizer&#xff09; 3. 执行器…...

面向无人机海岸带生态系统监测的语义分割基准数据集

描述&#xff1a;海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而&#xff0c;目前该领域仍面临一个挑战&#xff0c;即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...

安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲

文章目录 前言第一部分&#xff1a;体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分&#xff1a;体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...

【Linux系统】Linux环境变量:系统配置的隐形指挥官

。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量&#xff1a;setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...

TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?

在工业自动化持续演进的今天&#xff0c;通信网络的角色正变得愈发关键。 2025年6月6日&#xff0c;为期三天的华南国际工业博览会在深圳国际会展中心&#xff08;宝安&#xff09;圆满落幕。作为国内工业通信领域的技术型企业&#xff0c;光路科技&#xff08;Fiberroad&…...