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

Linux 内核定时器实验

目录

一、内核时间管理简介

二、内核定时器简介

三、驱动编写

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;
}

相关文章:

Linux 内核定时器实验

目录 一、内核时间管理简介 二、内核定时器简介 三、驱动编写 1、修改makefile 2、添加定义 3、初始化led函数 4、添加调用 5、初始化定时器与定时器处理函数 这部分代码如下 四、ioctl函数 五、内核添加unlocked_ioctl 函数 1、添加设备操作集unlocked_ioctl成员 2…...

喜欢大屏电视?那就选择酷开系统,实现智能生活享受

随着科技的发展和我们生活水平的提高&#xff0c;越来越多的消费者开始认可并习惯使用各种高质量的科技产品&#xff0c;比如喜欢玩游戏的消费者&#xff0c;他们往往会追求流畅性更强、刷新率更快的大显示屏&#xff0c;以此获得更真实刺激的游戏体验&#xff0c;而喜欢追剧的…...

PMP应该如何备考?

备考之初的我们&#xff0c;总会四处搜索PMP备考经验&#xff0c;希望能拿到那些高分通关前辈的备考经验和方法。众所周知PMP考试因为有35个学时培训的基本要求&#xff0c;所以肯定是要通过培训机构报名的。 一&#xff0c;首先我们需要了解到新的考纲 1.PMP模块划分发生变化…...

AcWing《蓝桥杯集训·每日一题》—— 3956.截断数组

AcWing《蓝桥杯集训每日一题》—— 3956. 截断数组 文章目录AcWing《蓝桥杯集训每日一题》—— 3956. 截断数组一、题目二、解题思路三、代码实现本次博客我是通过Notion软件写的&#xff0c;转md文件可能不太美观&#xff0c;大家可以去我的博客中查看&#xff1a;北天的 BLOG…...

Docker的数据管理

一、管理docker容器中数据 管理Docker 容器中数据主要有两种方式:数据卷(Data Volumes)和数据卷容器( DataVolumes Containers) 。 1、 数据卷 数据卷是一个供容器使用的特殊目录&#xff0c;位于容器中。可将宿主机的目录挂载到数据卷上&#xff0c;对数据卷的修改操作立刻…...

RxJS处理异步数据流

什么是异步? 异步&#xff08;Asynchronous&#xff09;指的是不同步发生的事件或操作。通常&#xff0c;同步操作是指一系列代码按照顺序依次执行&#xff0c;直到当前代码块执行完毕后才继续执行下一个代码块&#xff1b;而异步操作则是指某些代码会被提交到后台执行&#…...

IP地址与用户行为

IP地址能够解决网络风险和提高网络安全的原因是&#xff1a;所有的网络请求都会带有IP信息&#xff0c;是访问者的独立标识&#xff0c;另外ip地址的分配和管理比较严格&#xff0c;难以造假。另外ip属于网络层&#xff0c;可以轻松的对其进行阻断。现有的各种网络安全、负载均…...

底层逻辑2

有些创业者&#xff0c;在3D打印⽕爆的时候做3D打印&#xff0c;在VR&#xff08;虚拟现 实&#xff09;蓬勃发展的时候转⾏做VR&#xff0c;在区块链成为热⻔话题的时候摇身⼀ 变成了区块链专家&#xff0c;⽽⼈⼯智能⽕了以后&#xff0c;他们⼜全身⼼投⼊⼈⼯智 能这⼀⾏。再…...

TCP报头详解及TCP十种核心机制(一)

目录 前言&#xff1a; TCP报头 TCP核心机制 一、确认应答 二、超时重传 小结&#xff1a; 前言&#xff1a; 这篇文章详细介绍了TCP报头中的一些核心数据&#xff0c;及两种TCP核心机制。其他的一些机制会在后面文章中详细介绍。 TCP报头 解释&#xff1a; 1&#xff…...

Linux用户的添加、修改和删除以及相关配置文件:useradd、passwd、usermod、userdel、相关配置文件

目录 账户的创建&#xff08;useradd&#xff09; 第一步&#xff1a;创建账号 第二步&#xff1a;创建密码 useradd参考文件 CROUP100 HOME/home INACTIVE-1 EXPIRE SHELL/bin/bash SKEL/etc/skel UID/GID密码参数参考&#xff1a;/etc/login.defs 密码参数显示命…...

进程地址空间

目录 回顾C/C语言的程序地址空间 感性认识虚拟地址空间 虚拟地址空间与物理空间如何建立映射关系 为什么要虚拟地址空间&#xff1f; 回顾C/C语言的程序地址空间 在学习C/C语言时我们知道了一个概念叫程序地址空间。通俗来说就是如下一张表&#xff0c;从中可以得知系统的几…...

