Linux 内核定时器实验

news2024/12/26 23:16:16

目录

一、内核时间管理简介

二、内核定时器简介

三、驱动编写

1、修改makefile

 2、添加定义

 3、初始化led函数

4、添加调用 

5、初始化定时器与定时器处理函数

这部分代码如下

四、ioctl函数

 五、内核添加unlocked_ioctl 函数

1、添加设备操作集unlocked_ioctl成员 

 2、添加timepriod变量

 3、初始化timepriod

 4、添加unlocked_ioctl对应的函数

六、编写应用测试

1、添加宏定义

 2、使用ioctl函数

七、编译测试

                总体代码如下:

                驱动

                应用


一、内核时间管理简介

硬件定时器提供时钟源,时钟源的频率可以设置, 设置好以后就周期性的产生定时中断,系统使用定时中断来计时。中断周期性产生的频率就是系统频率,也叫节拍率(tick rate)(有的叫系统率),比如 1000Hz, 100Hz 等等说的就是系统节拍率。系统节拍率是可以通过Linux 内核设置的

高节拍率和低节拍率的优缺点:
①、高节拍率会提高系统时间精度,如果采用 100Hz 的节拍率,时间精度就是 10ms,采用
1000Hz 的话时间精度就是 1ms,精度提高了 10 倍。高精度时钟的好处有很多,对于那些对时
间要求严格的函数来说,能够以更高的精度运行,时间测量也更加准确。
②、高节拍率会导致中断的产生更加频繁,频繁的中断会加剧系统的负担, 1000Hz 和 100Hz
的系统节拍率相比,系统要花费 10 倍的“精力”去处理中断。中断服务函数占用处理器的时间增加,但是现在的处理器性能都很强大,所以采用 1000Hz 的系统节拍率并不会增加太大的负载压力。根据自己的实际情况,选择合适的系统节拍率,这里全部采用默认的 100Hz 系统节拍率。

Linux 内核使用全局变量 jiffies 来记录系统从启动以来的系统节拍数,系统启动的时候会将 jiffies 初始化为 0,jiffies 定义在文件 include/linux/jiffies.h 中,如下

extern u64 __jiffy_data jiffies_64;
extern unsigned long volatile __jiffy_data jiffies;

jiffies_64 和 jiffies 其实是同一个东西, jiffies_64 用于 64 位系统,而 jiffies 用于 32 位系统。
为了兼容不同的硬件, jiffies 其实就是 jiffies_64 的低 32 位
 

访问 jiffies 的时候其实访问的是 jiffies_64 的低 32 位,在 32 位的系统上读取 jiffies 的值,在 64 位的系统上 jiffes 和 jiffies_64表示同一个变量,因此也可以直接读取 jiffies 的值。所以不管是 32 位的系统还是 64 位系统,都可以使用 jiffies

HZ 表示每秒的节拍数, jiffies 表示系统运行的 jiffies 节拍数,所以 jiffies/HZ 就是系统运行时间,单位为秒。不管是 32 位还是 64 位的 jiffies,都有溢出的风险,溢出以后会重新从 0 开始计数,相当于绕回来了,因此有些资料也将这个现象也叫做绕回。处理 32 位 jiffies 的绕回显得尤为重要

相关API使用到的时候介绍

二、内核定时器简介

Linux 内核定时器使用很简单,只需要提供超时时间(相当于定时值)和定时处理函数即可,当超时时间到了以后设置的定时处理函数就会执行,和使用硬件定时器的套路一样,只是使用内核定时器不需要做一大堆的寄存器初始化工作。Linux 内核使用 timer_list 结构体表示内核定时器, timer_list 定义在文件include/linux/timer.h 中

struct timer_list {
        struct list_head entry;

        struct tvec_base *base;

        void (*function)(unsigned long);              /* 定时处理函数

        unsigned long data;                                /* 要传递给 function 函数的参数 */

        unsigned long expires;                            /* 定时器超时时间,单位是节拍数*/

        int slack;}

