借助内核逻辑锁pagecache到内存

news2025/7/19 14:08:54

一、背景

内存管理是一个永恒的主题,尤其在内存紧张触发内存回收的时候。系统在通过磁盘获取磁盘上的文件的内容时,若不开启O_DIRECT方式进行读写,磁盘上的任何东西都会被缓存到系统里,我们称之为page cache。可以想象,如果这样的行为持续,且如果我们持续地不断要访问磁盘上新的文件时,那么page cache就会一直增长,page cache毕竟也是占用物理内存的,所以物理内存终有一天还是会不够的。

针对这种场景,内核有一些现有的机制,如使能swap分区,这样可以把不活跃的匿名页给交换出去,交换到磁盘上,在后面再使用的时候再给交换回来,还有就是回收掉page cache,因为大部分的page cache上的数据都是有磁盘文件与之对应的,为什么说大部分,因为还有一部分是共享内存的数据,如使用shm_open出来的共享内存的数据,它也被统计进了free -h里的buff/cache里,所以,你说它算是pagecache,也是OK的,毕竟shmem.c里的shmem_get_folio_gfp里有如下调用:

但是,另外一方面,它还是比较特别的,因为这部分共享内存的数据是没有磁盘对应的文件的,所以它除了被交换到swap分区之外,它对应的内存是不能回收的。另外,这块内存虽然统计到buff/cache里,但是并没有统计到/proc/meminfo里的Active(file)和Inactive(file)里,所以严格意义上来说,它又不是pagecache。

剔除共享内存这种特殊的pagecache以外,对于真正的文件页pagecache,在不开swap分区的话,这部分文件页的pagecache仍然会被系统里的内存回收逻辑给回收。触发该内存回收逻辑的可能有node里的memory zone触及了低水位,也有memory cgroup触及memory.high水位,还可能是加的内核功能逻辑去主动做释放的动作。

如果一旦内核把一些将来会被用到的文件页给回收了,那么就会造成下次再次使用时重新从磁盘上读取该文件页的同步读的性能损耗。为了减少这样的性能损耗,我们可以把系统里的一些关键的代码段或者一些关键的文件对应的内存锁住,不让系统在回收时选择它们。

我们可以使用一些上层的手段如mlock去锁住,但是用mlock去锁相关文件页的一个前提是这个进程得一直在,如果进程退出,mlock的锁住的行为就会被“释放”。而如果用内核手段去锁住文件页,那么这个锁住状态是持久了,不会因为进程的退出而释放。我们只需要在必要的时候去解锁即可。

这篇博客里下面第二章会给出锁住文件页的一个内核模块的示例程序,并演示效果。在第三章里,我们对第二章代码里的细节做出分析和原理解释。

二、源码及效果展示

2.1 锁文件page的内核模块代码

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/dcache.h>
#include <linux/namei.h>
#include <linux/pagemap.h>

// 模块参数
static char *filepath = "/tmp/testfile";  // 默认文件路径
module_param(filepath, charp, S_IRUGO);
MODULE_PARM_DESC(filepath, "Path of the file to open");

static char *mode = "nothing";
module_param(mode, charp, S_IRUGO);

char buffer[4096];

int getfullpath(struct inode *inode)
{
    struct dentry *dentry;
    printk("inode = %p\n", inode);
	hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) {
        char *path;
        path = dentry_path_raw(dentry, buffer, PAGE_SIZE);
        if (IS_ERR(path)){
            continue;   
        }
        printk("dentry name = %s , path = %s\n", dentry->d_name.name, path);
	}
    return 0;
}

