10.2linux内核定时器实验(详细编程)_csdn
我尽量讲的更详细,为了关注我的粉丝!!!
本章使用通过设置一个定时器来实现周期性的闪烁 LED 灯,因此本章例程就使用到了一个LED 灯。
这里我们以毫秒为单位,所以要用msecs_to_jiffies这个函数。
如果是2s就是msecs_to_jiffies(2000)。
在linux函数定时器里面,判断它定时多久都是和系统的HZ作比较,2HZ就是2s,msecs_to_jiffies(2000)也是2s,毫秒为单位。
1.修改设备树文件
这一步骤在前面已经有过,所以跟着前面文章的同学应该很熟悉!
在 stm32mp157d-atk.dts 文件的根节点“/”下创建 LED 灯节点,节点名为“timer”,节点内容如下:
发现节点已经有了!
2.LED 灯驱动程序编写
之前的博客也是跟大家按照肌肉记忆来编写程序!一步一步按照思路来编写!
总代码会放在最后。
为了让大家更能明白,可以先对着总代码,进行对我的写代码流程更加详细得当!
放心,我也是一步一步打的代码,不是复制粘贴!!!
2.1头文件
比以前的代码添加了
#include <linux/timer.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
2.2驱动入口函数和出口函数及MODULE函数
2.3创建设备结构体
在跟之前一样,为了后续的注册设备字符,要在开发板内核里面显示的话,要申请设备号,便包括主设备号和次设备号;为了显得更专业一点,就要使用结构体来统一有关设备的相关父子类结构体信息。后面也可以添加更多的子类信息!
这里看总代码就知道我放在哪个位置了!
dev_t
是一个用于表示设备编号的数据类型,它在 <sys/types.h>
头文件中被定义。
timer
:这是一个变量名,它的类型是 struct timer_dev
。也就是说,timer
是一个 timer_dev
结构体类型的变量,通过这个变量可以存储和操作该结构体所定义的各种数据。
比如timer.devid,timer.major,timer.minor。等等。
后续有很多子类会在这里!
例如:
struct timer_dev { dev_t devid; // 设备号struct cdev cdev; // 字符设备对象 struct class *class; // 设备类(sysfs接口)struct device *device; // 设备节点(/dev目录下) int major; // 主设备号 int minor; // 次设备号 struct device_node *nd; // 设备树节点指针 int led_gpio; // GPIO编号(从设备树解析) };
2.4注册字符设备
这个很早就说了,在驱动入口就开始注册字符了,打开驱动就是注册!
33行是先让主设备号为0,防止以前的实验占用及未注销的设备号继续利用。
34-41行分别以给定设备号和未给定设备号的情况进行编程。其中timer.major,timer.minor便是利用了结构体内的变量子类,MKDEV
是 Linux 内核提供的一个宏,用于根据主设备号和次设备号生成一个 dev_t
类型的设备编号。其定义通常位于 <linux/kdev_t.h>
头文件中,register_chrdev_region
函数的原型定义在 <linux/fs.h>
头文件中,若是给了设备号就是用这个函数,从gpio.devid所赋值的设备号告诉内核进行相应设备号的注册。第二个位置就是设备号个数,第三个位置就是设备号名称。
alloc_chrdev_region
不需要手动指定主设备号,内核会自动分配一个未被使用的主设备号给驱动程序。&timer.devid是指针类型,原型是dev_t *dev
,要通过&取地址来存储设备号,第二个位置是起始的此设备号。
既然有创建字符设备,那就要有删除字符设备。
2.4.1补充设备结构体
完成字符设备注册后,就是在设备结构体中添加字符设备对象。
2.5初始化cdev
很显然cdev也是变量timer的子类timer.cdev。同时在内核里面cdev里面包括很多子类信息。
这里是内核里面的定义。不用管,只要配置cdev的子类即可。
对cdev进行初始化,这个是字符设备对象。
timer.cdev.owner = THIS_MODULE;
的作用- 内核利用这个关联来管理模块的引用计数。当有用户空间程序对该字符设备进行操作(如打开、读写)时,内核会增加当前模块的引用计数,以确保在设备被使用期间模块不会被卸载。当所有使用该设备的操作结束后,内核会减少引用计数,当引用计数降为 0 时,模块可以被安全卸载。
cdev_init
函数的核心功能是对一个 struct cdev
结构体实例进行初始化,并且把一组文件操作函数(由 struct file_operations
结构体定义)和该字符设备关联起来。这样一来,当用户空间的程序通过文件操作接口(像 open
、read
、write
等)对该字符设备进行操作时,内核就能调用相应的函数来处理这些请求。
2.5.1配置file_operations
同理,上面的&led_fops需要配置。
图片中还有些未进行修改,不用管,后面对字符添加操作集子类函数时改,先初始化cdev。
2.6添加cdev
cdev_add
函数的主要作用是将一个已经初始化好的字符设备(struct cdev
结构体实例)添加到内核的字符设备管理系统中,使该字符设备正式在系统中可用。一旦调用 cdev_add
成功,用户空间的程序就可以通过设备文件(通常位于 /dev
目录下)来访问这个字符设备。
即后续的App 可以执行到/dev
2.6.1注销字符设备对象
目前已经注册和注销了字符设备,同时也注册了字符设备对象,所以要进行字符设备对象的注销。
依然按照逻辑顺序进行注销。
2.7创建设备类和设备节点
2.7.1补充类与节点的定义
这两者作用在后续内核中创建/dev/timer。方便执行代码程序,传入到设备执行,关联设备号
2.7.2创建类
其中定义
#define class_create(owner, name)
owner和结构体内的owner都是module模块的,一般来说是THIS_MODULE;name就是设备名字。
2.7.3创建设备节点
以便访问设备设备树根节点的信息,与下文的获取设备节点相互呼应。
代码如下:
struct device *device_create(struct class *cls, struct device *parent,dev_t devt, void *drvdata,const char *fmt, ...);
struct device *parent
,指向父设备的指针。如果该设备没有父设备,可以传入 NULL
。
dev_t:即本文的timer.devid。
void *drvdata
,是一个指向设备驱动私有数据的指针。可以传入自定义的数据结构指针,用于在设备驱动中存储和管理设备相关的信息,这里同样给NULL。
const char *fmt- fmt
是一个格式化字符串,...
表示可变参数列表。类似于 printf
函数的用法,用于指定设备节点的名称。作用是指定要创建的设备节点在 /dev
目录下的名称,可以使用格式化字符串动态生成名称。
2.7.4补充错误信息(错误信息已经在前面写了)
IS_ERR
宏通过比较指针的值和 (unsigned long)-MAX_ERRNO
的大小来判断该指针是否为错误指针。如果指针的值大于等于 (unsigned long)-MAX_ERRNO
,则认为它是一个==错误指针,返回 true
;==否则返回 false
。
PTR_ERR:当 IS_ERR(timer.cls)
返回 true
时,代码会执行 return PTR_ERR(timer.cls);
,这会将 timer.cls
转换为对应的错误码并返回给调用者。调用者可以根据这个错误码进行相应的错误处理,例如打印错误信息、释放已经分配的资源等。
同样:
2.7.5注销类和设备节点
2.7.6配置操作集函数
这里不用write,read。用上static long timer_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
是一个典型的 Linux 内核驱动程序中的 ioctl
系统调用处理函数。ioctl
(Input/Output Control)是一种用于设备驱动程序和用户空间程序之间进行特殊控制操作的接口。这个函数允许用户空间程序向内核空间的设备驱动发送特定的命令(cmd
),并传递相关的数据(arg
),以实现对设备的各种控制。
复制上一节的操作函数集
static int timer_open(struct inode *inode, struct file *filp)
{int ret = 0;filp->private_data = &timerdev;return 0;
}
static long timer_unlocked_ioctl(struct file *filp,unsigned int cmd, unsigned long arg)
{struct timer_dev *dev = (struct timer_dev *)filp->private_data;return 0;
}
static int timer_release(struct inode *inode, struct file *filp)
{struct timer_dev *dev = filp->private_data;return 0;
}
/*操作集*/
static const struct file_operations timer_fops = {.owner = THIS_MODULE,.open = timer_open,.release = timer_release,.unlocked_ioctl = timer_unlocked_ioctl,
};
2.8获取设备节点(设备树属性)
这里和前面的文章不同,我们分别利用timer_init,led_init来编程,timer_init用于注册设备相关的,led_init用于gpio的属性配置。
2.8.1配置设备树结构体
2.8.2获取设备树节点
2.8.3获取led所对应的GPIO编号
这里知道了有关led驱动的gpio信息,仅仅是能知道信息,并没有驱动能力,所以要向内核申请权限来驱动gpio口。
2.8.4申请IO
相应需要注销函数时释放,不然占用资源。
2.8.5使用IO,设置为输出
2.8.6配置错误处理
按照以前的习惯,从后面进行处理错误信息,按照逻辑!!!
timer_init:
led_init:
2.9初始化自旋锁
这里我们用自旋锁来保证需要的代码不会受到中断的干扰!
因为这里用的是线程,所以用
spin_lock_irqsave(&gpioled.lock, flags);/*上锁*/
spin_unlock_irqrestore(&gpioled.lock, flags);/解锁/
//这是为了防止在持有锁期间被中断服务程序打断,避免出现死锁或数据不一致的问题
2.10配置定时器
2.10.1初始化定时器
这里应用timer_function是回调函数。
/*
`通过 mod_timer 函数设置了定时器的到期时间后,一旦系统时钟的节拍数(jiffies)`
`达到了设定的到期时间,内核就会触发定时器,进而调用注册的回调函数 timer_function,后面会不断去执行timer_function`
*/
知道了回调函数的作用,直接可以在这写定时器周期执行想要实现的功能!
其中sta是静态变量,sta虽然初始化为整数变量,会保留上一次调用结束的值。
后面不停的执行实现led灯的反转。不过也要等时间过后才会执行。
2.11配置操作函数
46行代码定义定时器周期为1s,单位用的是ms,所以后面用msecs_to_jiffies(timerperiod),跟HZ作比较,jiffies/HZ=1000的时候就是1s。
48行代码初始化led_init,y因为在timer_init中并没有初始化led相关代码,所以在这里初始化led。
static long timer_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
是一个典型的 Linux 内核驱动程序中的 ioctl
系统调用处理函数。ioctl
(Input/Output Control)是一种用于设备驱动程序和用户空间程序之间进行特殊控制操作的接口。这个函数允许用户空间程序向内核空间的设备驱动发送特定的命令(cmd
),并传递相关的数据(arg
),以实现对设备的各种控制。
所以可以利用switch(cmd)来执行特定的命令,传递相关的数据。
就是允许App(用户空间程序),./App /dev/传递相关数据arg。
timer_exit注销了timer字符设备相关的代码,但是并没有释放gpio相关的信息,释放资源。
所以这里就是在timer_release执行gpio相关代码,这里也关闭定时器!
这里同时删除定时器!
2.12程序效果
这里实现了打开定时器,改变定时器周期,2s,0.5s。
2.13总代码
timer.c
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>#define TIMER_CNT 1
#define TIMER_NAME "timer"
#define CLOSE_CMD (_IO(0XEF, 0x1)) /* 关闭定时器 */
#define OPEN_CMD (_IO(0XEF, 0x2)) /* 打开定时器 */
#define SETPERIOD_CMD (_IO(0XEF, 0x3)) /* 设置定时器周期命令*//*timer设备结构体*/
struct timer_dev{dev_t devid;//设备号int major;//主设备号int minor;//次设备号struct cdev cdev;//关联设备,字符设备对象struct class *class;//设备类struct device *device;//设备节点struct device_node *nd;//设备树节点指针int led_gpio; /*led所使用的GPIO编号*/spinlock_t lock; /*定义自旋锁*/struct timer_list timer; /*定义一个定时器*/int timeperiod; /*定时周期,单位为ms*/
};
struct timer_dev timerdev;//设备/*初始化led*/
static int led_init(void)
{int ret;/*1.获取设备节点*/timerdev.nd = of_find_node_by_path("/gpioled");//根节点下的gpioledif(timerdev.nd == NULL){printk("timerdev node not find!\r\n");ret = -EINVAL;goto fail_findnode;}/*2.获取led下的所对应gpio编号*/timerdev.led_gpio = of_get_named_gpio(timerdev.nd,"led-gpio",0);if(timerdev.led_gpio < 0){printk("can't find led gpio\r\n");ret = -EINVAL;goto fail_findnode;}printk("led gpio num = %d\r\n",timerdev.led_gpio);/*3.申请IO*/ret = gpio_request(timerdev.led_gpio,"led-gpio");if(ret){printk("Failed to request the led gpio!\r\n");ret = -EINVAL;goto fail_findnode;}/*4.设置IO为输出*/ret = gpio_direction_output(timerdev.led_gpio, 1);//默认关闭ledif (ret < 0) {ret = -EINVAL;goto fail_setoutput;}return 0;
fail_setoutput:gpio_free(timerdev.led_gpio);
fail_findnode:device_destroy(timerdev.class,timerdev.devid);return ret;
}static int timer_open(struct inode *inode, struct file *filp)
{int ret = 0;filp->private_data = &timerdev;timerdev.timeperiod = 1000; /*默认周期为1s,这里跟系统的单位HZ比较节拍率,不用我们管,是函数调用内部自动完成,jiffies和HZ比较,我们只要找换算单位即可*/ret = led_init(); /*初始化 LED IO*/if(ret < 0){return ret;}return 0;
}static long timer_unlocked_ioctl(struct file *filp,unsigned int cmd, unsigned long arg)
{struct timer_dev *dev = (struct timer_dev *)filp->private_data;int timerperiod;unsigned long flags;switch (cmd) {case CLOSE_CMD: /* 关闭定时器 */del_timer_sync(&dev->timer);break;case OPEN_CMD: /* 打开定时器 *//*这里其实可以mod_timer(&dev->timer, jiffies +msecs_to_jiffies(dev->timeperiod);但是考虑到如果执行命令过快时,dev->timeperiod会不断变化,线程还没解锁dev->timeperiod就发生改变了。*/spin_lock_irqsave(&dev->lock, flags);//线程上锁timerperiod = dev->timeperiod;//保护这个代码spin_unlock_irqrestore(&dev->lock, flags);//线程解锁mod_timer(&dev->timer, jiffies +msecs_to_jiffies(timerperiod));break;case SETPERIOD_CMD: /* 设置定时器周期 */spin_lock_irqsave(&dev->lock, flags);//线程上锁dev->timeperiod = arg;//保护这个代码spin_unlock_irqrestore(&dev->lock, flags);//线程解锁mod_timer(&dev->timer, jiffies + msecs_to_jiffies(arg));break;default:break;}return 0;
}static int timer_release(struct inode *inode, struct file *filp)
{struct timer_dev *dev = filp->private_data;gpio_set_value(dev->led_gpio, 1); /* APP 结束的时候关闭 LED */gpio_free(dev->led_gpio); /* 释放 LED 的GPIO*/del_timer_sync(&dev->timer); /* 关闭定时器 */return 0;
}/*操作集*/
static const struct file_operations timer_fops = {.owner = THIS_MODULE,.open = timer_open,.release = timer_release,.unlocked_ioctl = timer_unlocked_ioctl,
};/*定时器回调函数*/
void timer_function(struct timer_list *arg)
{//这个函数作用就是定时周期的执行程序:比如周期翻转led的状态
/* from_timer 是个宏,可以根据结构体的成员地址,获取到这个结构体的首地址。第一个参数表示结构体,第二个参数表示第一个参数里的一个成员,第三个参数表
示第二个参数的类型,得到第一个参数的首地址。
*/
/*
通过 mod_timer 函数设置了定时器的到期时间后,一旦系统时钟的节拍数(jiffies)
达到了设定的到期时间,内核就会触发定时器,进而调用注册的回调函数 timer_function
*/struct timer_dev *dev = from_timer(dev, arg, timer);static int sta = 1;/*静态局部变量的生命周期从程序开始运行到程序结束,它在函数的多次调用之间会保留其值。也就是说,当 timer_function 函数第一次被调用时,sta 被初始化为 1,后续每次调用该函数时,sta 不会再次被初始化,而是保留上一次调用结束时的值。*/int timerperiod;unsigned long flags;sta = !sta; /* 每次都取反,实现 LED 灯反转 */gpio_set_value(dev->led_gpio, sta);/*重启定时器*/spin_lock_irqsave(&dev->lock, flags);//上锁timerperiod = dev->timeperiod;spin_unlock_irqrestore(&dev->lock, flags);//解锁mod_timer(&dev->timer, jiffies +msecs_to_jiffies(timerperiod));
}/*字符设备驱动入口函数*/
static int __init timer_init(void)
{int ret;/*1.注册字符设备*/timerdev.major = 0;if(timerdev.major){//若给定了主设备号timerdev.devid = MKDEV(timerdev.major,0);ret = register_chrdev_region(timerdev.devid,TIMER_CNT,TIMER_NAME);}else{//若未给定主设备号ret = alloc_chrdev_region(&timerdev.devid,0,TIMER_CNT,TIMER_NAME);timerdev.major = MAJOR(timerdev.devid);timerdev.minor = MINOR(timerdev.devid);}if(ret < 0){//注册字符设备驱动失败goto fail_devid;}printk("major = %d,minor = %d,CNT = %d,NAME = %s\r\n",timerdev.major,timerdev.minor,TIMER_CNT,TIMER_NAME);/*2.初始化cdev*/timerdev.cdev.owner = THIS_MODULE;cdev_init(&timerdev.cdev,&timer_fops);/*3.添加cdev*/ret = cdev_add(&timerdev.cdev,timerdev.devid,TIMER_CNT);//可以执行/devif(ret < 0){goto fail_cdev;}/*4.创建类*/timerdev.class = class_create(THIS_MODULE,TIMER_NAME);//创建/dev/timerif(IS_ERR(timerdev.class)){ret = PTR_ERR(timerdev.class);goto fail_class;}/*5.创建设备节点*/timerdev.device = device_create(timerdev.class,NULL,timerdev.devid,NULL,TIMER_NAME);if(IS_ERR(timerdev.device)){ret = PTR_ERR(timerdev.device);goto fail_device;}/*6.初始化自旋锁*/spin_lock_init(&timerdev.lock);/*7.初始化timer设置定时器处理函数,还未设置周期,所有不会激活定时器*/timer_setup(&timerdev.timer, timer_function, 0);return 0;fail_device:class_destroy(timerdev.class);
fail_class:cdev_del(&timerdev.cdev);
fail_cdev:unregister_chrdev_region(timerdev.devid,TIMER_CNT);
fail_devid:return ret;
}/*字符设备驱动出口函数*/
static void __exit timer_exit(void)
{/*删除timer*/del_timer_sync(&timerdev.timer);
#if 0del_timer(&timerdev.tiemr);
#endif/*释放IO*/gpio_free(timerdev.led_gpio);/*注销设备节点*/device_destroy(timerdev.class,timerdev.devid);/*注销设备类*/class_destroy(timerdev.class);/*注销字符设备对象驱动*/cdev_del(&timerdev.cdev);/*注销字符设备驱动*/unregister_chrdev_region(timerdev.devid,TIMER_CNT);
}module_init(timer_init);
module_exit(timer_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("chensir");
MODULE_INFO(intree,"Y");
LEDAPP.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 <sys/ioctl.h>/*
*argc:应用程序参数个数
*argv[]:具体的参数内容,字符串形式
*./timerApp <fliename>
*./timerApp /dev/timer
*./timerApp /dev/timer
*//* 命令值 */#define CLOSE_CMD (_IO(0XEF, 0x1)) /* 关闭定时器 */#define OPEN_CMD (_IO(0XEF, 0x2)) /* 打开定时器 */#define SETPERIOD_CMD (_IO(0XEF, 0x3)) /* 设置定时器周期命令 *//** @description : main 主程序* @param - argc : argv 数组元素个数* @param - argv : 具体参数* @return : 0 成功;其他 失败*/int main(int argc, char *argv[]){int fd, ret;char *filename;unsigned int cmd;unsigned int arg;unsigned char str[100];if (argc != 2) {printf("Error Usage!\r\n");return -1;}filename = argv[1];fd = open(filename, O_RDWR);if (fd < 0) {printf("Can't open file %s\r\n", filename);return -1;}while (1) {printf("Input CMD:");ret = scanf("%d", &cmd);if (ret != 1) { /* 参数输入错误 */fgets(str, sizeof(str), stdin); /* 防止卡死 */}if(4 == cmd) /* 退出 APP */goto out;if(cmd == 1) /* 关闭 LED 灯 */cmd = CLOSE_CMD;else if(cmd == 2) /* 打开 LED 灯 */cmd = OPEN_CMD;else if(cmd == 3) {cmd = SETPERIOD_CMD; /* 设置周期值 */printf("Input Timer Period:");ret = scanf("%d", &arg);if (ret != 1) { /* 参数输入错误 */fgets(str, sizeof(str), stdin); /* 防止卡死 */}}ioctl(fd, cmd, arg); /* 控制定时器的打开和关闭 */}out:close(fd);}
makefile
KERNELDIR := /home/chensir/linux/atk-mp1/linux/my_linux/linux-5.4.31
CURRENT_PATH := $(shell pwd)
obj-m := timer.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
相关文章:
10.2linux内核定时器实验(详细编程)_csdn
我尽量讲的更详细,为了关注我的粉丝!!! 本章使用通过设置一个定时器来实现周期性的闪烁 LED 灯,因此本章例程就使用到了一个LED 灯。 这里我们以毫秒为单位,所以要用msecs_to_jiffies这个函数。 如果是2s就…...
机器学习——正则化、欠拟合、过拟合、学习曲线
过拟合(overfitting):模型只能拟合训练数据的状态。即过度训练。 避免过拟合的几种方法: ①增加全部训练数据的数量(最为有效的方式) ②使用简单的模型(简单的模型学不够,复杂的模型学的太多&am…...
Java多线程与高并发专题——阻塞和非阻塞队列的并发安全原理是什么?
引入 之前我们探究了常见的阻塞队列的特点,在本文我们就以 ArrayBlockingQueue 为例,首先分析 BlockingQueue ,也就是阻塞队列的线程安全原理,然后再看看它的兄弟——非阻塞队列的并发安全原理。 ArrayBlockingQueue 源码分析 …...
git 撤销某次提交的上交到远程服务器的commit提交,此提交后面的commit需要保留【deeepseek生成】
核心思路 使用 git rebase -i 重写提交历史,删除目标提交后强制推送到远程(需谨慎操作)。 操作步骤 1. 确认要删除的提交位置 # 查看提交历史(找到要删除的提交哈希,例如 a1b2c3d) git log --oneline查看提…...
docker composeyaml文件,什么是swap-space,内存不足硬盘来凑,--ipc=host,yaml文件、环境变量、容器报警健康检查
--swap-space 参数明确针对的是系统内存(RAM),与显存(GPU Memory)无关。以下是关键区分: 内存(RAM) vs 显存(GPU Memory) 类型内存(RAMÿ…...
tsfresh:时间序列特征自动提取与应用
tsfresh:时间序列特征自动提取与应用 本文系统介绍了 tsfresh 技术在 A 股市场数据分析与量化投资中的应用。从基础特征提取到高级策略开发,结合实战案例,详细讲解了如何利用 tsfresh 构建量化投资策略,并优化风险控制,…...
【A2DP】深入解读A2DP中通用访问配置文件(GAP)的互操作性要求
目录 一、模式支持要求 1.1 发现模式 1.2 连接模式 1.3 绑定模式 1.4 模式间依赖关系总结 1.5 注意事项 1.6 协议设计深层逻辑 二、安全机制(Security Aspects) 三、空闲模式操作(Idle Mode Procedures) 3.1 支持要求 …...
CUDA编程之内存
CUDA的内存类型有全局内存、共享内存、常量内存、纹理内存、本地内存、寄存器等。我们需要分别了解它们的特点和使用场景。在CUDA编程中,合理利用各种内存类型对性能优化至关重要。 1. 全局内存(Global Memory) 特点:设…...
【Agent实战】货物上架位置推荐助手(RAG方式+结构化prompt(CoT)+API工具结合ChatGPT4o能力Agent项目实践)
本文原创作者:姚瑞南 AI-agent 大模型运营专家,先后任职于美团、猎聘等中大厂AI训练专家和智能运营专家岗;多年人工智能行业智能产品运营及大模型落地经验,拥有AI外呼方向国家专利与PMP项目管理证书。(转载需经授权) 目录 结论 效果图示 1.prompt 2. API工具封…...
ffmpeg面试题整理
1. 基础概念 问题:FFmpeg 是什么?它的核心功能有哪些? 编解码:支持几乎所有音视频格式(如 H.264, AAC, MP3)。转换:在不同容器格式之间转换(如 MP4 → MKV)。流处理&…...
Idea运行项目报错:java.lang.OutOfMemoryError: Java heap space 解决方法
问题描述 Maven构建的时候,一直报错java.lang.OutOfMemoryError: Java heap space 尝试解决 找了几个JAVA高级小伙伴,一起去百度了各种可能,设置内存大小,发现都不行,还不断的重装了IDEA,以为是这个版本…...
解决 Linux /dev/mapper/ubuntu--vg-ubuntu--lv 磁盘空间不足的问题
解决 Linux /dev/mapper/ubuntu–vg-ubuntu–lv 磁盘空间不足的问题 https://blog.csdn.net/weixin_47908992/article/details/139882219 查看LVM卷组的信息 vgdisplay rootubuntu:~# vgdisplay--- Volume group ---VG Name ubuntu-vgSystem ID Fo…...
前端UI编程基础知识:基础三要素(结构→表现→行为)
以下是重新梳理的前端UI编程基础知识体系,结合最新技术趋势与实战要点,以更适合快速掌握的逻辑结构呈现: 一、基础三要素(结构→表现→行为) 1. HTML5 核心能力 • 语义化标签:<header>, <nav&g…...
Trae:与AI结伴,开启编程新体验
Trae:与AI结伴,开启编程新体验 在数字化时代,编程已经成为推动技术发展的核心力量。然而,随着项目复杂度的增加,开发者面临着诸多挑战,例如代码编写效率低下、代码质量难以把控等。如今,Trae作…...
如何用正则表达式爬取古诗文网中的数据(python爬虫)
一、了解正则表达式的基本内容: 什么是正则表达式 正则表达式(Regular Expression,简称 regex)是一种用于匹配字符串的模式。它通过特定的语法规则,可以高效地搜索、替换和提取文本中的特定内容。正则表达式广泛应用于…...
深度学习 Deep Learning 第1章 深度学习简介
第1章 深度学习简介 概述 本章介绍人工智能(AI)和深度学习领域,讨论其历史发展、关键概念和应用。解释深度学习如何从早期的AI和机器学习方法演变而来,以及如何有效解决之前方法无法应对的挑战。 关键概念 1. 人工智能的演变 …...
ByteByteGo学习笔记:通知系统设计
引言 在当今这个信息爆炸的时代,通知系统已经成为了现代应用程序中不可或缺的重要组成部分。无论是突发新闻的即时推送、产品更新的及时告知、促销活动的精准触达,还是用户交互的实时反馈,通知都扮演着至关重要的角色。一个高效、可靠、可扩…...
[设计模式]1_设计模式概览
摘要:设计模式原则、设计模式的划分与简要概括,怎么使用重构获得设计模式并改善代码的坏味道。 本篇作概览与检索用,后续结合源码进行具体模式深入学习。 目录 1、设计模式原理 核心原则(语言无关) 本质原理图 原…...
Python + Qt Designer构建多界面GUI应用程序:Python如何调用多个界面文件
引言 Qt Designer是一个用户友好的图形用户界面设计工具,它可以帮助开发人员通过拖放的方式快速创建界面。在实际开发中,往往需要设计多个界面文件,并在Python代码中进行统一管理和使用。本文将介绍如何在Python中使用Qt Designer设计好的多…...
AGI大模型(7):提示词应用
1 生成数据 LLM具有⽣成连贯⽂本的强⼤能⼒。使⽤有效的提示策略可以引导模型产⽣更好、更⼀致和更真实的响应。LLMs还可以特别有⽤地⽣成数据,这对于运⾏各种实验和评估⾮常有⽤。例如,我们可以使⽤它来为情感分类器⽣成快速样本,如下所示: 提示: ⽣成10个情感分析的范…...
【倒霉bug2025】找不到vc_runtimeMinimum_x64.msi
今天是倒霉的一天,当喉咙痛到无法出门玩耍的我打开steam准备开始玩《冰封世界》时,游戏启动直接报错 在选择安装之后弹出一个经典窗口 然后在C:\ProgramData\PackageCache中找msi到位置点击确定继续报错说msi版本不对 上网一搜,找不到vc_ru…...
什么是强哈希算法pbkdf2(Password-Based Key Derivation Function)
文章目录 什么是pbkdf2使用场景 在线工具 什么是pbkdf2 维基百科:https://zh.wikipedia.org/zh-cn/PBKDF2 PBKDF2(Password-Based Key Derivation Function 2)是一种基于密码的密钥派生函数。它的主要作用是从密码和盐(salt&…...
Python 基础语法详解
一、变量和数据类型 变量 在 Python 中,变量无需声明类型,直接赋值即可。变量名区分大小写。 # 整数类型 age 25 print(age) # 输出:25# 浮点数类型 height 1.75 print(height) # 输出:1.75# 字符串类型 name "张三&…...
AI Agent 时代开幕-Manus AI与OpenAI Agent SDK掀起新风暴
【本周AI新闻: AI Agent 时代开幕-Manus AI与OpenAI Agent SDK掀起新风暴】 https://www.bilibili.com/video/BV1bkQyYCEvQ/?share_sourcecopy_web&vd_source32ed33e1165d68429b2e2eb4749f3f26 最近AI圈子里最火的话题非Manus莫属!这款由中国武汉创业公司“蝴…...
为什么会出现redis数据库?redis是什么?
什么是 Redis? 为什么要用 Redis? 下面我将从 Redis 出现的背景、Redis 的解决方案个来回答。 1、Redis 出现的背景 互联网的应用越来越多,例如社交网络、电商、实时服务发展的十分迅速,这就导致了传统技术栈(如关系型数据库)…...
每日一题---dd爱框框(Java中输入数据过多)
dd爱框框 实例: 输入: 10 20 1 1 6 10 9 3 3 5 3 7 输出: 3 5 这道题要解决Java中输入的数过多时,时间不足的的问题。 应用这个输入模板即可解决: Java中输入大量数据 import java.util.*; import java.io.*;pu…...
Flink-学习路线
最近想学习一下Flink,公司的实时需求还是不少的,因此结合ai整理了一份学习路线,记录一下。 当然,公司也有Scala版本Flink框架,也学习了一下。这里只说Java版本 1. Java基础 目标: 掌握Java编程语言的基础知识。 内容…...
一次Milvus迁移的记录
前言 希望把Linux上生产环境中使用docker compose运行的milvus迁移到本地(mac os)的docker compose中 操作过程 找到了官方有两个相关的项目: https://github.com/zilliztech/milvus-backup https://github.com/zilliztech/vts 但是…我都没用,因为使…...
矩阵的转置
对于的矩阵,使用两个指针变量,可以方便实现(i,j)处元素与(j,i)处元素交换位置。令指针Arow&A[i][0],则Arow[j]可实现对第i行j列元素的访问。令指针Bptr&A[0][i],则*Bptr就可以访问(0,i)处元素,然后,…...
使用 VLOOKUP 和条件格式在 Excel 中查找并标红匹配的串号
使用 VLOOKUP 和条件格式在 Excel 中查找并标红匹配的串号 你的步骤非常详细且清晰,能够帮助用户在 Excel 中通过 VLOOKUP 和条件格式来查找并标红匹配的串号。以下是对你提供的步骤的简要总结和补充说明: 1. 添加“是否匹配”列 在 a.xlsx 中新增一列…...
Python Matplotlib面试题精选及参考答案
目录 绘制函数 y=2x+5 在区间 [1,10] 的折线图,设置标题和坐标轴标签 在同一图中绘制 sin (x) 和 cos (x) 曲线,添加图例和网格线(x∈[0,2π]) 绘制分段函数:当 x<0 时 y=0,x≥0 时 y=x,设置不同线段颜色 绘制带数据点的折线图,使用红色虚线样式和圆形标记(数据…...
在线 SQL 转 SQLAlchemy:一键生成 Python 数据模型
一款高效的在线 SQL 转 SQLAlchemy 工具,支持自动解析 SQL 语句并生成 Python SQLAlchemy 模型代码,适用于数据库管理、后端开发和 ORM 结构映射。无需手写 SQLAlchemy 模型,一键转换 SQL 结构,提升开发效率,简化数据库…...
基于自定义线程池手写一个异步任务管理器
我们在后端执行某些耗时逻辑操作时往往会导致长时间的线程阻塞,在这种情况之下,我们往往会引一条异步线程去处理这些异步任务,如果每次都创建新的线程来处理这些任务,不仅会增加代码冗余,还可能造成线程管理混乱&#…...
基恩士PLC编程小技巧八:脚本过长如何实现换行及替换
基恩士PLC编程小技巧八:脚本过长如何实现换行? 一、问题点 我们在使用基恩士编程软件KV STUDIO 进行脚本编程时,经常遇到这样的问题:脚本的一行过长,程序不好阅读及维护。 IF MR1000 OR MR1001 OR MR1002 OR MR1003 OR…...
每日一题---数组中两个字符串的最小距离
数组中两个字符串的最小距离 给定一个字符串数组strs,再给定两个字符串str1和str2,返回在strs中str1和str2的最小距离,如果str1或str2为null,或不在strs中,返回-1。 链接:数组中两个字符串的最小距离__牛…...
【PTA题目解答】7-1利用STL比较数据大小并排序(15分)c++
1.题目: 2.算法原理 根据题目要求,模拟即可,set容器会帮我们把插入的数自动排序好 题目说输入非整型数据就停止,不用特意判断输入的数据是整型还是非整型,如果用户输入的是字符(例如 a)&#…...
如何用Deepseek制作流程图?
使用Deepseek制作流程图,本质上是让AI根据你的需求,生成相关流程图的代码,然后在流程图编辑器中渲染,类似于Python一样,ChatGPT可以生成代码,但仍需在IDE中执行。 你知道绘制流程图最高效的工具是什么吗&a…...
【09】单片机编程核心技巧:变量赋值,从定义到存储的底层逻辑
【09】单片机编程核心技巧:变量赋值,从定义到存储的底层逻辑 🌟 核心概念 单片机变量的定义与赋值是程序设计的基础,其本质是通过 RAM(随机存储器) 和 ROM(只读存储器) 的协作实现…...
vscode python相对路径的问题
vscode python相对路径的问题 最近使用使用vscode连接wsl2写python时,经常遇到找不到包中的方法的问题,最终发现vscode在执行python代码时目录不是从当前python文件开始算起,而是从当前工作区的目录开始算起,比如说我打开的是/ho…...
C语言中的指针与函数
引言 在C语言编程中,指针是一个非常重要且强大的概念。它不仅帮助我们高效地管理内存,还能提升程序的灵活性和性能。而指针与函数的结合使用,是C语言中非常常见且极具挑战性的一个话题。正确理解和使用指针与函数的关系,不仅能帮助程序员提高代码质量,还能优化程序的执行…...
深度学习-服务器训练SparseDrive过程记录
1、cuda安装 1.1 卸载安装失败的cuda 参考:https://blog.csdn.net/weixin_40826634/article/details/127493809 注意:因为/usr/local/cuda-xx.x/bin/下没有卸载脚本,很可能是apt安装的,所以通过执行下面的命令删除: a…...
理解langchain langgraph 官方文档示例代码中的MemorySaver
以下是langchain v0.3官方示例代码 from langgraph.checkpoint.memory import MemorySaver from langgraph.graph import START, MessagesState, StateGraph# 可以理解为:定义一个流程,这个流程中用到的数据类型是Messages。 <---定义一个有向图&…...
JumpServer基础功能介绍演示
堡垒机可以让运维人员通过统一的平台对设备进行维护,集中的进行权限的管理,同时也会对每个操作进行记录,方便后期的溯源和审查,JumpServer是由飞致云推出的开源堡垒机,通过简单的安装配置即可投入使用,本文…...
Spring @Bean注解使用场景二
bean:最近在写一篇让Successfactors顾问都能搞明白的sso的逻辑的文章,所以一致在研究IAS的saml2.0的协议,希望用代码去解释SP、idp的一些概念,让顾问了解SSO与saml的关系,在github找代码的时候发现一些代码的调用关系很难理解&…...
创业者认知、思辨、成长指南
一、为什么要创业? 1、因为没有家产继承和家庭关系,不能躺平; 比如父母留下了大量的财富,靠钱生钱吃利息,收租,做做投资这些形式,就可以活得很好; 再比如父母或者血亲有资源&#…...
ECharts中Map(地图)样式配置、渐变色生成
前言 ECharts是我们常用的图表控件,功能特别强大,每次使用都要查API比较繁琐,这里就记录开发中常用的配置。 官网:https://echarts.apache.org/handbook/zh/get-started 配置项:https://echarts.apache.org/zh/opti…...
PostgreSQL存储管理体系结构学习笔记2
1.表和元组的组织方式 在PostgreSQL中,同一个表中的元组按照创建顺序依次插入到表文件中。元组之间不进行关联,这样的表文件称之为堆文件。PostgreSQL系统中包含了四种堆文件:普通堆,临时堆,序列,TOAST表。…...
【PTA题目解答】7-3 字符串的全排列(20分)next_permutation
1.题目 给定一个全由小写字母构成的字符串,求它的全排列,按照字典序从小到大输出。 输入格式: 一行,一个字符串,长度不大于8。 输出格式: 输出所有全排列,每行一种排列形式,字典序从小到大。 输入样例…...
SOME/IP:用Python实现协议订阅、Offer、订阅ACK与报文接收
文章目录 前言一、代码层次二、详细代码1. eth_scapy_sd.py2、eth_scapy_someip.py3、network_define.py4、packet_define.py5、unpack_define.py6、someip_controller.py 前言 1、需要pip安装scapy库 2、需要修改根据实际情况配置network_define.py 3、执行someip_controller…...
嵌入式八股ARM篇
前言 ARM篇主要介绍一下寄存器和中断机制,至于汇编这一块…还请大家感兴趣自行学习 1.寄存器 R0 - R3 R4 - R11 寄存器 R0 - R3一般用作函数传参 R4 - R11用来保存程序运算的中间结果或函数的局部变量 在函数调用过程中 注意在发生异常的时候 cortex-M0架构会自动将R0-R3压入…...