在使用内核定时器的时候要注意一点,内核定时器并不是周期性运行的,超时以后就会自动关闭,因此如果想要实现周期性定时,那么就需要在定时处理函数中重新开启定时器。

要使用内核定时器首先要先定义一个 timer_list 变量,表示定时器,比如需要定义一个周期为 2 秒的定时器,那么这个定时器的超时时间就是 jiffies+(2*HZ),相关API使用到的时候介绍

三、驱动编写

这里的led节点在之前篇章已经添加到设备树中,可以直接获取使用 

自行编写好基本的字符设备驱动

1、修改makefile

 2、添加定义

 3、初始化led函数

在驱动入口函数前面添加

 77行,通过路径查找获取led设备节点

82行,通过名字获取“led-gpios”属性的第0个索引

87行,申请一个叫“led”的 GPIO 管脚

93行,设置gpio为输出,默认输出值为1

这段代码主要是通过设备树获取属性,并设置GPIO的输出

4、添加调用 

在驱动入口函数里面添加调用led_init()函数

5、初始化定时器与定时器处理函数

在驱动入口里面调用初始化led后面添加

 152行,初始化 timer_list 变量,init_timer 函数原型如下

void init_timer(struct timer_list *timer)

timer:要初始化定时器。返回值: 没有返回值

154行,这里编写一个函数为timer_func,把它赋给 function,这个是定时处理函数

                timer_func函数如下,在驱动入口函数之前编写

 68行,获取私有数据

71行,设置gpio的电平为0,也就是低

72行,mod_timer 函数用于修改定时值,如果定时器还没有激活的话, mod_timer 函数会激活定时器,原型如下:

int mod_timer(struct timer_list *timer, unsigned long expires)

timer:要修改超时时间(定时值)的定时器。expires:修改后的超时时间。
返回值: 0,调用 mod_timer 函数前定时器未被激活;

                 1,调用 mod_timer 函数前定时器已被激活

 这里定时器的超时时间为jiffies+msecs_to_jiffies(500),msecs_to_jiffies函数原型如下

long msecs_to_jiffies(const unsigned int m)

将毫秒转换为 jiffies 类型,这里是500毫秒

msecs_to_jiffies函数主要就是设置周期运行,周期为500毫秒

回到155行,expires为定时器超时时间,和timer_func函数里一样500毫秒

156行,通过data传递timer数据给timer_func函数

157行,add_timer 函数用于向 Linux 内核注册定时器,使用 add_timer 函数向内核注册定时器以后,定时器就会开始运行,函数原型如下:

void add_timer(struct timer_list *timer)

timer:要注册的定时器; 没有返回值

 这段代码主要是,在初始化led后,使用定时器,在执行代码就会以500毫秒为一个周期,使led闪烁

这部分代码如下

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/atomic.h>
#include <linux/timer.h>
#include <linux/jiffies.h>

#define TIMER_CNT 1
#define TIMER_NAME "timer"

/*timer设备结构体*/
struct timer_dev{
    dev_t devid;/*设备号*/
    int major;/*主设备号*/
    int minor;/*次设备号*/
    struct cdev cdev;/*cdev表示一个字符设备*/
    struct class *class;/*类*/
    struct device *device;/*设备*/
    struct device_node *nd;/* 设备节点 */
    struct timer_list timer;/*定时器*/
    int ledgpio;/* key所使用的GPIO编号		*/
}timer;

static int timer_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &timer;/* 设置私有数据 */
    return 0;
}
static ssize_t timer_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    return 0;
}
static ssize_t timer_write(struct file *filp, const char __user *buf,
			 size_t cnt, loff_t *ppos)
{
    int ret = 0;


    return ret;
}
static int timer_release(struct inode *inode, struct file *filp)
{
    
    return 0;
}