static int __init my_module_init(void) {
    struct file *file;

    printk(KERN_INFO "Opening file: %s\n", filepath);

    // 打开文件
    file = filp_open(filepath, O_RDONLY, 0);
    printk(KERN_INFO "file[%p]\n", file);
    if (IS_ERR(file)) {
        printk(KERN_ERR "Error opening file: %ld\n", PTR_ERR(file));
        return PTR_ERR(file);
    }

    // getfullpath(file->f_inode);

	// do {
    //     char *path;
    //     path = dentry_path_raw(file->f_path.dentry, buffer, PAGE_SIZE);
    //     if (IS_ERR(path)){
    //         break;
    //     }
    //     printk("[2] dentry name = %s , path = %s\n", file->f_path.dentry->d_name.name, path);
	// } while(0);

    // lock pages of the input filepath file
    if (strcmp(mode, "nothing") != 0) {
        struct address_space *mapping = file->f_mapping;
        struct page *page;
        pgoff_t index;
        unsigned long start_index, end_index;
        start_index = 0;
        printk("i_size=%ld\n", mapping->host->i_size);
        end_index = (mapping->host->i_size >> PAGE_SHIFT);
        //printk("end_index=%lu\n", end_index);
#if 1
        for (index = start_index; index < end_index; index++) {
            if (strcmp(mode, "lock") == 0) {
                page = find_get_page(mapping, index);
                if (!page) {
                    page = read_cache_page_gfp(mapping, index, GFP_KERNEL);
                    if (!page) {
                        printk("page[%lu] is NULL!\n", index);
                    }
                    else {
                        //get_page(page);
                        //SetPageMlocked(page);
                        //page = find_get_page(mapping, index);
                        unsigned long ref_count = page_ref_count(page);
                        printk("page[%lu] ref=%lu\n", index, ref_count);
                    }
                }
                else {
                    unsigned long ref_count = page_ref_count(page);
                    printk("page[%lu] ref=%lu\n", index, ref_count);
                }
                //mapping_set_unevictable(mapping);
            }
            else if (strcmp(mode, "unlock") == 0) {
                page = find_get_page(mapping, index);
                if (page) {
                    //__ClearPageMlocked(page);
                    put_page(page);
                    put_page(page);
                }
                //mapping_clear_unevictable(mapping);
            }
            else if (strcmp(mode, "query") == 0) {
                page = find_get_page(mapping, index);
                if (!page) {
                    printk("page[%lu] is NULL!\n", index);
                }
                else {
                    //get_page(page);
                    //SetPageMlocked(page);
                    //page = find_get_page(mapping, index);
                    unsigned long ref_count = page_ref_count(page);
                    printk("page[%lu] ref=%lu\n", index, ref_count);
                    put_page(page);
                }
            }
            // else if (strcmp(mode, "grablock") == 0) {
            //     page = grab_cache_page(mapping, index);
            //     if (!page) {
            //         printk("page[%lu] is NULL!\n", index);
            //     }
            //     else {
            //         unsigned long ref_count = page_ref_count(page);
            //         printk("page[%lu] ref=%lu\n", index, ref_count);
            //     }
            // }
        }
#endif
    }

    // 关闭文件
    filp_close(file, NULL);
    return -EINVAL;
}

static void __exit my_module_exit(void) {
    printk(KERN_INFO "Module exiting\n");
}

module_init(my_module_init);
module_exit(my_module_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zhaoxin");
MODULE_DESCRIPTION("A simple module to read file and lock pagecache");

2.2 配合做实验的用户态程序代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/resource.h>

#define FILE_NAME "large_file.img"
#define FILE_SIZE 1024*1024*1024ull

int main() {
    int fd;
    //char *buffer;

    // 创建并打开文件
    //fd = open(FILE_NAME, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
    fd = open(FILE_NAME, O_RDWR, S_IRUSR | S_IWUSR);
    if (fd == -1) {
        perror("open");
        return EXIT_FAILURE;
    }

    //buffer = (char*)malloc(FILE_SIZE);
    // if (!buffer) {
    //     perror("malloc");
    //     close(fd);
    //     return EXIT_FAILURE;
    // }
    // memset(buffer, 0, FILE_SIZE);

    // const char *data = "This is some sample data to be written to the file.";
    // strncpy(buffer, data, FILE_SIZE);

    // getchar();

    char *mapped = (char*)mmap(NULL, FILE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (mapped == MAP_FAILED) {
        perror("mmap");
        close(fd);
        exit(EXIT_FAILURE);
    }

    // if (read(fd, buffer, FILE_SIZE) == -1) {
    //     perror("read");
    //     free(buffer);
    //     close(fd);
    //     return EXIT_FAILURE;
    // }

    getchar();

    {
        unsigned int i = 0;
        unsigned int sum = 0;
        for (i = 0; i < FILE_SIZE; i++) {
            sum += *(mapped + i);
            *(mapped + i) = (char)sum;
        }
    }
    printf("finish read write all\n");

    getchar();

    // 锁定内存区域
    if (mlock(mapped, FILE_SIZE) == -1) {
        perror("mlock");
        //free(buffer);
        close(fd);
        return EXIT_FAILURE;
    }
    printf("finish mlock\n");

    getchar();

    //printf("Memory locked successfully. Data written to file: %s\n", buffer);

    // 释放内存
    if (munlock(mapped, FILE_SIZE) == -1) {
        perror("munlock");
    }
    munmap(mapped, FILE_SIZE);
    printf("finish munlock munmap\n");
    getchar();

    //free(buffer);
    close(fd);

    return EXIT_SUCCESS;
}

2.3 效果展示

在上面 2.2 里的测试程序里,通过mmap来读写大文件,叫large_file.img,大小是1G。

我们分几种情况来进行测试:

1)mmap方式读写大文件(注意是MAP_SHARED方式),触发完所有页的缺页异常,让磁盘文件都加载进page cache,但不mlock锁定

——在这种情况下通过内核逻辑锁住page cache,并用vmtouch -e来做移除实验

——然后再调用内核逻辑解锁page cache,看是否能vmtouch -e来移除掉

通过读写文件所有的字节来触发完所有页的缺页异常:

2)mmap映射了大文件,但是还未进行任何读写,也就是还未触发缺页异常

