当前位置: 首页 > 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;它能…...

Linux多线程

目录 一、认识线程 1.1 线程概念 1.2 页表 1.3 线程的优缺点 1.3.1 优点 1.3.2 缺点 1.4 线程异常 二、进程 VS 线程 三、Linux线程控制 3.1 POSIX线程库 3.1 线程创建 3.3 线程等待 3.4 线程终止 3.4.1 return退出 3.4.2 pthread_exit() 3.4.3 pthread_cancel…...

Webpack5 环境下 Openlayers 标注(Icon) require 引入图片问题

Webpack5 环境下 Openlayers 标注&#xff08;Icon&#xff09; require 引入图片问题环境版本Openlayers 使用 require 问题Webpack5 正确配置构建新环境的时候&#xff0c;偶然发现 Openlayers 使用 require 的方式加载图片&#xff08;Icon&#xff09;报错&#xff0c;开始…...

Zookeeper安装部署

文章目录Zookeeper安装部署Zookeeper安装部署 将Zookeeper安装包解压缩&#xff0c; [rootlocalhost opt]# ll 总用量 14032 -rw-r--r--. 1 root root 12392394 10月 13 11:44 apache-zookeeper-3.6.0-bin.tar.gz drwxrwxr-x. 6 root root 4096 10月 18 01:44 redis-5.0.4 …...

Cow Acrobats ( 临项交换贪心 )

题目大意&#xff1a; N 头牛 &#xff0c; 每头牛有一个重量(Weight)和一个力量(Strenth) &#xff0c; N头牛进行排列 &#xff0c; 第 i 头牛的风险值为其上所有牛总重减去自身力量 &#xff0c; 问如何排列可以使最大风险值最小 &#xff0c; 求出这个最小的最大风险值&am…...

MySQL:为什么说应该优先选择普通索引,尽量避免使用唯一索引

前言 在使用MySQL的过程中&#xff0c;随着表数据的逐渐增多&#xff0c;为了更快的查询我们需要的数据&#xff0c;我们会在表中建立不同类型的索引。 今天我们来聊一聊&#xff0c;普通索引和唯一索引的使用场景&#xff0c; 以及为什么说推荐大家优先使用普通索引&#xf…...

Spring Cloud alibaba之Feign

JAVA项目中如何实现接口调用&#xff1f;HttpclientHttpclient是Apache Jakarta Common下的子项目&#xff0c;用来提供高效的、最新的、功能丰富的支持Http协议的客户端编程工具包&#xff0c;并且它支持HTTP协议最新版本和建议。HttpClient相比传统JDK自带的URL Connection&a…...

零信任-Google谷歌零信任介绍(3)

谷歌零信任的介绍&#xff1f; "Zero Trust" 是一种网络安全模型&#xff0c;旨在通过降低网络中的信任级别来防止安全威胁。在零信任模型中&#xff0c;不论请求来自内部网络还是外部网络&#xff0c;系统都将对所有请求进行详细的验证和审核。这意味着每次请求都需…...

Numpy基础——人工智能基础

文章目录一、Numpy概述1.优势2.numpy历史3.Numpy的核心&#xff1a;多维数组4.numpy基础4.1 ndarray数组4.2 内存中的ndarray对象一、Numpy概述 1.优势 Numpy(Nummerical Python),补充了Python语言所欠缺的数值计算能力&#xff1b;Numpy是其它数据分析及机器学习库的底层库&…...

电商仓储与配送云仓是什么?

仓库是整个供给链的关键局部。它们是产品暂停和触摸的点&#xff0c;耗费空间和时间(工时)。空间和时间反过来也是费用。经过开发数学和计算机模型来微调仓库的规划和操作&#xff0c;经理能够显著降低与产品分销相关的劳动力本钱&#xff0c;进步仓库空间应用率&#xff0c;并…...

【零基础入门前端系列】—HTML介绍(一)

【零基础入门前端系列】—HTML介绍&#xff08;一&#xff09; 一、什么是HTML HTML是用来描述网页的一种语言HTML指的是超文本标记语言&#xff1a;HyperText Markup LanguageHTML不是一种编程语言&#xff0c;而是一种超文本标记语言&#xff0c;标记语言是一套标记标签(ma…...

wordpress 家教/中国网民博客 seo

学习&#xff1a;我和阿九Azure有约 系列视频教程&#xff08;油管可看&#xff09; 第一节&#xff1a;Subscription是什么&#xff1f; 建立Subscription 不用信用卡也不用电话。 我们还可以用下面方式 第二节&#xff1a;Resource Group 第三节&#xff1a;App Service 免…...

wordpress 咚门下载/seo做的比较牛的公司

ifconfig eth0 |grep "inet addr:" |awk {print $2}|cut -c 6- 或者ifconfig | grep inet addr:| grep -v 127.0.0.1 | cut -d: -f2 | awk { print $1} 本文转自 技术花妞妞 51CTO博客&#xff0c;原文链接:http://blog.51cto.com/xiaogongju/1951804...

旅游网站的建设的文献综述/嘉兴百度seo

单词种类单词符号种别码单词种类单词符号种别码整型常数digit digit*1运算符*20字符串&#xff08;标识符ID&#xff09;letter(letter|digit)*2运算符/21关键字main3运算符22关键字if4运算符>23关键字else5运算符<24关键字do6运算符<25关键字while7运算符26关键字for…...

中南大学双一流建设网站/中国百强城市榜单

1、什么是BeanDefinition BeanDefinition是Spring Framework中定义Bean的配置元信息接口&#xff0c;包含&#xff1a; Bean的类名Bean行为配置元素&#xff0c;如作用域、自动绑定的模式、生命周期回调等其他Bean引用&#xff0c;又可称作合作者&#xff08;Collaborators&a…...

北京网站开发公司排名/如何查看网站收录情况

需求 通过一个聚合二维码&#xff0c;支付宝扫码进入到生活号的首页&#xff0c;微信扫码进入微信公众号关注界面 思路 判断用户使用的支付宝扫码还是微信扫码。这个比较容易&#xff0c;根据浏览器请求头里面的信息就可以识别获取生活号和公众号首页关注链接&#xff0c;根…...

html怎么做/什么是优化设计

北京----日照的T51次空调快速列车。22&#xff1a;25北京始发&#xff0c;次日11&#xff1a;14到日照。北京到日照964公里&#xff0c;列车运行12小时49分。硬座----硬卧中铺------软座------软卧下铺125元----224元-------198元-------352元北京到日照就这一趟直达列车&#…...