/* 设备操作集 */
static const struct file_operations timer_fops = {
    .owner = THIS_MODULE,
    .open = timer_open,
    .read = timer_read,
    .write = timer_write,
    .release = timer_release,
};
/*定时器处理函数*/
static void timer_func(unsigned long arg){
    struct timer_dev *dev = (struct timer_dev*)arg;
    static int sta =1;
    sta =!sta;/* 每次都取反,实现LED灯反转 */
    gpio_set_value(dev->ledgpio,sta);
    mod_timer(&dev->timer,jiffies + msecs_to_jiffies(500));
}
/*初始化led*/
int led_init(struct timer_dev *dev){
    int ret = 0;
    dev->nd = of_find_node_by_path("/gpioled");
    if(dev->nd == NULL){
        ret = -EINVAL;
        goto fail_fd;
    }
    dev->ledgpio = of_get_named_gpio(dev->nd,"led-gpios",0);
    if(dev->ledgpio<0){
         ret = -EINVAL;
        goto fail_gpio;
    }
    ret = gpio_request(dev->ledgpio,"led");
    if(ret){
        ret = -EBUSY;
        printk("IO %d can't request\r\n",dev->ledgpio);
        goto fail_request;
    }
    ret= gpio_direction_output(dev->ledgpio,1);/**/
    if(ret < 0){
         ret = -EINVAL;
        goto fail_gpioset;
    }
    return 0;
fail_gpioset:
    gpio_free(dev->ledgpio);
fail_request:
fail_gpio:
fail_fd:
    return ret;
}
/*驱动入口函数*/
static int __init timer_init(void){
    int ret =0;
    /*注册字符设备驱动*/
    timer.major = 0;
    if(timer.major){ /*指定设备号*/
        timer.devid = MKDEV(timer.major,0);/*构建设备号*/
        ret = register_chrdev_region(timer.devid,TIMER_CNT,TIMER_NAME);/*注册设备号*/
    }else{/*未指定设备号*/
        ret = alloc_chrdev_region(&timer.devid,0,TIMER_CNT,TIMER_NAME);/*申请设备号*/
        timer.major = MAJOR(timer.devid);/*提取出主设备号*/
        timer.minor = MINOR(timer.devid);/*提取出次设备号*/
    }
    if(ret < 0){
        goto fail_devid;
    }
    printk("timer.major = %d,timer.minor = %d\r\n", timer.major,timer.minor);
    /*初始化cdev*/
    timer.cdev.owner = THIS_MODULE;
    cdev_init(&timer.cdev,&timer_fops);
    /*向 Linux 系统添加这个字符设备*/
    ret = cdev_add(&timer.cdev,timer.devid,TIMER_CNT);
    if(ret < 0){
        goto fail_cdev;
    }
    /*设备文件节点的自动创建与删除*/
    /*创建类*/
    timer.class = class_create(THIS_MODULE,TIMER_NAME);
    if(IS_ERR(timer.class)){
        ret = PTR_ERR(timer.class);
        goto fail_class;
    }
    /*在类下创建设备,生成/dev/TIMER_NAME这个设备文件*/
    timer.device = device_create(timer.class,NULL,timer.devid,NULL,TIMER_NAME);
    if(IS_ERR(timer.device)){
        ret = PTR_ERR(timer.device);
        goto fail_device;
    }
    
    /*初始化led*/
    ret = led_init(&timer);
    if(ret < 0){
        goto fail_ledinit;
    }

    /*初始化定时器*/
    init_timer(&timer.timer);/* 初始化定时器 */

    timer.timer.function =  timer_func;/* 设置定时处理函数 */
    timer.timer.expires = jiffies + msecs_to_jiffies(500);/* 超时时间 500毫秒 */
    timer.timer.data = (unsigned long)&timer;/* 将设备结构体作为参数 */
    add_timer(&timer.timer);/* 启动定时器 */
    return 0;
fail_ledinit:
fail_device:
    class_destroy(timer.class);
fail_class:
    cdev_del(&timer.cdev);    
fail_cdev:
    unregister_chrdev_region(timer.devid,TIMER_CNT);
fail_devid:
    return ret;
}
/*驱动出口函数*/
static void __exit timer_exit(void){
    gpio_set_value(timer.ledgpio,1);/*关灯*/
    del_timer(&timer.timer);/*删除定时器*/
    gpio_free(timer.ledgpio);/*释放io*/
    del_timer(&timer.timer);/*删除定时器*/
    device_destroy(timer.class,timer.devid);/*删除设备*/
    class_destroy(timer.class);/*删除类*/
    cdev_del(&timer.cdev); /*Linux 内核中删除相应的字符设备*/
    unregister_chrdev_region(timer.devid,TIMER_CNT);/*释放设备号*/
}