数楼梯(加强版)

数楼梯(加强版) 题目背景: 小明一天放学回家,看到从1楼到2楼共有n个台阶,因为好奇,他想尝试一下总共有几种方案到二楼?他可以1步,2步,3步的跳,不能跳3步以上. 他试了很多次都没有解决这个问题,于是请求聪明的你帮忙解决这个问题. 题目描述: 1楼到2楼楼梯有n级台阶。小明每…...

MySQL-数据类型

数据类型简介数据表由多列字段构成&#xff0c;每一个字段指定了不同的数据类型&#xff0c;指定了数据类型之后&#xff0c;也就决定了向字段插入的数据内容。不同的数据类型也决定了 MySQL 在存储它们的时候使用的方式&#xff0c;以及在使用它们的时候选择什么运算符号进行运…...

剑指 Offer 32 - II. 从上到下打印二叉树 II(java解题)

剑指 Offer 32 - II. 从上到下打印二叉树 II&#xff08;java解题&#xff09;1. 题目2. 解题思路3. 数据类型功能函数总结4. java代码5. 踩坑记录1. 题目 从上到下按层打印二叉树&#xff0c;同一层的节点按从左到右的顺序打印&#xff0c;每一层打印到一行。 例如: 给定二叉…...

C#网络爬虫开发

1前言爬虫一般都是用Python来写&#xff0c;生态丰富&#xff0c;动态语言开发速度快&#xff0c;调试也很方便但是我要说但是&#xff0c;动态语言也有其局限性&#xff0c;笔者作为老爬虫带师&#xff0c;几乎各种语言都搞过&#xff0c;现在这个任务并不复杂&#xff0c;用我…...

Fastjson 总结

0x00 前言 这一篇主要是针对已经完成的fastjson系列做一个知识点总结&#xff0c;一来是为了更加有条理的梳理已经存在的内容&#xff0c;二来是为了更好的复习和利用。 0x01 Fastjson基础知识点 1.常见问题&#xff1a; 问&#xff1a;fastjson的触发点是什么&#xff1f;…...

文件路径模块os.path

文件路径模块os.path 文章目录文件路径模块os.path1.概述2.解析路径2.1.拆分路径和文件名split2.2.获取文件名称basename2.3.返回路径第一部分dirname2.4.扩展名称解析路径splitext2.5.返回公共前缀路径commonprefix3.创建路径3.1.拼接路径join3.2.获取家目录3.3.规范化路径nor…...

Kerberos简单介绍及使用

Kerberos作用 简单来说安全相关一般涉及以下方面&#xff1a;用户认证&#xff08;Kerberos的作用&#xff09;、用户授权、用户管理.。而Kerberos功能是用户认证&#xff0c;通俗来说解决了证明A是A 的问题。 认证过程&#xff08;时序图&#xff09; 核心角色/概念 KDC&…...

DOM编程-全选、全不选和反选

<!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>全选、全不选和反选</title> </head> <body bgcolor"antiquewhite"> <script type"text/jav…...

C++11可变模板参数

C11可变模板参数一、简介二、语法三、可变模版参数函数3.1、递归函数方式展开参数包3.2、逗号表达式展开参数包一、简介 C11的新特性–可变模版参数&#xff08;variadic templates&#xff09;是C11新增的最强大的特性之一&#xff0c;它对参数进行了高度泛化&#xff0c;它能…...

基于算法竞赛的c++编程(28)结构体的进阶应用

结构体的嵌套与复杂数据组织 在C中&#xff0c;结构体可以嵌套使用&#xff0c;形成更复杂的数据结构。例如&#xff0c;可以通过嵌套结构体描述多层级数据关系&#xff1a; struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

Cursor实现用excel数据填充word模版的方法

cursor主页&#xff1a;https://www.cursor.com/ 任务目标&#xff1a;把excel格式的数据里的单元格&#xff0c;按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例&#xff0c;…...

基于大模型的 UI 自动化系统

基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销&#xff0c;平衡网络负载&#xff0c;延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

遍历 Map 类型集合的方法汇总

1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

网络编程(UDP编程)

思维导图 UDP基础编程&#xff08;单播&#xff09; 1.流程图 服务器&#xff1a;短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

Map相关知识

数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…...

iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈

在日常iOS开发过程中&#xff0c;性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期&#xff0c;开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发&#xff0c;但背后往往隐藏着系统资源调度不当…...

2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...