Linux第90步_异步通知实验
“异步通知”的核心就是信号,由“驱动设备”主动报告给“应用程序”的。
1、添加“EXTI3.c”
#include "EXTI3.h"
#include <linux/gpio.h>
//使能gpio_request(),gpio_free(),gpio_direction_input(),
//使能gpio_direction_output(),gpio_get_value(),gpio_set_value()
#include <linux/of_gpio.h>
//使能of_gpio_named_count(),of_gpio_count(),of_get_named_gpio()
#include <linux/delay.h>
//Linux内核中用到的延时函数
//使能ndelay(),udelay(),mdelay(),占用cpu资源
#include <linux/time.h>
//使能msleep(),不会占用cpu资源
#include <linux/of_irq.h>
//使能irq_of_parse_and_map()
#include <linux/interrupt.h>
//request_irq(),free_irq(),enable_irq(),disable_irq(),disable_irq_nosync()#include "EXTI3_drv.h"struct Button_dev strButton;int Get_gpio_Button_num(void);
int Button_gpio_request(void);
void Button_free(void);
int Read_Button(void);static irqreturn_t key_interrupt_fun(int irq, void *dev_id)
{if(atomic_read(&strButton.button_irq_status) ==0){//若信号发送没有结束,则“驱动程序”则向“应用程序”发出信号,防止连续发出信号//在非中断程序中消抖,减少占用CPU资源atomic_set(&strButton.button_irq_status, 1);//设置原子变量值strButton.button_irq_status.counter=1if(strEXTI3Driver.async_queue)kill_fasync(&strEXTI3Driver.async_queue, SIGIO, POLL_IN);//当设备可以访问时,“驱动程序”则向“应用程序”发出信号,相当于产生“中断”。//fp=&strEXTI3Driver.async_queue:要操作的fasync_struct//sig=SIGIO:要发送的信号//band=POLL_IN:可读时设置为POLL_IN,可写时设置为POLL_OUT}return IRQ_HANDLED;
}int Get_gpio_Button_num(void)
{int ret = 0;const char *str;strButton.button_irq_status = (atomic_t)ATOMIC_INIT(0);/*初始化原子变量*/atomic_set(&strButton.button_irq_status, 0);/*设置原子变量初始值strButton.button_irq_status.counter=0*/strButton.keyvalue=0;/* 设置Button所使用的GPIO *//* 1、获取设备节点:strButton */strButton.nd = of_find_node_by_path("/key0");//path="/key0,使用“全路径的节点名“在“stm32mp157d-atk.dts“中查找节点“key0”//返回值:返回找到的节点,如果为NULL,表示查找失败。if(strButton.nd == NULL) {printk("key0 device node not find!\r\n");return -EINVAL;}/* 2.读取status属性 */ret = of_property_read_string(strButton.nd, "status", &str);//在key0节点中,status = "okay";//指定的设备节点strButton.nd//proname="status",给定要读取的属性名字//out_string=str:返回读取到的属性值//返回值:0,读取成功,负值,读取失败。if(ret < 0) return -EINVAL;if(strcmp(str, "okay")) return -EINVAL;//strcmp(s1,s2),当s1<s2时,返回值为负数//strcmp(s1,s2),当s1>2时,返回值为正数//strcmp(s1,s2),当s1=s2时,返回值为0/* 3、获取compatible属性值并进行匹配 */ret = of_property_read_string(strButton.nd, "compatible", &str);//在key0节点中,compatible = "zgq,key";//指定的设备节点strButton.nd//proname="compatible",给定要读取的属性名字//out_string=str:返回读取到的属性值//返回值:0,读取成功,负值,读取失败。if(ret < 0) {printk("key0 node: Failed to get compatible property\n"); return -EINVAL;}if (strcmp(str, "zgq,key")) {printk("key0 node: Compatible match failed\n");return -EINVAL;}/* 4、 根据设备树中的"key-gpio"属性,得到key0所使用的key0编号 */strButton.button_gpio_number = of_get_named_gpio(strButton.nd, "key-gpio", 0);//在key0节点中,key-gpio = <&gpiog 3 GPIO_ACTIVE_LOW>//np=strButton.nd,指定的“设备节点”//propname="key-gpio",给定要读取的属性名字//Index=0,给定的GPIO索引为0//返回值:正值,获取到的GPIO编号;负值,失败。if(strButton.button_gpio_number < 0) {printk("can't get key-gpio");return -EINVAL;}/* 5 、获取GPIO对应的中断号 */strButton.irq_num = irq_of_parse_and_map(strButton.nd, 0);//dev=strButton.nd:为设备节点;//Index=0:索引号,intemrupts属性可能包含多条中断信息,通过index指定要获取的信息;//返回值:中断号;if(!strButton.irq_num){return -EINVAL;}printk("key-gpio num = %d\r\n", strButton.button_gpio_number);//打印结果为:“key-gpio num = 99“//因为GPIO编号是从0开始的,GPIOG端口的序号是6,每个端口有16个IO口,因此GPIOI0的编号为6*16 + 3 = 99return 0;
}int Button_gpio_request(void)
{int ret = 0;unsigned long irq_flags;/* 5.向gpio子系统申请使用“gpio编号” */ret = gpio_request(strButton.button_gpio_number, "Button0");//gpio=strButton.button_gpio_number,指定要申请的“gpio编号”//label="Button",给这个gpio引脚设置个名字为"Button"//返回值:0,申请“gpio编号”成功;其他值,申请“gpio编号”失败;if (ret) {printk(KERN_ERR "Button: Failed to request key0\n");return ret;}/* 6、设置PG3为输入模式*/ret = gpio_direction_input(strButton.button_gpio_number);//gpio=strButton.button_gpio_number,指定的“gpio编号”if(ret < 0) {printk("can't set gpio!\r\n");}/* 获取设备树中指定的中断触发类型 */irq_flags = irq_get_trigger_type(strButton.irq_num);//strButton.irq_num为中断号if ( irq_flags==IRQF_TRIGGER_NONE )irq_flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;/*申请中断*/ret = request_irq(strButton.irq_num, key_interrupt_fun, irq_flags, "Key0_IRQ", NULL);//irq=strButton.irq_num:要申请中断的中断号;//handler=key_interrupt_fun:中断处理函数,当中断发生以后就会执行此中断处理函数;//fags=irq_flags:中断标志;可以在文件“include/linux/interrupt.h”里面查看所有的中断标志;//name="Key0_IRQ":中断名字,设置以后可以在“/proc/interrupts”文件中看到对应的中断名字;//dev=NULL:如果将flags设置为IRQF_SHARED的话,dev用来区分不同的中断。//一般情况下将dev设置为“设备结构体”,dev会传递给中断处理函数irg_handler_t的第二个参数。//返回值:0表示中断申请成功,如果返回“-EBUSY”的话表示中断已经被申请过了, 其他负值,表示中断申请失败。if (ret) {printk(KERN_ERR "Button: Failed to request irq\n");return ret;}return 0;
}//函数功能:释放Button的gpio
void Button_free(void)
{free_irq(strButton.irq_num, NULL); /* 释放中断 */gpio_free(strButton.button_gpio_number);/* 释放IO */
}//读取按键的值
int Read_Button(void)
{u8 ch;int status;if(atomic_read(&strButton.button_irq_status) ==0){status = -EAGAIN;//无按钮按下, Try again}else{ch=10;while(ch)//消抖{//mdelay(10);//延时10毫秒消抖,占用cpu资源msleep(10);//延时10毫秒消抖,不会占用cpu资源if(gpio_get_value(strButton.button_gpio_number) == 0) ch=10;ch--;}ch=0;if(strButton.keyvalue) ch=1;if(ch)//关灯{strButton.keyvalue=KEY_OFF;//准备中断返回值printk("Button: off\r\n");}else//开灯{strButton.keyvalue=KEY_ON;//准备中断返回值printk("Button: on\r\n");}status=strButton.keyvalue;//发送中断返回值atomic_set(&strButton.button_irq_status, 0);/*原子变量初始值strButton.button_irq_status.counter=0, 状态重置 */}return status;
}
2、添加“EXTI3.h”
#ifndef __EXTI3_H
#define __EXTI3_H#include <linux/types.h>
/*
数据类型重命名
使能bool,u8,u16,u32,u64, uint8_t, uint16_t, uint32_t, uint64_t
使能s8,s16,s32,s64,int8_t,int16_t,int32_t,int64_t
*/
#include <linux/of.h> //使能device_node结构
#include <linux/wait.h> //使能wait_queue_head_t结构#define KEY_ON 1 /* 按键按下 */
#define KEY_OFF 0 /* 按键松开 */struct Button_dev{struct device_node *nd; /*设备节点*/int button_gpio_number; /*Button所使用的GPIO编号*/int irq_num; /* 中断号 */atomic_t button_irq_status; /*原子变量*/int keyvalue;
};
extern struct Button_dev strButton;extern int Get_gpio_Button_num(void);
extern int Button_gpio_request(void);
extern void Button_free(void);
extern int Read_Button(void);#endif
3、添加“EXTI3_drv.c”
#include "EXTI3_drv.h"
#include "EXTI3.h"
#include <linux/types.h>
//数据类型重命名
//使能bool,u8,u16,u32,u64, uint8_t, uint16_t, uint32_t, uint64_t
//使能s8,s16,s32,s64,int8_t,int16_t,int32_t,int64_t
#include <linux/ide.h>
//使能copy_from_user(),copy_to_user()
#include <linux/module.h>
//使能EXTI3Driver_init(),EXTI3Driver_exit()
#include <linux/gpio.h>
//使能gpio_request(),gpio_free(),gpio_direction_input(),
//gpio_direction_output(),gpio_get_value(),gpio_set_value()
#include <linux/spinlock.h>
//使能DEFINE_SPINLOCK(),spin_lock_init(),spin_lock(),spin_trylock(),spin_is_locked()
//spin_lock_irq(),spin_unlock_irq(),spin_lock_irqsave(),spin_unlock_irqrestore()#include <linux/delay.h>
//Linux内核中用到的延时函数
//使能ndelay(),udelay(),mdelay()
#include <linux/poll.h>
//使能poll(),poll_wait()#define EXTI3Driver_CNT 1 //定义设备数量为1
#define EXTI3Driver_NAME "EXTI3Driver" //定义设备的名字struct EXTI3Driver_dev strEXTI3Driver;/* 打开设备 */
static int EXTI3Driver_open(struct inode *inode, struct file *filp)
{/*通过判断原子变量的值来检查EXTI3有没有被别的应用使用*/if (!atomic_dec_and_test(&strEXTI3Driver.lock)){//当strEXTI3Driver.lock.counter=1时,atomic_dec_and_test()返回1//从strEXTI3Driver.lock.counter减1,如果结果为0就返回1,否则返回0;atomic_inc(&strEXTI3Driver.lock);/*小于0的话就加1,使其原子变量等于0*/return -EBUSY; /* EXTI3被使用,返回忙*/}filp->private_data = &strEXTI3Driver; /*设置私有数据*/printk("EXTI3Driver_open!\r\n");return 0;
}/* 从设备读取数据,保存到首地址为buf的数据块中,长度为cnt个字节 */
//file结构指针变量flip表示要打开的设备文件
//buf表示用户数据块的首地址
//cnt表示用户数据的长度,单位为字节
//loff_t结构指针变量offt表示“相对于文件首地址的偏移”
static ssize_t EXTI3Driver_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{int ret;int value;value = Read_Button();if( value == -EAGAIN ) ret = value;else{ret = copy_to_user(buf, &value, sizeof(value));//将value拷贝到buf[]中}return ret;
}/*fasync函数,用于处理异步通知
fd : 文件描述符
filp : 要打开的设备文件(文件描述符)
on : 模式
返回值: 负数表示函数执行失败
*/
/*当“应用程序”通过“fcntl(fd,F SETFL, flags|FASYNC)”改变fasync标记的时候,
“驱动程序” file_operations操作集中的EXTI3Driver_fasync()函数就会被执行*/
static int EXTI3Driver_fasync(int fd, struct file *filp, int on)
{struct EXTI3Driver_dev *dev = filp->private_data;if (fasync_helper(fd, filp, on, &dev->async_queue) < 0){/*当“应用程序”通过“fcntl(fd,F SETFL, flags|FASYNC)”改变fasync标记的时候,“驱动程序”file_operations操作集中的xxx_fasync()函数就会被执行*/return -EIO;}return 0;
}/* 向设备写数据,将数据块首地址为buf的数据,长度为cnt个字节,发送给用户 */
//file结构指针变量flip表示要打开的设备文件
//buf表示用户数据块的首地址
//cnt表示用户数据的长度,单位为字节
//loff_t结构指针变量offt表示“相对于文件首地址的偏移”
static ssize_t EXTI3Driver_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{return 0;
}/* 关闭/释放设备 */
static int EXTI3Driver_release(struct inode *inode, struct file *filp)
{struct EXTI3Driver_dev *dev = filp->private_data;atomic_inc(&dev->lock);/*关闭驱动文件的时候释放原子变量,便于其它线程使用*/printk("EXTI3Driver_release!\r\n");return EXTI3Driver_fasync(-1, filp, 0);/* 删除异步通知 */
}/*声明file_operations结构变量MyCharDevice_fops*/
/*它是指向设备的操作函数集合变量*/
const struct file_operations EXTI3Driver_fops = {.owner = THIS_MODULE,.open = EXTI3Driver_open,.read = EXTI3Driver_read,.write = EXTI3Driver_write,.release = EXTI3Driver_release,.fasync = EXTI3Driver_fasync,
};/*驱动入口函数 */
static int __init EXTI3Driver_init(void)
{int ret;strEXTI3Driver.lock = (atomic_t)ATOMIC_INIT(0);/*初始化原子变量*/atomic_set(&strEXTI3Driver.lock, 1);/*原子变量初始值strEXTI3Driver.lock.counter=1*/ret=Get_gpio_Button_num();//读引脚编号if(ret < 0) return ret; /* 1、申请“gpio编号”*/ret=Button_gpio_request();//申请“gpio编号” if(ret < 0) return ret;//向gpio子系统申请使用“gpio编号” 失败/*2、申请设备号*/strEXTI3Driver.major=0;if(strEXTI3Driver.major)/*如果指定了主设备号*/{strEXTI3Driver.devid = MKDEV(strEXTI3Driver.major, 0);//输入参数strEXTI3Driver.major为“主设备号”//输入参数0为“次设备号”,大部分驱动次设备号都选择0//将strEXTI3Driver.major左移20位,再与0相或,就得到“Linux设备号”ret=register_chrdev_region( strEXTI3Driver.devid,\EXTI3Driver_CNT, \EXTI3Driver_NAME );//strEXTI3Driver.devid表示起始设备号//EXTI3Driver_CNT表示次设备号的数量//EXTI3Driver_NAME表示设备名if(ret < 0)goto free_gpio;}else{ /* 没有定义设备号 */ret=alloc_chrdev_region( &strEXTI3Driver.devid,\0, \EXTI3Driver_CNT,\EXTI3Driver_NAME);/* 申请设备号 *///strEXTI3Driver.devid:保存申请到的设备号//0:次设备号的起始地址//EXTI3Driver_CNT:要申请的次设备号数量;//EXTI3Driver_NAME:表示“设备名字”if(ret < 0)goto free_gpio;strEXTI3Driver.major = MAJOR(strEXTI3Driver.devid);/* 获取分配号的主设备号 *///输入参数strEXTI3Driver.devid为“Linux设备号”//将strEXTI3Driver.devid右移20位得到“主设备号”strEXTI3Driver.minor = MINOR(strEXTI3Driver.devid);/* 获取分配号的次设备号 *///输入参数strEXTI3Driver.devid为“Linux设备号”//将strEXTI3Driver.devid与0xFFFFF相与后得到“次设备号”}/*3、注册字符设备*/strEXTI3Driver.cdev.owner = THIS_MODULE;//使用THIS_MODULE将owner指针指向当前这个模块cdev_init(&strEXTI3Driver.cdev,&EXTI3Driver_fops);//注册字符设备,初始化“字符设备结构变量strEXTI3Driver.cdev”//strEXTI3Driver.cdev是等待初始化的结构体变量//EXTI3Driver_fops就是字符设备文件操作函数集合/*4、添加字符设备*/ret=cdev_add(&strEXTI3Driver.cdev,strEXTI3Driver.devid,EXTI3Driver_CNT);//添加字符设备/*&strEXTI3Driver.cdev表示指向要添加的字符设备,即字符设备结构strEXTI3Driver.cdev变量*///strEXTI3Driver.devid表示设备号//EXTI3Driver_CNT表示需要添加的设备数量if(ret < 0 ) //添加字符设备失败goto del_register;printk("dev id major = %d,minor = %d\r\n", strEXTI3Driver.major, strEXTI3Driver.minor);printk("EXTI3Driver_init is ok!!!\r\n");/*5、自动创建设备节点 */strEXTI3Driver.class =class_create(THIS_MODULE, EXTI3Driver_NAME);if (IS_ERR(strEXTI3Driver.class)){goto del_cdev;}/*6、创建设备 */strEXTI3Driver.device = device_create(strEXTI3Driver.class, NULL, strEXTI3Driver.devid, NULL, EXTI3Driver_NAME);//创建设备//设备要创建在strEXTI3Driver.class类下面//NULL表示没有父设备//strEXTI3Driver.devid是设备号;//参数drvdata=NULL,设备没有使用数据//EXTI3Driver_NAME是设备名字//如果设置fmt=EXTI3Driver_NAME 的话,就会生成/dev/EXTI3Driver_NAME设备文件。//返回值就是创建好的设备。if (IS_ERR(strEXTI3Driver.device)){goto destroy_class;}return 0;destroy_class:class_destroy(strEXTI3Driver.class);//删除类//strEXTI3Driver.class就是要删除的类del_cdev:cdev_del(&strEXTI3Driver.cdev);//删除字符设备//&strEXTI3Driver.cdev表示指向需要删除的字符设备,即字符设备结构strEXTI3Driver.cdev变量del_register:unregister_chrdev_region(strEXTI3Driver.devid, EXTI3Driver_CNT);/* 释放设备号 *///strEXTI3Driver.devid:需要释放的起始设备号//EXTI3Driver_CNT:需要释放的次设备号数量;free_gpio://申请设备号失败/*释放gpio编号*/Button_free();return -EIO;
}/*驱动出口函数 */
static void __exit EXTI3Driver_exit(void)
{/*1、删除字符设备*/cdev_del(&strEXTI3Driver.cdev);/*删除字符设备*//*&strEXTI3Driver.cdev表示指向需要删除的字符设备,即字符设备结构&strEXTI3Driver.cdev变量*//*2、 释放设备号 */unregister_chrdev_region(strEXTI3Driver.devid, EXTI3Driver_CNT);/*释放设备号 *///strEXTI3Driver.devid:需要释放的起始设备号//EXTI3Driver_CNT:需要释放的次设备号数;/*3、 删除设备 */device_destroy(strEXTI3Driver.class, strEXTI3Driver.devid);//删除创建的设备//strEXTI3Driver.class是要删除的设备所处的类//strEXTI3Driver.devid是要删除的设备号/*4、删除类*/class_destroy(strEXTI3Driver.class);//删除类//strEXTI3Driver.class就是要删除的类/*5、释放gpio编号*/Button_free();
}module_init(EXTI3Driver_init);
//指定EXTI3Driver_init()为驱动入口函数
module_exit(EXTI3Driver_exit);
//指定EXTI3Driver_exit()为驱动出口函数MODULE_AUTHOR("Zhanggong");//添加作者名字
MODULE_LICENSE("GPL");//LICENSE采用“GPL协议”
MODULE_INFO(intree,"Y");
//去除显示“loading out-of-tree module taints kernel.”
4、添加“EXTI3_drv.h”
#ifndef __EXTI3_DRIVER_H
#define __EXTI3_DRIVER_H#include <linux/types.h>
/*
数据类型重命名
使能bool,u8,u16,u32,u64, uint8_t, uint16_t, uint32_t, uint64_t
使能s8,s16,s32,s64,int8_t,int16_t,int32_t,int64_t
*/
#include <linux/cdev.h> //使能cdev结构
#include <linux/cdev.h> //使能class结构和device结构#include <linux/fs.h> //使能fasync_struct结构struct EXTI3Driver_dev{dev_t devid; /*声明32位变量devid用来给保存设备号*/int major; /*主设备号*/int minor; /*次设备号*/struct cdev cdev; /*字符设备结构变量cdev */struct class *class; /*类*/struct device *device; /*设备*/atomic_t lock; /*原子变量*/struct fasync_struct *async_queue; /* fasync_struct结构体 */
};
extern struct EXTI3Driver_dev strEXTI3Driver;#endif
5、添加“LED.c”
#include "LED.h"
#include <linux/gpio.h>
//使能gpio_request(),gpio_free(),gpio_direction_input(),
//使能gpio_direction_output(),gpio_get_value(),gpio_set_value()
#include <linux/of_gpio.h>
//使能of_gpio_named_count(),of_gpio_count(),of_get_named_gpio()struct MyLED_dev strMyLED;int Get_led0_num(void);
int led_GPIO_request(void);
void MyLED_free(void);
void led_switch(u8 sta);int Get_led0_num(void)
{int ret = 0;const char *str;/* 设置LED所使用的GPIO *//* 1、获取设备节点:strMyLED */strMyLED.nd = of_find_node_by_path("/led0");//path="/led0,使用“全路径的节点名“在“stm32mp157d-atk.dts“中查找节点“led0”//返回值:返回找到的节点,如果为NULL,表示查找失败。if(strMyLED.nd == NULL) {printk("led0 node not find!\r\n");return -EINVAL;}/* 2.读取status属性 */ret = of_property_read_string(strMyLED.nd, "status", &str);//在led0节点中,status = "okay";//指定的设备节点strMyLED.nd//proname="status",给定要读取的属性名字//out_string=str:返回读取到的属性值//返回值:0,读取成功,负值,读取失败。if(ret < 0) return -EINVAL;if (strcmp(str, "okay")) return -EINVAL;//strcmp(s1,s2),当s1<s2时,返回值为负数//strcmp(s1,s2),当s1>2时,返回值为正数//strcmp(s1,s2),当s1=s2时,返回值为0/* 3、获取compatible属性值并进行匹配 */ret = of_property_read_string(strMyLED.nd, "compatible", &str);//在led0节点中,compatible = "zgq,led";//指定的设备节点strMyLED.nd//proname="compatible",给定要读取的属性名字//out_string=str:返回读取到的属性值//返回值:0,读取成功,负值,读取失败。if(ret < 0) {printk("led0 node: Failed to get compatible property\n"); return -EINVAL;}if (strcmp(str, "zgq,led")) {printk("led0 node: Compatible match failed\n");return -EINVAL;}/* 4、 根据设备树中的"led-gpio"属性,得到LED所使用的LED编号 */strMyLED.led_gpio_number = of_get_named_gpio(strMyLED.nd, "led-gpio", 0);//在led0节点中,led-gpio = <&gpioi 0 GPIO_ACTIVE_LOW>//np=strMyLED.nd,指定的“设备节点”//propname="led-gpio",给定要读取的属性名字//Index=0,给定的GPIO索引为0//返回值:正值,获取到的GPIO编号;负值,失败。if(strMyLED.led_gpio_number < 0) {printk("can't get led-gpio");return -EINVAL;}printk("led-gpio num = %d\r\n", strMyLED.led_gpio_number);//打印结果为:“led-gpio num = 128“//因为GPIO编号是从0开始的,GPIOI端口的序号是8,每个端口有16个IO口,因此GPIOI0的编号为8*16=128return 0;
}int led_GPIO_request(void)
{int ret = 0;/* 5.向gpio子系统申请使用“gpio编号” */ret = gpio_request(strMyLED.led_gpio_number, "LED-GPIO");//gpio=strMyLED.led_gpio_number,指定要申请的“gpio编号”//Iabel="LED-GPIO",给这个gpio引脚设置个名字为"LED-GPIO"//返回值:0,申请“gpio编号”成功;其他值,申请“gpio编号”失败;if (ret) {printk(KERN_ERR "strMyLED: Failed to request led-gpio\n");return ret;}/* 6、设置PI0为输出,并且输出高电平,默认关闭LED灯 */ret = gpio_direction_output(strMyLED.led_gpio_number, 1);//gpio=strMyLED.led_gpio_number,指定的“gpio编号”,这里是128,对应的是GI0引脚//value=1,设置引脚输出高电平//返回值:0,设置“引脚输出为vakued的值”成功;负值,设置“引脚输出为vakued的值”失败。if(ret < 0) {printk("can't set gpio!\r\n");}return 0;
}//函数功能:释放MyLED的gpio
void MyLED_free(void)
{gpio_free(strMyLED.led_gpio_number);
}void led_switch(u8 sta)
{if(sta == LEDON) {gpio_set_value(strMyLED.led_gpio_number, 0); /* 打开LED灯 */}
else if(sta == LEDOFF) {gpio_set_value(strMyLED.led_gpio_number, 1); /* 关闭LED灯 */}
}
6、添加“LED.h”
#ifndef __LED_H
#define __LED_H#include <linux/types.h>
/*
数据类型重命名
使能bool,u8,u16,u32,u64, uint8_t, uint16_t, uint32_t, uint64_t
使能s8,s16,s32,s64,int8_t,int16_t,int32_t,int64_t
*/
#include <linux/of.h> //使能device_node结构#define LEDOFF 0 /* 关灯 */
#define LEDON 1 /* 开灯 */struct MyLED_dev{struct device_node *nd; /*设备节点*/int led_gpio_number; /*led所使用的GPIO编号*/
};
extern struct MyLED_dev strMyLED;extern int Get_led0_num(void);
extern int led_GPIO_request(void);
extern void MyLED_free(void);
extern void led_switch(u8 sta);#endif
7、添加“LED_drv.c”
#include "LED_drv.h"
#include "LED.h"
#include <linux/types.h>
//数据类型重命名
//使能bool,u8,u16,u32,u64, uint8_t, uint16_t, uint32_t, uint64_t
//使能s8,s16,s32,s64,int8_t,int16_t,int32_t,int64_t
#include <linux/ide.h>
//使能copy_from_user(),copy_to_user()
#include <linux/module.h>
//使能LEDDriver_init(),LEDDriver_exit()
#include <linux/gpio.h>
//使能gpio_request(),gpio_free(),gpio_direction_input(),
//gpio_direction_output(),gpio_get_value(),gpio_set_value()#define LEDDriver_CNT 1 //定义设备数量为1
#define LEDDriver_NAME "LEDDriver" //定义设备的名字struct LEDDriver_dev strLEDDriver;/* 打开设备 */
static int LEDDriver_open(struct inode *inode, struct file *filp)
{/*通过判断原子变量的值来检查LED有没有被别的应用使用*/if (!atomic_dec_and_test(&strLEDDriver.lock)){//当strLEDDriver.lock.counter=1时,atomic_dec_and_test()返回1//从strLEDDriver.lock.counter减1,如果结果为0就返回1,否则返回0;atomic_inc(&strLEDDriver.lock);/*小于0的话就加1,使其原子变量等于0*/return -EBUSY; /* LED被使用,返回忙*/}filp->private_data = &strLEDDriver; /*设置私有数据*/printk("LEDDriver_open!\r\n");return 0;
}/* 从设备读取数据,保存到首地址为buf的数据块中,长度为cnt个字节 */
//file结构指针变量flip表示要打开的设备文件
//buf表示用户数据块的首地址
//cnt表示用户数据的长度,单位为字节
//loff_t结构指针变量offt表示“相对于文件首地址的偏移”
static ssize_t LEDDriver_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{return 0;
}/* 向设备写数据,将数据块首地址为buf的数据,长度为cnt个字节,发送给用户 */
//file结构指针变量flip表示要打开的设备文件
//buf表示用户数据块的首地址
//cnt表示用户数据的长度,单位为字节
//loff_t结构指针变量offt表示“相对于文件首地址的偏移”
static ssize_t LEDDriver_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{int ret = 0;unsigned char databuf[1];unsigned char ledstat;ret = copy_from_user(databuf, buf, cnt);
//将buf[]中的前cnt个字节拷贝到databuf[]中if(ret <0){printk("kernel write failed!\r\n");ret = -EFAULT;}ledstat = databuf[0];/*获取到应用传递进来的开关灯状态*/led_switch(ledstat);/*执行开灯或执行关灯*/return ret;
}/* 关闭/释放设备 */
static int LEDDriver_release(struct inode *inode, struct file *filp)
{struct LEDDriver_dev *dev = filp->private_data;atomic_inc(&dev->lock);/*关闭驱动文件的时候释放原子变量,便于其它线程使用*/printk("LEDDriver_release!\r\n");return 0;
}/*声明file_operations结构变量MyCharDevice_fops*/
/*它是指向设备的操作函数集合变量*/
const struct file_operations LEDDriver_fops = {.owner = THIS_MODULE,.open = LEDDriver_open,.read = LEDDriver_read,.write = LEDDriver_write,.release = LEDDriver_release,
};/*驱动入口函数 */
static int __init LEDDriver_init(void)
{int ret;strLEDDriver.lock = (atomic_t)ATOMIC_INIT(0);/*初始化原子变量*/atomic_set(&strLEDDriver.lock, 1);/*原子变量初始值strLEDDriver.lock.counter=1*/ret=Get_led0_num();//读引脚编号if(ret < 0) return ret; /* 1、申请“gpio编号”*/ret=led_GPIO_request();//申请“gpio编号” if(ret < 0) return ret;//向gpio子系统申请使用“gpio编号” 失败/*2、申请设备号*/strLEDDriver.major=0;if(strLEDDriver.major)/*如果指定了主设备号*/{strLEDDriver.devid = MKDEV(strLEDDriver.major, 0);//输入参数strLEDDriver.major为“主设备号”//输入参数0为“次设备号”,大部分驱动次设备号都选择0//将strLEDDriver.major左移20位,再与0相或,就得到“Linux设备号”
ret=register_chrdev_region( strLEDDriver.devid,\LEDDriver_CNT, \LEDDriver_NAME );//strLEDDriver.devid表示起始设备号//LEDDriver_CNT表示次设备号的数量//LEDDriver_NAME表示设备名if(ret < 0)goto free_gpio;}else{ /* 没有定义设备号 */
ret=alloc_chrdev_region( &strLEDDriver.devid,\0, \LEDDriver_CNT,\LEDDriver_NAME);/* 申请设备号 *///strLEDDriver.devid:保存申请到的设备号//0:次设备号的起始地址//LEDDriver_CNT:要申请的次设备号数量;//LEDDriver_NAME:表示“设备名字”if(ret < 0)goto free_gpio;strLEDDriver.major = MAJOR(strLEDDriver.devid);/* 获取分配号的主设备号 *///输入参数strLEDDriver.devid为“Linux设备号”//将strLEDDriver.devid右移20位得到“主设备号”strLEDDriver.minor = MINOR(strLEDDriver.devid);/* 获取分配号的次设备号 *///输入参数strLEDDriver.devid为“Linux设备号”//将strLEDDriver.devid与0xFFFFF相与后得到“次设备号”}/*3、注册字符设备*/strLEDDriver.cdev.owner = THIS_MODULE;//使用THIS_MODULE将owner指针指向当前这个模块cdev_init(&strLEDDriver.cdev,&LEDDriver_fops);//注册字符设备,初始化“字符设备结构变量strLEDDriver.cdev”//strLEDDriver.cdev是等待初始化的结构体变量//LEDDriver_fops就是字符设备文件操作函数集合/*4、添加字符设备*/ret=cdev_add(&strLEDDriver.cdev,strLEDDriver.devid,LEDDriver_CNT);//添加字符设备/*&strLEDDriver.cdev表示指向要添加的字符设备,即字符设备结构strLEDDriver.cdev变量*///strLEDDriver.devid表示设备号//LEDDriver_CNT表示需要添加的设备数量if(ret < 0 ) //添加字符设备失败goto del_register;printk("dev id major = %d,minor = %d\r\n", strLEDDriver.major, strLEDDriver.minor);printk("LEDDriver_init is ok!!!\r\n");/*5、自动创建设备节点 */strLEDDriver.class =class_create(THIS_MODULE, LEDDriver_NAME);if (IS_ERR(strLEDDriver.class)){goto del_cdev;}/*6、创建设备 */strLEDDriver.device = device_create(strLEDDriver.class, NULL, strLEDDriver.devid, NULL, LEDDriver_NAME);//创建设备//设备要创建在strLEDDriver.class类下面//NULL表示没有父设备//strLEDDriver.devid是设备号;//参数drvdata=NULL,设备没有使用数据//LEDDriver_NAME是设备名字//如果设置fmt=LEDDriver_NAME 的话,就会生成/dev/LEDDriver_NAME设备文件。//返回值就是创建好的设备。if (IS_ERR(strLEDDriver.device)){goto destroy_class;}return 0;destroy_class:class_destroy(strLEDDriver.class);//删除类//strLEDDriver.class就是要删除的类del_cdev:cdev_del(&strLEDDriver.cdev);//删除字符设备//&strLEDDriver.cdev表示指向需要删除的字符设备,即字符设备结构strLEDDriver.cdev变量del_register:unregister_chrdev_region(strLEDDriver.devid, LEDDriver_CNT);/* 释放设备号 *///strLEDDriver.devid:需要释放的起始设备号//LEDDriver_CNT:需要释放的次设备号数量;free_gpio://申请设备号失败/*释放gpio编号*/MyLED_free();return -EIO;
}/*驱动出口函数 */
static void __exit LEDDriver_exit(void)
{/*1、删除字符设备*/cdev_del(&strLEDDriver.cdev);/*删除字符设备*//*&strLEDDriver.cdev表示指向需要删除的字符设备,即字符设备结构&strLEDDriver.cdev变量*//*2、 释放设备号 */unregister_chrdev_region(strLEDDriver.devid, LEDDriver_CNT);/*释放设备号 *///strLEDDriver.devid:需要释放的起始设备号//LEDDriver_CNT:需要释放的次设备号数;/*3、 删除设备 */device_destroy(strLEDDriver.class, strLEDDriver.devid);//删除创建的设备//strLEDDriver.class是要删除的设备所处的类//strLEDDriver.devid是要删除的设备号/*4、删除类*/class_destroy(strLEDDriver.class);//删除类//strLEDDriver.class就是要删除的类/*5、释放gpio编号*/MyLED_free();
}module_init(LEDDriver_init);
//指定LEDDriver_init()为驱动入口函数
module_exit(LEDDriver_exit);
//指定LEDDriver_exit()为驱动出口函数MODULE_AUTHOR("Zhanggong");//添加作者名字
MODULE_LICENSE("GPL");//LICENSE采用“GPL协议”
MODULE_INFO(intree,"Y");
//去除显示“loading out-of-tree module taints kernel.”
8、添加“LED_drv.h”
#ifndef __LED_DRIVER_H
#define __LED_DRIVER_H#include <linux/types.h>
/*
数据类型重命名
使能bool,u8,u16,u32,u64, uint8_t, uint16_t, uint32_t, uint64_t
使能s8,s16,s32,s64,int8_t,int16_t,int32_t,int64_t
*/
#include <linux/cdev.h>
//使能cdev结构
//使能class结构和device结构struct LEDDriver_dev{dev_t devid; /*声明32位变量devid用来给保存设备号*/int major; /*主设备号*/int minor; /*次设备号*/struct cdev cdev; /*字符设备结构变量cdev */struct class *class; /*类*/struct device *device; /*设备*/atomic_t lock; /*原子变量*/
};
extern struct LEDDriver_dev strLEDDriver;#endif
9、添加“ButtonLED_APP.c”
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"#include <unistd.h>
//Linux系统编程下用到的延时函数
//使能usleep(),sleep()//#include <delay.h>
//Linux内核中用到的延时函数
//使能ndelay(),udelay(),mdelay()
#include <poll.h>
//FD_ZERO(),FD_SET(),select(),FD_CLR(),FD_ISSET()#include <signal.h>#define LED_OFF 0 /* 关灯 */
#define LED_ON 1 /* 开灯 */static int fd_button;
static int fd_led;
/*
* SIGIO信号处理函数
signum : 信号值
*/
static void sigio_signal_func(int signum)
{unsigned int key_val = 0;int keyvalue;int status;read(fd_button, &keyvalue, sizeof(keyvalue));if (keyvalue == LED_ON){//如果按键按下printf("KEY0 Press, value = %#X\r\n", keyvalue);/* 按下 */status = LED_ON;write(fd_led, &status, 1);}else if (keyvalue == LED_OFF){//如果按键松开printf("KEY0 Press, value = %#X\r\n", keyvalue);/* 按下 */status = LED_OFF;write(fd_led, &status, 1);//file结构指针变量fd_led表示要打开的设备文件//buf=&status表示用户数据块的首地址//cnt=1表示用户数据的长度,单位为字节}
}/*
参数argc: argv[]数组元素个数
参数argv[]:是一个指针数组
返回值: 0 成功;其他 失败
./ButtonLED_APP /dev/EXTI3Driver /dev/LEDDriver
*/
int main(int argc, char *argv[])
{int flags = 0;int retvalue;/* 1. 判断参数 */if(argc != 3){printf("Error Usage!\r\n");return -1;}//argv[]是指向输入参数“./ButtonLED_APP /dev/EXTI3Driver /dev/LEDDriver”/* 2. 打开文件 */fd_button = open(argv[1], O_RDONLY | O_NONBLOCK);//O_RDONLY表示只读模式;//O_NONBLOCK表示如果路径名指向FIFO/块文字/字符文件,则把文件的打开和后继I/O设置为非阻塞;//如果打开“/dev/EXTI3Driver”文件成功,则fd_button为“文件描述符”,argv[1]="/dev/EXTI3Driver"if(fd_button < 0){printf("Can't open file %s\r\n", argv[1]);return -1;}fd_led = open(argv[2], O_RDWR);//如果打开“/dev/LEDDriver”文件成功,则fd_led为“文件描述符”,argv[2]=“/dev/LEDDriver”if(fd_led < 0){printf("Can't open file %s\r\n", argv[2]);return -1;}signal(SIGIO, sigio_signal_func);/* 设置信号SIGIO的处理函数 */fcntl(fd_button, F_SETOWN, getpid()); /* 将当前进程的进程号告诉给内核 */flags = fcntl(fd_button, F_GETFD); /*获取当前的进程状态 */fcntl(fd_button, F_SETFL, flags | FASYNC);/* 设置进程启用异步通知功能 *//* 3. 读文件 */while(1){sleep(2);}/* 关闭设备 */retvalue = close(fd_button);//fd_button表示要关闭的“文件描述符”//返回值等于0表示关闭成功//返回值小于0表示关闭失败if(retvalue < 0){printf("Can't close file %s\r\n", argv[1]);return -1;}retvalue = close(fd_led);//fd_led表示要关闭的“文件描述符”//返回值等于0表示关闭成功//返回值小于0表示关闭失败if(retvalue < 0){printf("Can't close file %s\r\n", argv[2]);return -1;}return 0;
}
10、添加“Makefile”
#Linux一个Makefile编译多个内核驱动
KERNELDIR := /home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31
#使用“:=”将其后面的字符串赋值给KERNELDIR
CURRENT_PATH := $(shell pwd)
#采用“shell pwd”获取当前打开的路径
#使用“$(变量名)”引用“变量的值”
MyAPP := ButtonLED_APPobj-m := MyButton.o MyLED.oMyButton-y := EXTI3_drv.o EXTI3.o
MyLED-y := LED_drv.o LED.oCC := arm-none-linux-gnueabihf-gccdrv:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modulesapp:$(CC) $(MyAPP).c -o $(MyAPP)clean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) cleanrm $(MyAPP)install:sudo cp *.ko $(MyAPP) /home/zgq/linux/nfs/rootfs/lib/modules/5.4.31/ -f
11、添加“c_cpp_properties.json”
{"configurations": [{"name": "Linux","includePath": ["${workspaceFolder}/**","/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31","/home/zgq/linux/Linux_Drivers/Asynchronous_Notification", "/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31/arch/arm/include","/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31/include","/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31/arch/arm/include/generated"],"defines": [],"compilerPath": "/usr/bin/gcc","cStandard": "gnu11","cppStandard": "gnu++14","intelliSenseMode": "gcc-x64"}],"version": 4
}
12、编译
输入“make clean回车”
输入“make drv回车”
输入“make app回车”
输入“make install回车”
输入“ls /home/zgq/linux/nfs/rootfs/lib/modules/5.4.31/ -l回车”查看是存在“ButtonLED_APP,MyButton.ko和MyLED.ko”
13、测试
启动开发板,从网络下载程序
输入“root”
输入“cd /lib/modules/5.4.31/回车”
切换到“/lib/modules/5.4.31/”目录
注意:“lib/modules/5.4.31/”在虚拟机中是位于“/home/zgq/linux/nfs/rootfs/”目录下,但在开发板中,却是位于根目录中。
输入“ls -l”查看“ButtonLED_APP,MyButton.ko和MyLED.ko”是否存在
输入“depmod”,驱动在第一次执行时,需要运行“depmod”
输入“modprobe MyButton.ko”,加载“MyButton.ko”模块
输入“modprobe MyLED.ko”,加载“MyLED.ko”模块
输入“lsmod”查看有哪些驱动在工作
输入“ls /dev/EXTI3Driver -l回车”,发现节点文件“/dev/EXTI3Driver”
输入“ls /dev/LEDDriver -l回车”,发现节点文件“/dev/LEDDriver”
输入“./ButtonLED_APP /dev/EXTI3Driver /dev/LEDDriver回车”
按下按钮,等待串口输出“KEY0 Press, value = 0X1”,同时LED会亮。
14、测试结果
相关文章:
Linux第90步_异步通知实验
“异步通知”的核心就是信号,由“驱动设备”主动报告给“应用程序”的。 1、添加“EXTI3.c” #include "EXTI3.h" #include <linux/gpio.h> //使能gpio_request(),gpio_free(),gpio_direction_input(), //使能gpio_direction_output(),gpio_get_v…...
elasticdump之python脚本
参考文章目录 elasticdump之shell备份脚本 前言 在企业实际生产环境中,避免不了要对es集群进行迁移、数据备份与恢复,以此来确保数据的可用性及完整性。因此,就涉及到了数据备份与恢复。本章主要以elasticdumppython为主,实现es集群索引备…...
Hystrix应用:如何在Spring Boot中使用Hystrix?
Hystrix应用:如何在Spring Boot中使用Hystrix? 引言 在微服务架构的发展过程中,面对复杂的服务依赖和不可预见的系统故障,如何提升系统的容错能力成为了一个非常急迫且重要的能力。 由 Netflix(网飞)公司…...
js的常用方法
js的常用方法已经使用过的实例 JavaScript有许多基本方法,这些方法可用于执行各种操作,包括字符串操作、数组操作、数学运算等。以下是一些常用的JavaScript基本方法及简单示例: 一、字符串方法 1、toUpperCase():将字符串转换为…...
基于SpringBoot实现的在线拍卖系统
系统开发环境 编程语言:Java数据库:MySQL容器:Tomcat工具:IDEA/Ecilpse、Navicat、Maven 系统实现 管理员功能模块 首页 修改密码 用户管理 商品类型管理 拍卖商品 竞拍公告 轮播图 历史竞拍管理 竞拍订单管理 留言板管理 用户…...
React 组件生命周期对比:Class vs. 函数式
在 React 中,Class 组件和函数式组件的生命周期存在一些差异。通过对 React 中 Class 组件和函数式组件的生命周期进行对比,详细探讨了它们在设计哲学、生命周期管理和开发技巧上的异同。全面了解 React 中两种组件类型的生命周期特点,以及如…...
Ubuntu去除烦人的顶部【活动】按钮
文章目录 一、需求说明二、打开 extensions 网站三、安装 GNOME Shell 插件四、安装本地连接器五、安装 Hide Activities Button 插件六、最终效果七、卸载本地连接器命令参考 本文所使用的 Ubuntu 系统版本是 Ubuntu 22.04 ! 一、需求说明 使用 Ubuntu 的过程中,屏…...
Vue2(十五):replace属性、编程式路由导航、缓存路由组件、路由组件独有钩子、路由守卫、history与hash
一、router-link的replace属性 1、作用:控制路由跳转时操作浏览器历史记录的模式 2、浏览器的历史记录有两种写入方式:分别为push和replace,push是追加历史记录,replace是替换当前记录。路由跳转时候默认为push 3、如何开启repla…...
智慧污水井物联网远程监控案例
智慧污水井物联网远程监控案例 在当今数字化转型的浪潮中,智慧水务已成为城市基础设施建设的重要组成部分。其中,基于物联网技术的智慧污水井远程监控系统以其高效、精准、实时的特性,在提升污水处理效能、保障城市水环境安全、实现精细化管…...
程序员Java.vue,python前端后端爬虫开发资源分享
bat面试资料 bat面试题汇总 提取码:724z 更多资料...
PCL:基于法线微分分割
1.介绍 在三维点云处理中,法线微分分割(Difference of Normals,简称DoN)是一种常用的分割方法,用于将点云中的物体或者场景进行分割成不同的部分或者簇。通过计算点云中每个点的法线向量,以及法线向量的变化率(差异),可以有效地分割出具有明显形状差异的部分,从而实现…...
生产事故:线程管理不善诱发P0故障
背景 处于业务诉求,需要建立一个统一的调度平台,最终是基于 Dolphinscheduler 的 V1.3.6 版本去做二次开发。在平台调研建立时,这个版本是最新的版本 命运之轮开始转动 事故 表象 上班后业务部门反馈工作流阻塞,登录系统发现大…...
WPF —— GDI画板
定义绘制对象 Graphics g; 起始点坐标 Point start; 画笔颜色 Color c1 Color.Black; 是否开始绘制 当flagtrue开始绘制,结束绘 private void Form1_MouseDown(object sender, MouseEventArgs e) {if (e.Button MouseButtons.Left) //点击了鼠标左键{start …...
C++:基于范围的for循环
使用迭代器遍历容器在遍历的过程中需要给出容器的两端:开头(begin)和结尾(end),因为这种遍历方式不是基于范围来设计的。在基于范围的for循环中,不需要再传递容器的两端,循环会自动以…...
引领智能互联时代,紫光展锐赋能百业创新发展
随着5G技术的快速发展,各行各业对通信技术的需求也在不断升级。紫光展锐持续深耕5G垂直行业,不断推进5G标准演进,从R15到R16,再到R17,展锐携手生态合作伙伴,不断推出创新性解决方案,在5G RedCap…...
lv_micropython to download and building
想要在ESP32-C3使用Micropython开发GUI,所以需要编译lv_micropython,当前github上的版本是9.1.0。 一、开发环境 因为编译lv_micropython需要在linux系统下,但是我的电脑是windows系统,所以我在windows系统上安装了VMware虚拟机&…...
二叉树练习day.9
669.修剪二叉搜索树 链接:. - 力扣(LeetCode) 题目描述: 给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树 不应该 改变…...
2024年第十七届“认证杯”数学中国数学建模网络挑战赛B题思路
B题 神经外科手术的定位与导航 人的大脑结构非常复杂,内部交织密布着神经和血管,所以在大脑内做手术具有非常高的精细和复杂程度。例如神经外科的肿瘤切除手术或血肿清除手术,通常需要将颅骨打开一个(或几个)圆形窗口,将病变部位暴露在术野中。但当病变部位较深时,就必…...
【vue】slot 匿名插槽 / 具名插槽
slot父组件向子组件传递数据 匿名插槽–直接写 具名插槽–指定名称 父组件中 子组件中: 代码 App.vue <template><h2>App.vue</h2><!-- 匿名插槽 --><Header><a href"1234567890.com">1234567890</a>&…...
FFmpeg: 自实现ijkplayer播放器-02环境搭建
文章目录 安装环境项目工程配置库文件 安装环境 IDE: Qt5.12 库: ffmpeg-4.2.1-win32SDL 项目工程配置 pro文件 TEMPLATE app TARGET SimpleIJKPlayer DESTDIR bin QT core gui widgets #CONFIG debug #DEFINES _UNICODE WIN64 QT_WIDGETS_LIBwin32 { LIBS -L$$PW…...
Redis从入门到精通(十七)多级缓存(二)Lua语言入门、OpenResty集群的安装与使用
文章目录 前言6.4 Lua语法入门6.4.1 初识Lua6.4.2 Hello World6.4.3 变量6.4.3.1 Lua的数据类型6.4.3.2 声明变量 6.4.4 循环6.4.5 函数6.4.6 条件控制 6.5 实现多级缓存6.5.1 安装和启动OpenResty6.5.2 实现ajax请求反向代理至OpenResty集群6.5.2.1 反向代理配置6.5.2.2 OpenR…...
pytest常用钩子函数
1、什么叫钩子函数 在Pytest框架中,钩子函数是一种允许用户扩展或者自定义测试执行过程的机制。钩子函数允许用户在测试的不同阶段插入自定义的代码,以实现特定的行为,操作或处理。这种插入式的机制使得Pytest具有高度的灵活性和扩展性。 如…...
.Net <% %>
<% %> 语法 : <% import namespace"system.data"%> 用来导入后台命名空间 指令用于指定当页和用户控件编译器处理 ASP.NET Web 窗体页 (.aspx) 和用户控件 (.ascx) 文件时所使用的设置。<% %> 语法 : <% name %> <% getstr() %&g…...
【C语言__编译和链接__复习篇2】
目录 前言 一、翻译环境和运行环境 二、翻译环境 2.1 预处理 2.1 编译 2.1.1 词法分析 2.1.2 语法分析 2.1.3 语义分析 2.2 汇编 2.3 链接 三、运行环境 四、简答主线问题 前言 本篇主要讨论以下问题: 主线问题: 1. 源文件(.c)如何转换成(.exe)文件…...
Jmeter —— 自动录制脚本
1、Jmeter配置 1.1新增一个线程组 1.2Jmeter中添加HTTP代理 1.3配置HTTP代理服务器 修改端口 修改Target Cintroller(目标控制器) 修改Grouping(分组) 编辑录制中的包含和排除 在“URL Patterns to include包含模式”中填入.*(123456).*用以过滤请求地址中不包含123456的请求…...
使用python互相转换AVI、MP4、GIF格式视频文件
一、AVI文件转MP4文件 要将AVI格式的视频转换为 MP4,你可以使用 Python的 moviepy 库。以下是一个示例代码,用于将 AVI 文件转换为 MP4 文件: from moviepy.editor import VideoFileClip# 读取 AVI 文件 clip VideoFileClip("input.a…...
11 Php学习:函数
PHP 内建函数Array 函数 PHP Array 函数是 PHP 核心的组成部分。无需安装即可使用这些函数。 创建 PHP 函数 当您需要在 PHP 中封装一段可重复使用的代码块时,可以使用函数。下面详细解释如何创建 PHP 函数并举例说明。 创建 PHP 函数的语法 PHP 函数的基…...
查询电脑用户名和组信息
在命令行里查看电脑名: c:\>hostname 在命令行里,查看组信息: # 显示本地所有的用户组 c:\>net localgroup #显示administrators组包含的用户信息 c:\>net localgroup administrators # 比如我的显示信息: C:\>ne…...
【Godot4.2】CanvasItem绘图函数全解析 - 9.绘制表格
概述 之前介绍TextLine和TextParagraph的时候,提到了用制表符和设定列宽形式来绘制简易表格,但是很明显,单纯使用此种方式很难获得对表格的精确控制。 所以对于表格绘制问题,我决定单独开坑,单独深入研究。 目前比较…...
部署HDFS集群(完全分布式模式、hadoop用户控制集群、hadoop-3.3.4+安装包)
目录 前置 一、上传&解压 (一 )上传 (二)解压 二、修改配置文件 (一)配置workers文件 (二)配置hadoop-env.sh文件 (三)配置core-site.xml文件 &…...
绵阳做网站的有哪些/大型集团网站建设公司
背景 在服务器上安装了服务,其中有使用chromedp来进行网页截屏 在云服务器使用chromedb包需要事先安装chrome ##下载源加入到系统的源列表 sudo wget http://www.linuxidc.com/files/repo/google-chrome.list -P /etc/apt/sources.list.d/##导入谷歌软件公钥 wget…...
胶州家园网站建设/网络营销推广工具有哪些?
你真的需要分布式锁吗? 用到分布式锁说明遇到了多个进程共同访问同一个资源的问题。一般是在两个场景下会防止对同一个资源的重复访问: **提高效率。**比如多个节点计算同一批任务,如果某个任务已经有节点在计算了,那其他节点就不…...
提供手机自适应网站公司/如何创建自己的网站
欢迎关注头条号:Java小野猫web项目部署到tomcat上之后,有时需要打断点单步调试,如果用的是Intellij idea,可以通过如下方法实现:开启debug端口,启动tomcat以tomcat7.0.75为例,打开bin目录下的ca…...
四川住房建设厅网站首页/官方百度下载安装
💥💥💞💞欢迎来到本博客❤️❤️💥💥🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。⛳座右铭&#…...
preec网站/seo外链怎么做
为什么80%的码农都做不了架构师?>>> lampp下的mysql配置文件路径: /opt/lampp/etc/my.cnf 1 配置默认字符集 於mysqld项下添加 character-set-serverutf8 init_connect SET NAMES utf8 於client项下添加 default-character-set utf8 酱紫就ok了,尝试着用…...
常用设计网站有哪些软件/seo推广技术
希望和正在或者想要学习使用ISAAC-GYM的朋友一起有一个讨论群,尝试互帮互助,交流学习内容~ 目前刚开始尝试,不知道能不能建立起来,如果有意向请私戳!! ——2023.02 一、常用命令行选项 命令作用–help打印…...