/*注册和卸载驱动*/
module_init(timer_init);
module_exit(timer_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ba che kai qi lai");

编译验证——加载驱动之后就会以500毫秒进行闪烁

四、ioctl函数

在应用层有ioctl 函数,在内核上对应就会调用unlocked_ioctl 函数或者compat_ioctl 函数,这两个功能一样,前者用在32为操作系统上,后者用在64位操作系统上

作用:设备在运行的时候可能要求数据的写入是连续的,如果这个时候用write函数去写指令的话,就有可能导致数据的不连续,比如声卡放音乐卡顿,电影播放不流畅等情况,为了解决这种情况,就有了ioctl函数,此函数专门向驱动层发送或者接收指令

 五、内核添加unlocked_ioctl 函数

1、添加设备操作集unlocked_ioctl成员 

 2、添加timepriod变量

        用于后续接收设置的毫秒

 3、初始化timepriod

在驱动入口函数的初始化定时器里面添加,timepriod设置为500 ,182行,用变量替换500毫秒

 同理、把下面99行的500毫秒改为用变量timepriod

 4、添加unlocked_ioctl对应的函数

在添加之前先定义三个宏

解释: _IO(type,nr)

type:是个0-0xff的数或者一个字符,占8bit。这个数是用来区分不同的驱动的,像设备号一样

nr:命令编号/序数,8 bit,取值范围 0~255

这里分别用1-2-3来实现关闭、打开和设置周期的操作

下面在设备操作集之前添加函数

 68行,删除定时器。del_timer_sync 函数是 del_timer 函数的同步版,会等待其他处理器使用完定时器再删除,del_timer_sync 不能使用在中断上下文中。 del_timer_sync 函数原型如下所示:

int del_timer_sync(struct timer_list *timer)
timer:要删除的定时器。
返回值: 0,定时器还没被激活; 1,定时器已经激活。

 70行, mod_timer 函数会激活定时

74-80行,获取应用层数据,根据获取到的数据来设置周期值,并开始按数据进行周期运行

六、编写应用测试

需要头文件#include <sys/ioctl.h>

1、添加宏定义

这个和内核驱动的一样

 2、使用ioctl函数

 ioctl函数原型如下

int ioctl(int fd, int cmd, ...) ;

fd:文件描述符;cmd:交互命令,设备驱动将根据 cmd 执行对应操作;

...:可变参数 arg,一些情况下应用程序需要向驱动程序传参,参数就通过arg来传递

执行成功时返回 0,失败则返回 -1 并设置全局变量 errorno 值

 通过应用层ioctl函数获取数据之后,内核就会根据数据执行对应的操作

七、编译测试

通过1和2命令实现关闭和开启定时器

通过3命令设置时间(毫秒)之后就会按照设置的时间来周期运行

总体代码如下

驱动

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/atomic.h>
#include <linux/timer.h>
#include <linux/jiffies.h>

#define TIMER_CNT 1
#define TIMER_NAME "timer"

#define CLOSE_CMD _IO(0XEF ,1)  /*关闭命令*/
#define OPEN_CMD _IO(0XEF,2)    /*打开命令*/
#define SETPERIOD_CMD _IO(0XEF,3) /*设置周期*/

/*timer设备结构体*/
struct timer_dev{
    dev_t devid;/*设备号*/
    int major;/*主设备号*/
    int minor;/*次设备号*/
    struct cdev cdev;/*cdev表示一个字符设备*/
    struct class *class;/*类*/
    struct device *device;/*设备*/
    struct device_node *nd;/* 设备节点 */
    struct timer_list timer;/*定时器*/
    int ledgpio;/* key所使用的GPIO编号		*/
    int timepriod;/*定时器周期ms*/
}timer;

static int timer_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &timer;/* 设置私有数据 */
    return 0;
}
static ssize_t timer_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    return 0;
}
static ssize_t timer_write(struct file *filp, const char __user *buf,
			 size_t cnt, loff_t *ppos)
{
    int ret = 0;


    return ret;
}
static int timer_release(struct inode *inode, struct file *filp)
{
    
    return 0;
}
static long timer_ioctl(struct file *filp,unsigned int cmd, unsigned long arg){
    struct timer_dev *dev = filp->private_data;
    int ret = 0;
    int value=0;
    switch (cmd){
        case CLOSE_CMD:
            del_timer_sync(&dev->timer);
            break;
        case OPEN_CMD:
             mod_timer(&dev->timer,jiffies + msecs_to_jiffies(dev->timepriod));
            break;
        case SETPERIOD_CMD:
            ret = copy_from_user(&value,(int *)arg,sizeof(int));
            if(ret < 0){
                return -EFAULT;
            }
            dev->timepriod = value;
            mod_timer(&dev->timer,jiffies + msecs_to_jiffies(dev->timepriod));
            break;
    }
   return ret;
}
/* 设备操作集 */
static const struct file_operations timer_fops = {
    .owner = THIS_MODULE,
    .open = timer_open,
    .read = timer_read,
    .write = timer_write,
    .release = timer_release,
    .unlocked_ioctl = timer_ioctl,
};
/*定时器处理函数*/
static void timer_func(unsigned long arg){
    struct timer_dev *dev = (struct timer_dev*)arg;
    static int sta =1;
    sta =!sta;/* 每次都取反,实现LED灯反转 */
    gpio_set_value(dev->ledgpio,sta);
    mod_timer(&dev->timer,jiffies + msecs_to_jiffies(dev->timepriod));
}
/*初始化led*/
int led_init(struct timer_dev *dev){
    int ret = 0;
    dev->nd = of_find_node_by_path("/gpioled");
    if(dev->nd == NULL){
        ret = -EINVAL;
        goto fail_fd;
    }
    dev->ledgpio = of_get_named_gpio(dev->nd,"led-gpios",0);
    if(dev->ledgpio<0){
         ret = -EINVAL;
        goto fail_gpio;
    }
    ret = gpio_request(dev->ledgpio,"led");
    if(ret){
        ret = -EBUSY;
        printk("IO %d can't request\r\n",dev->ledgpio);
        goto fail_request;
    }
    ret= gpio_direction_output(dev->ledgpio,1);/**/
    if(ret < 0){
         ret = -EINVAL;
        goto fail_gpioset;
    }
    return 0;
fail_gpioset:
    gpio_free(dev->ledgpio);
fail_request:
fail_gpio:
fail_fd:
    return ret;
}
/*驱动入口函数*/
static int __init timer_init(void){
    int ret =0;
    /*注册字符设备驱动*/
    timer.major = 0;
    if(timer.major){ /*指定设备号*/
        timer.devid = MKDEV(timer.major,0);/*构建设备号*/
        ret = register_chrdev_region(timer.devid,TIMER_CNT,TIMER_NAME);/*注册设备号*/
    }else{/*未指定设备号*/
        ret = alloc_chrdev_region(&timer.devid,0,TIMER_CNT,TIMER_NAME);/*申请设备号*/
        timer.major = MAJOR(timer.devid);/*提取出主设备号*/
        timer.minor = MINOR(timer.devid);/*提取出次设备号*/
    }
    if(ret < 0){
        goto fail_devid;
    }
    printk("timer.major = %d,timer.minor = %d\r\n", timer.major,timer.minor);
    /*初始化cdev*/
    timer.cdev.owner = THIS_MODULE;
    cdev_init(&timer.cdev,&timer_fops);
    /*向 Linux 系统添加这个字符设备*/
    ret = cdev_add(&timer.cdev,timer.devid,TIMER_CNT);
    if(ret < 0){
        goto fail_cdev;
    }
    /*设备文件节点的自动创建与删除*/
    /*创建类*/
    timer.class = class_create(THIS_MODULE,TIMER_NAME);
    if(IS_ERR(timer.class)){
        ret = PTR_ERR(timer.class);
        goto fail_class;
    }
    /*在类下创建设备,生成/dev/TIMER_NAME这个设备文件*/
    timer.device = device_create(timer.class,NULL,timer.devid,NULL,TIMER_NAME);
    if(IS_ERR(timer.device)){
        ret = PTR_ERR(timer.device);
        goto fail_device;
    }
    
    /*初始化led*/
    ret = led_init(&timer);
    if(ret < 0){
        goto fail_ledinit;
    }

    /*初始化定时器*/
    init_timer(&timer.timer);/* 初始化定时器 */
    timer.timepriod = 500;
    timer.timer.function =  timer_func;/* 设置定时处理函数 */
    timer.timer.expires = jiffies + msecs_to_jiffies(timer.timepriod);/* 超时时间 500毫秒 */
    timer.timer.data = (unsigned long)&timer;/* 将设备结构体作为参数 */
    add_timer(&timer.timer);/* 启动定时器 */
    return 0;
fail_ledinit:
fail_device:
    class_destroy(timer.class);
fail_class:
    cdev_del(&timer.cdev);    
fail_cdev:
    unregister_chrdev_region(timer.devid,TIMER_CNT);
fail_devid:
    return ret;
}
/*驱动出口函数*/
static void __exit timer_exit(void){
    gpio_set_value(timer.ledgpio,1);/*关灯*/
    del_timer(&timer.timer);/*删除定时器*/
    gpio_free(timer.ledgpio);/*释放io*/
    del_timer(&timer.timer);/*删除定时器*/
    device_destroy(timer.class,timer.devid);/*删除设备*/
    class_destroy(timer.class);/*删除类*/
    cdev_del(&timer.cdev); /*Linux 内核中删除相应的字符设备*/
    unregister_chrdev_region(timer.devid,TIMER_CNT);/*释放设备号*/
}