——在这种情况下通过内核逻辑锁住page cache,并用vmtouch -v、vmtouch -e来打印情况和做移除实验

——然后再调用内核逻辑解锁page cache,看是否能vmtouch -e来移除掉

3)mmap映射了大文件,并调用mlock锁住内存页

——在这种情况下通过内核逻辑锁住page cache,并用vmtouch -v、vmtouch -e来打印情况和做移除实验

——然后再调用内核逻辑解锁page cache,看是否能vmtouch -e来移除掉

上面 2.2 的测试程序里有多处响应按键的地方,在按第一次按键前,进行了mmap该大文件的映射,但是并未进行任何的读写:

按第一次按键后是进行1Gsize读写,自然会加载进pagecache里:

按第二次按键后,按第三次按键前,调用了mlock锁住了这个1G的内存:

按第三次按键后,程序会执行munlock再munmap后再做退出:

下面,我们针对上面这几次按键的不同场景分别做实验。

2.3.1 mmap方式已触发缺页异常后的场景

这一节测试的场景是mmap方式,并已经触发了缺页异常,但是还未mlock锁定的场景下,执行我们的内核模块程序来进行内核态锁住逻辑,看执行完是否能驱逐掉,再进行内核态解锁,看是否能驱逐掉。

如下图,触发缺页异常之后,large_file.img这个文件对应的pagecache都加载进去了。

我们进行内核态锁定后,可以看到是驱逐不掉的:

然后通过内核态解锁后,可以看到已经发生缺页异常的部分,在没有munmap时就算不mlock也是不能驱逐掉的:

这个原因会在下面 3.2 里进行解释。

我们下面展示一下,不进行内核逻辑的锁定,看是否能驱逐出去这部分已经触发了缺页异常的pagecache:

如上图可以看到,对于mmap方式(MAP_SHARED方式)进行读写,就算不调用mlock,在munmap之前,就算不用内核锁定逻辑,对应的pagecache都是驱逐不出去的。

2.3.2 mmap方式未触发缺页异常后的场景

分两种情况来测,先测执行filetest但是不触发缺页异常,看vmtouch -v的情况,然后加载内核模块进行锁定,再看vmtouch -v的情况,并看是否可以驱逐出去;然后我们再测,在执行filetest之前直接运行内核模块的锁定逻辑,再运行filetest但是不触发缺页异常,看是否可以驱逐出去。

如上图可以看到,在filetest未触发缺页异常时,相关的pagecache未被加载,然后调用了内核逻辑,让其全部被加载,并驱逐无效。

下面我们测试看先调用内核模块的锁定逻辑,再调用filetest并不触发缺页异常,看是否可以驱逐出去,可以从下图看到,同样是驱逐不出去的:

2.3.3 mmap方式mlock后及munmap后的场景

其实在上面 2.3.1 的实验里,我们已经知道,如果是mmap方式MAP_SHARED方式对于已经触发缺页异常的部分,就算不做mlock对应的pagecache也是无法被驱逐的。

我们这里只需再做补充实验,就是不调用内核锁定逻辑,并不触发缺页异常,直接调用mlock,看是否相关pagecache已经被加载进来了,并看是否可以被驱逐掉。

我们改写一下程序,让触发缺页异常的逻辑干掉:

看不调用内核逻辑锁定,只靠mlock是否可以保证mmap MAP_SHARED的读写方式对应的pagecache是不是会被驱逐。

如下图可以看到是不会被驱逐的:

关于mlock的内核逻辑之前的博客有详细的介绍,可以参考 内存管理相关——malloc,mmap,mlock与unevictable列表-CSDN博客。

我们下面再看一下munmap后,如果不调用内核锁定逻辑看是否可以被驱逐,如下图看到是可以被驱逐的:

然后,我们试一下,执行过内核锁定逻辑之后,在munmap后是否能被驱逐:

可以从上图看到,是不会被驱逐的。

三、源码分析及原理解释

3.1 使用inode的i_size获取文件的大小

通过file->f_mapping->host可以获得打开的文件对应inode,这里的file是指进程地址空间实例的file,通过inode->i_size可以得到文件的大小:

上图对应的是执行两次filetest,第一次没有写入文件,第二次写入了一段时间就ctrl+c停止了:

对于64位系统上,直接获取inode->i_size是没有什么问题的,如果是32bit系统上,得使用i_size_read来获取,如i_size_read里的实现,使用到了顺序锁,顺序锁的细节见之前的博客  顺序锁的原理和使用注意事项-CSDN博客:

3.2 对于mmap文件出来的地址空间,一旦触发了缺页异常,其pagecache不会回收

上面 2.3.1 的实验可以看到,如果mmap一个磁盘上的文件到一个进程的虚拟地址空间之后,一旦触发了缺页异常,就算不执行mlock,也不执行内核态的锁pagecache,这些mmap且已经触发缺页异常的pagecache系统是回收不了的。

对于系统里的程序,如果程序并未退出,mmap加载的一些so库(程序的代码段都是mmap方式加载进地址空间)如果一旦触发过缺页异常,那么它们对应的pagecache是不会被回收的。但是要注意,对于一些会退出的进程,一旦进程退出,对应的代码段就可以被回收了,比如像grep/ls等这些系统命令的代码段。

通过mlock或者内核态的锁定逻辑可以提前把这些代码段给加载进pagecache,如果加上锁定后,它们就一直不回被回收了(如果用内核逻辑进行锁定,就算程序退出后,相关已经锁定的代码段也不会被回收了)。

3.2.1 mmap的MAP_PRIVATE方式的说明

虽然我们这篇博客里的示例程序用的是MAP_SHARED方式进行的读写,但是对于库文件来说,一般都是用的MAP_PRIVATE方式。

MAP_PRIVATE方式要注意的是,如果是读,那其pagecache肯定是可以多个进程共享同一个so的代码文件的。但是对于可写的部分,MAP_PRIVATE方式进行的映射会把可写的部分触发一个COW分配匿名内存并拷贝一份出来改写,这样原代码文件不会被改写,这也是so库里的data段也是这么一个方式。

对于我们这篇博客里的示例程序而言,如果用MAP_PRVIATE方式,如果要进行写的话,那么就写的是匿名内存,那么所有的pagecache的实验都不凑效了。但是如果用MAP_PRIVATE方式只是读的话,那么这篇博客里的实验也是一样凑效的。

3.3 对于非mmap方式的文件读写情况的说明

所谓非mmap方式的文件读写,就是直接通过read/write/fread/fwrite的这样的文件系统的接口来读写文件数据。这种方式,由于并没有直接映射相关文件到进程地址空间,而是借助vfs进行代码读写,在完成读写之后,内核是可以对其pagecache内容进行回收的。

对于读和写还得分开来看,对于读而言,虽然不放面使用mlock(因为通过常规手段不mmap根本拿不到pagecache对应的虚拟内存),但是可以使用内核模块的锁定逻辑进行锁定。

但是对于写而言,要特别注意,如果是一个1G文件,重新O_CREAT创建并从开头开始写入,那么对于旧文件的这些锁定逻辑包括内核锁定逻辑,由于文件大小已经变更回过0了,所以之前的page是可以被拿去回收的,这一点要额外注意

3.4 通过增加page引用计数来防止被驱逐

通过page引用计数来方式被内存回收的做法,其实在之前的博客 内存管理之——get_user_pages和pin_user_pages及缺页异常_get user page-CSDN博客 里也有讲到。

这里,我们的关键逻辑是如何找到文件相关的page,如下方式通过find_get_page来根据address_space来获取到指定index的page:

address_space的指针可通过file的f_mapping拿到,另外上图里的index即address_space的映射的pages里的序号。

如果find_get_page找不到的话,再通过read_cache_page_gfp来读取磁盘上的文件读到pagecache,当然自然需要按需创建pagecache的内存,所以需要传入分配内存时的gfp参数:

由于find_get_page和read_cache_page_gfp都是会增加page的引用计数的,所以就没必要再get_page一次了。只需在对应的unlock逻辑里也得相应的扣除引用计数(多put_page一次就是为了扣除,另一次是抵消find_get_page的引用计数):

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

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

相关文章

Nacos简介—2.Nacos的原理简介

大纲 1.Nacos集群模式的数据写入存储与读取问题 2.基于Distro协议在启动后的运行规则 3.基于Distro协议在处理服务实例注册时的写路由 4.由于写路由造成的数据分片以及随机读问题 5.写路由 数据分区 读路由的CP方案分析 6.基于Distro协议的定时同步机制 7.基于Distro协…

TCP协议理解

文章目录 TCP协议理解理论基础TCP首部结构图示字段逐项解析 TCP是面向连接&#xff08;Connection-Oriented&#xff09;面向连接的核心表现TCP 面向连接的核心特性TCP 与UDP对比 TCP是一个可靠的(reliable)序号与确认机制&#xff08;Sequencing & Acknowledgment&#xf…

用 LangChain 手搓 RAG 系统:从原理到实战

一、RAG 系统简介 在当今信息爆炸的时代&#xff0c;如何高效地从海量数据中获取有价值的信息并生成准确、自然的回答&#xff0c;成为了人工智能领域的重要课题。检索增强生成&#xff08;Retrieval-Augmented Generation&#xff0c;RAG&#xff09;系统应运而生&#xff0c;…

联合体和枚举类型

1.联合体类型 1.1:联合体类型变量的创建 与结构体类型一样&#xff0c;联合体类型 (关键字:union) 也是由⼀个或者多个成员变量构成&#xff0c;这些成员变量既可以是不同的类型&#xff0c;也可以是相同的类型。但是编译器只为最⼤的成员变量分配⾜够的内存空间。联合体的特…

C语言指针5

1.void*概述 void称为无类型&#xff0c;void*称为无类型指针&#xff0c;void不可以单独定义变量&#xff0c;却可以定义无类型的指针&#xff0c;而且所定义的指针称为泛型指针&#xff0c;所谓泛型指针&#xff0c;其含义是void*类型的指针可以接收一切类型变量的地址 struc…

文档构建:Sphinx全面使用指南 — 强化篇

文档构建&#xff1a;Sphinx全面使用指南 — 强化篇 Sphinx 是一款强大的文档生成工具&#xff0c;使用 reStructuredText 作为标记语言&#xff0c;通过扩展兼容 Markdown&#xff0c;支持 HTML、PDF、EPUB 等多种输出格式。它具备自动索引、代码高亮、跨语言支持等功能&#…

深度理解C语言函数之strlen()的模拟实现

文章目录 前言一、strlen的模拟实现二、模拟实现代码及思路2.1 计数法2.2 指针相减法三、递归计数法 总结 前言 我写这篇文章的目的主要是帮助理解C语言中重要函数的用法&#xff0c;后面也会总结C相关的函数的模拟实现&#xff0c;这里的算法不一定是最好的&#xff0c;因为只…

0基础 | Proteus仿真 | 51单片机 | 继电器

继电器---RELAY 本次选择一款5v一路继电器进行讲解 信号输入 IN1输入高电平&#xff0c;三极管导通&#xff0c;LED1点亮&#xff0c;电磁铁12接通吸引3向下与4接通&#xff0c;J1A的12接通 IN1输入低电平&#xff0c;则J1A的23接通 产品引脚定义及功能 序号 引脚符号 引脚…

Python解析地址中省市区街道

Python解析地址中省市区街道 1、效果 输入&#xff1a;海珠区沙园街道西基村 输出&#xff1a; 2、导入库 pip install jionlp3、示例代码 import jionlp as jiotext 海珠区沙园街道西基村 res jio.parse_location(text, town_villageTrue) print(res)

在vscode终端中运行npm命令报错