/*注册和卸载驱动*/
module_init(timer_init);
module_exit(timer_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ba che kai qi lai");

应用

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>


#define CLOSE_CMD _IO(0XEF ,1)  /*关闭命令*/
#define OPEN_CMD _IO(0XEF,2)    /*打开命令*/
#define SETPERIOD_CMD _IO(0XEF,3) /*设置周期*/

/*
    argc:应用程序参数个数(argv数组元素个数)
    argv:具体参数,也可以写作char **argv
    ./timerAPP <filename>    
    ./timerAPP  /dev/timer
*/
int main(int argc, char *argv[])
{
    int fd,ret;
    char *filename;
    unsigned char databuf[1];
    unsigned int cmd,arg;
    /*判断命令行输入参数是否正确*/
    if(argc != 2){
        printf("error usage!\r\n");
        return -1;
    }
    /*用指针指向文件*/
    filename = argv[1];
    /*打开文件*/
    fd = open(filename , O_RDWR);
    if(fd < 0){
        printf("file open failed\r\n",filename);
        return -1;
    }
    /*循环读取*/
    while(1){
       printf("input cmd:");
        ret = scanf("%d",&cmd);
        getchar();/*使用getchar()清理回车\n*/
        if(cmd == 1){
            ioctl(fd,CLOSE_CMD,&arg);/*关闭*/
        }else if (cmd == 2){
            ioctl(fd,OPEN_CMD,&arg);/*打开*/
        }else if (cmd == 3){
            printf("input TImer period:");
            ret = scanf("%d",&arg);
            getchar();/*使用getchar()清理回车\n*/
            ioctl(fd,SETPERIOD_CMD,&arg);/*设置周期*/
        }else{
         printf("input error\r\n");   
        }
       
    }
    /*关闭文件*/
    close(fd);

    return 0;
}

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

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

相关文章

【Android 后台持续定位】

最近工作中遇到了一个涉及后台持续性定位的问题。这里做一下总结&#xff1a;随着Android版本的条件&#xff0c;Google对后台服务管控的也是越来越严格。 这时有的小伙伴会说了&#xff0c;开启一个service然后把服务和通知关联一下变成前台服务&#xff0c;不就行了&#xff…

steam/csgo搬砖,2023年最暴利的项目

这个项目赚钱主要来源于两个地方&#xff1a; 1.比如说今天美元的汇率是1美元6.8人民币&#xff0c;那我们有特定的渠道能拿到1美元5.0-5.5左右人民币的价格&#xff0c;100美元的汇率差利润就有180元左右的利润&#xff0c;当然这个价格是根据国际的汇率上下会有浮动的。 2.…

什么是极速文件传输,极速文件传输如何进行大文件传输

当谈到大文件传输时&#xff0c;人们总是担心大数据文件的大小以及将它们从一个位置交换到另一个位置需要多长时间。由于数据捕获高分辨率视频和图像的日益复杂&#xff0c;文件的大小不断增加。数据工作流在地理上变得越来越分散。在一个位置生成的文件在其他位置处理或使用。…

晚上适合做什么副业?靠自己的劳动赚钱最光荣

对于大多数普通人来说&#xff0c;晚上的空闲时间是很多的&#xff0c;但是却总是在手机上打游戏、刷视频而白白度过了一晚上。其实最近几年来很多朋友都想利用晚上的时间做一些副业&#xff0c;因为当下的工资已经满足不了自己的需求&#xff0c;再加上生活方方面面的压力&…

【swagger2】开发api文档

文章目录一、swagger2 简介背景Open API ???swagger2的作用swagger2常用工具组件&#xff1a;二、Springfox三、springBoot使用swagger2&#xff08;简单示例&#xff09;四、Swagger-UI使用五、配置文件1、配置类&#xff1a;给docket上下文配置api描述信息2、配置类&#…

净现值、投资回收期例题讲解

净现值概念净现值&#xff08;NPV&#xff09;&#xff1a;指今后某年的Y元相当于今年的X元。需要关注两个概念&#xff1a;利率&#xff1a;利率是指借款、存入或借入金额&#xff08;称为本金总额&#xff09;中每个期间到期的利息金额与票面价值的比率。贴现率&#xff08;D…

微软Bing的AI人工只能对话体验名额申请教程

微软Bing 免费体验名额申请教程流程ChatGPT这东西可太过火了。国外国内&#xff0c;圈里圈外都是人声鼎沸。微软&#xff0c;谷歌&#xff0c;百度这些大佬纷纷出手。连看个同花顺都有GPT概念了&#xff0c;搞技术&#xff0c;做生意的看来都盯上了 流程 下面就讲一下如何申…

Python3遍历文件夹提取关键字及其附近字符

要求&#xff1a; 1&#xff0c;遍历文件夹下所有的.xml文件 2&#xff0c;从.xml文件中提取关键字以及左右十个字符 3&#xff0c;输出到excel 一&#xff1a;遍历文件夹找到所有xml文件及其路径 for root, dirs, files in os.walk(self.inputFilePath):for file in files:…

靓号管理-搜索

搜索手机号&#xff1a; 最后一条就是使用的关键mobile__contains 使用字典&#xff1a; 后端的逻辑&#xff1a; """靓号列表"""data_dict {}search_data request.GET.get(q, "")# 根据关键字进行搜索&#xff0c;如果关键字存在&…

综合项目 旅游网 【5.旅游线路收藏功能】

分析判断当前登录用户是否收藏过该线路当页面加载完成后&#xff0c;发送ajax请求&#xff0c;获取用户是否收藏的标记根据标记&#xff0c;展示不同的按钮样式编写代码后台代码RouteServlet/*** 判断当前登录用户是否收藏过该路线*/ public void isFavorite(HttpServletReques…

.md文件上传视频的踩坑经历小记

分别用QQ录制了前后两个视频&#xff0c;并利用video标签引用。这两个视频&#xff0c;明明代码一样&#xff0c;偏偏就一个成功&#xff0c;一个失败。 代码如下&#xff1a; <!-- 能够成功显示mp4视频 --> <video src"/images/video/2020110411.mp4" co…

海卡和海派有什么区别

一、海卡和海派有什么区别 海派和海卡实际上就是快船和慢船的区别。都是头程选用海运的方式&#xff0c;海派是到海港海关清关拆柜后&#xff0c;尾程配送是采用快递配送。而海卡则是到海港海关清关拆柜后&#xff0c;尾程选用货车配送。1、海派比较适用于小件货物 海派是海运抵…

OPenCV库移植到ARM开发板子上面配置过程

步骤一 1&#xff0c;环境准备去下载opencv官方的源码。 我这里用的是opencv-4.5.5版本的 2&#xff0c;还需要交叉编译工具一般&#xff0c;你交叉编译的工具板子厂家会提供工具&#xff0c;最好还是用板子厂家提供的交叉编译工具&#xff0c;因为我之前编译试过其他的交叉…

第一章:unity性能优化之内存优化

目录 前言 unity性能优化之内存的优化 一、unity Analysis工具的使用。 二、内存优化方法 1、设置和压缩图片 2、图片格式 3、动画文件 4、模型 5、RenderTexture&#xff08;RT&#xff09; 6、分辨率 7、资源的重复利用 8、shader优化 9、对bundle进行良好的管…

数字三角形

题目描述上图给出了一个数字三角形。从三角形的顶部到底部有很多条不同的路径。对于每条路径&#xff0c;把路径上面的数加起来可以得到一个和&#xff0c;你的任务就是找到最大的和。路径上的每一步只能从一个数走到下一层和它最近的左边的那个数或者右 边的那个数。此外&…

RK3399平台开发系列讲解(文件系统篇)虚拟文件系统的数据结构

🚀返回专栏总目录 文章目录 一、超级块二、挂载描述符三、文件系统类型四、索引节点五、目录项沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将介绍虚拟文件系统的数据结构。 一、超级块 文件系统的第一块是超级块,用来描述文件系统的总体信息。当我们把文件系…

【论文速递】Arxiv2018 - 加州伯克利大学借助引导网络实现快速、准确的小样本分割

【论文速递】Arxiv2018 - 加州伯克利大学借助引导网络实现快速、准确的小样本分割 【论文原文】&#xff1a;Few-Shot Segmentation Propagation with Guided Networks 【作者信息】&#xff1a;Kate Rakelly∗ Evan Shelhamer∗ Trevor Darrell Alexei Efros Sergey Levine …

源码深度解析Spring Bean的加载

在应用spring 的过程中&#xff0c;就会涉及到bean的加载&#xff0c;bean的加载经历一个相当复杂的过程&#xff0c;bean的加载入口如下&#xff1a; 使用getBean&#xff08;&#xff09;方法进行加载Bean&#xff0c;最终调用的是AbstractBeanFactory.doGetBean() 进行Bean的…

Hudi-基本概念(时间轴、文件布局、索引、表类型、查询类型、数据写、数据读、Compaction)

文章目录基本概念时间轴(TimeLine)文件布局&#xff08;File Layout&#xff09;Hudi表的文件结构Hudi存储的两个部分Hudi的具体文件说明索引&#xff08;Index&#xff09;原理索引选项全局索引与非全局索引索引的选择策略对事实表的延迟更新对事件表的去重对维度表的随机更删…

23岁去培训机构学习Java可以成功吗?

当然是可以的&#xff01; 23岁这么美好的年纪&#xff0c;要是小课重回23岁&#xff0c;一定好好学习&#xff0c;努力克服一切困难障碍。可惜是没有时光机器可以穿梭到过去。所以你在这么美好的年纪&#xff0c;有自己喜欢想学习的专业一定要好好学习&#xff0c;天天向上&a…