解决方案 这个错误信息表明&#xff0c;你的系统&#xff08;可能是 Windows&#xff09;阻止了 PowerShell 执行脚本&#xff0c;这是由于 PowerShell 的执行策略导致的。PowerShell 的执行策略控制着在系统上运行哪些 PowerShell 脚本。默认情况下&#xff0c;Windows 可能…

提升变电站运维效率:安科瑞无线测温系统创新应用

一、引言 变电站作为电力系统的关键枢纽&#xff0c;承担着变换电压、分配电能以及控制电力流向等重要任务。在变电站的运行过程中&#xff0c;电气设备的接点温度监测至关重要。过热问题可能由多种因素引发&#xff0c;如电阻过大、接头质量欠佳、衔接不紧密、物理老化等&…

vue3 使用 vite 管理多个项目,实现各子项目独立运行,独立打包

场景&#xff1a; 之前写过一篇 vite vue2 的配置&#xff0c;但是现在项目使用 vue3 较多&#xff0c;再更新一下 vue脚手架初始化之后的项目&#xff0c;每个项目都是独立的&#xff0c;导致项目多了之后&#xff0c;node依赖包过多&#xff0c;占用内存较多。想实现的效果…

WebRTC服务器Coturn服务器用户管理和安全性

1、概述 Coturn服务器对用户管理和安全方面也做了很多的措施&#xff0c;以下会介绍到用户方面的设置 1.1、相关术语 1.1.1 realm 在 coturn 服务器中&#xff0c;域&#xff08;realm&#xff09;是一种逻辑上的分组概念&#xff0c;用于对不同的用户群体、应用或者服务进行区…

如何使用极狐GitLab 的外部状态检查功能?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;关于中文参考文档和资料有&#xff1a; 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 外部状态检查 (ULTIMATE ALL) pending 状态引入于极狐GitLab 16.5 pending 状态检查的超时时间为两分钟引入于极狐GitLab 16…

【Langchain】RAG 优化:提高语义完整性、向量相关性、召回率--从字符分割到语义分块 (SemanticChunker)

RAG 优化&#xff1a;提高语义完整性、向量相关性、召回率–从字符分割到语义分块 (SemanticChunker) 背景&#xff1a;提升 RAG 检索质量 在构建基于知识库的问答系统&#xff08;RAG&#xff09;时&#xff0c;如何有效地将原始文档分割成合适的文本块&#xff08;Chunks&a…

深入剖析扣子智能体的工作流与实战案例

前面我们已经初步带大家体验过扣子工作流&#xff0c;工作流程是 Coze 最为强大的功能之一&#xff0c;它如同扣子中蕴含的奇妙魔法工具&#xff0c;赋予我们的机器人处理极其复杂问题逻辑的能力。 这篇文章会带你更加深入地去理解并运用工作流解决实际问题 目录 一、工作流…

基于K8s日志审计实现攻击行为检测

K8s日志审计以一种事件溯源的方式完整记录了所有API Server的交互请求。当K8s集群遭受入侵时&#xff0c;安全管理员可以通过审计日志进行攻击溯源&#xff0c;通过分析攻击痕迹&#xff0c;找到攻击者的入侵行为并还原攻击者的攻击路径&#xff0c;修复安全问题。 在本篇文章中…

【Linux网络编程】应用层协议HTTP(实现一个简单的http服务)

目录 前言 一&#xff0c;HTTP协议 1&#xff0c;认识URL 2&#xff0c;urlencode和urldecode 3&#xff0c;HTTP协议请求与响应格式 二&#xff0c;myhttp服务器端代码的编写 HTTP请求报文示例 HTTP应答报文示例 代码编写 网络通信模块 处理请求和发送应答模块 结…

短视频+直播商城系统源码全解析:音视频流、商品组件逻辑剖析

时下&#xff0c;无论是依托私域流量运营的品牌方&#xff0c;还是追求用户粘性与转化率的内容创作者&#xff0c;搭建一套完整的短视频直播商城系统源码&#xff0c;已成为提升用户体验、增加商业变现能力的关键。本文将围绕三大核心模块——音视频流技术架构、商品组件设计、…

STM32定时器---基本定时器

目录 一、定时器的概述 二、时基单元 三、基本定时器的的时序 &#xff08;1&#xff09;预分频器时序 &#xff08;2&#xff09;计数器时序 四、基本定时器的使用 一、定时器的概述 在没有定时器的时候&#xff0c;我们想要延时往往都是写一个Delay函数&#xff0c;里面…