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

tmpfs的监控筛选/dev/shm下的shmem创建

一、背景

在一个比较注重性能的系统上,共享内存的使用肯定非常普遍。为了能更好的了解系统里共享内存的使用,比如创建、删除等操作,我们是可以对其进行监控的。

这篇博客以共享内存的创建监控为例来介绍如何监控共享内存。

这里有一个概念就是/dev/shm下的文件,是放在内存上的文件,这个/dev/shm如下面的df -h命令可以看到,它是一个tmpfs文件系统,而系统上的tmpfs文件系统其实有很多,/dev/shm是其中一个。我们其实在监控共享内存时,一般来说都是只感兴趣/dev/shm下的也就是shm_open这套shmem接口来创建的共享内存文件。

在下面第二章里,我们贴出改动源码和结果展示,在第三章里,我们给出源码分析和相关细节分析。

二、源码和结果展示

2.1 内核shmem.c里的改动部分

在内核shmem.c里增加下面这段代码:


static u32 _enable_output_path = 0;

void shmem_set_enable_output_path(void) {
	_enable_output_path = 1;
}
EXPORT_SYMBOL_GPL(shmem_set_enable_output_path);

void shmem_set_disable_output_path(void) {
	_enable_output_path = 0;
}
EXPORT_SYMBOL_GPL(shmem_set_disable_output_path);

#include <linux/blk_types.h>
#include <linux/blkdev.h>

#include <linux/fs.h>
#include <linux/mount.h>

#if 0
void print_superblock_root_path(struct super_block *sb) {
    struct dentry *root_dentry = sb->s_root;
    char path_buffer[256];
    struct path root_path;

    root_path.dentry = root_dentry;
    root_path.mnt = root_dentry->d_sb->s_root->mnt; // 获取挂载点

    if (d_path(&root_path, path_buffer, sizeof(path_buffer)) < 0) {
        printk(KERN_ERR "Failed to get path\n");
    } else {
        printk(KERN_INFO "Root path: %s\n", path_buffer);
    }
}
#endif

extern struct dentry* get_dentry_by_sb_currns(struct super_block *sb);

static struct super_block *_psb_devshm = NULL;

int outputfullpath(const char* i_prefix, struct inode *i_inode, struct dentry* i_dentry)
{
    struct dentry *dentry;
	char *buffer, *buffer_mount1, *buffer_mount2, *buffer_mount3, *path;
	buffer = (char *)__get_free_page(GFP_KERNEL);
	if (!buffer)
		return -ENOMEM;
	buffer_mount1 = (char *)__get_free_page(GFP_KERNEL);
	if (!buffer_mount1)
		return -ENOMEM;
	buffer_mount2 = (char *)__get_free_page(GFP_KERNEL);
	if (!buffer_mount2)
		return -ENOMEM;
	buffer_mount3 = (char *)__get_free_page(GFP_KERNEL);
	if (!buffer_mount3)
		return -ENOMEM;
	
	if (_psb_devshm) {
		if (i_inode->i_sb != _psb_devshm) {
			printk("not /dev/shm!\n");
			goto free_label;
		}
	}
	hlist_for_each_entry(dentry, &i_inode->i_dentry, d_u.d_alias) {
        path = dentry_path_raw(dentry, buffer, PAGE_SIZE);
        if (IS_ERR(path)){
            continue;   
        }

		{
            struct dentry* de = get_dentry_by_sb_currns(i_inode->i_sb);
            char *path1 = dentry_path_raw(de, buffer_mount1, PAGE_SIZE);
            char *path2 = NULL;
            char *path3 = NULL;
            //printk("path1 = %s\n", path1);
            if (de->d_sb) {
                if (strcmp(path1, "/") != 0) {
                    de = get_dentry_by_sb_currns(de->d_sb);
                    path2 = dentry_path_raw(de, buffer_mount2, PAGE_SIZE);
                    //printk("path2 = %s\n", path2);
                    if (de->d_sb) {
                        if (strcmp(path2, "/") != 0) {
                            de = get_dentry_by_sb_currns(de->d_sb);
                            path3 = dentry_path_raw(de, buffer_mount3, PAGE_SIZE);
                            //printk("path3 = %s\n", path3);
                        }
                        else {
                            path2 = NULL;
                        }
                    }
                }
                else {
                    path1 = NULL;
                }
            }
			else {
				goto free_label;
			}

			if (path1 && path2) {
				if ((strcmp(path2, "/dev") == 0) 
					&& (strcmp(path1, "/shm") == 0)) {
					if (strcmp(path3, "/") == 0) {
						printk("[/dev/shm][%s]dentry name = %s , path = %s, i_dentry=%s, i_dentry->d_parent.name=%s"
							" sb->s_root.name=%s (d_parent==sb->s_root)[%u]\n", 
							i_prefix, dentry->d_name.name, path, i_dentry->d_name.name, 
							i_dentry->d_parent->d_name.name, i_inode->i_sb->s_root->d_name.name,
							(i_dentry->d_parent == i_inode->i_sb->s_root)?1:0);
						_psb_devshm = i_inode->i_sb;
						goto free_label;
					}
				}
			}
            // if (path2) {
            //     printk("dentry name = %s%s%s\n", path2, path1, path);
            // }
            // else if (path1) {
            //     printk("dentry name = %s%s\n", path1, path);
            // }
            // else {
            //     printk("dentry name = %s\n", path);
            // }
        }
	}

free_label:
	free_page((unsigned long)buffer);
	free_page((unsigned long)buffer_mount1);
	free_page((unsigned long)buffer_mount2);
	free_page((unsigned long)buffer_mount3);

	//spin_unlock(&inode->i_lock);
    return 0;
}

然后在shmem.c里的shmem_mknod函数里增加下面这段红框部分的代码:

另外,和之前的博客 获取inode的完整路径包含挂载的路径_内核如何通过inode获取完整路径-CSDN博客 里增加在namespace.c里 3.1 一节里讲的内容一样,增加了下面这个函数:

struct dentry* get_dentry_by_sb_currns(struct super_block *sb)
{
	struct mnt_namespace *mnt_ns = current->nsproxy->mnt_ns;
	struct mount *mnt;
	struct dentry* dentry = NULL;
	lock_mount_hash();
	list_for_each_entry(mnt, &mnt_ns->list, mnt_list) {
		if (mnt->mnt.mnt_sb == sb) {
			dentry = mnt->mnt_mountpoint;
			break;
		}
	}
	unlock_mount_hash();
	return dentry;
}
EXPORT_SYMBOL_GPL(get_dentry_by_sb_currns);

2.2 内核模块用来启动/关闭shmem的这个调试判断和打印

#include <linux/module.h>
#include <linux/capability.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <linux/proc_fs.h>
#include <linux/ctype.h>
#include <linux/seq_file.h>
#include <linux/poll.h>
#include <linux/types.h>
#include <linux/ioctl.h>
#include <linux/errno.h>
#include <linux/stddef.h>
#include <linux/lockdep.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/init.h>
#include <asm/atomic.h>
#include <trace/events/workqueue.h>
#include <linux/sched/clock.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/tracepoint.h>
#include <trace/events/osmonitor.h>
#include <trace/events/sched.h>
#include <trace/events/irq.h>
#include <trace/events/kmem.h>
#include <linux/ptrace.h>
#include <linux/uaccess.h>
#include <asm/processor.h>
#include <linux/sched/task_stack.h>
#include <linux/nmi.h>
#include <asm/apic.h>
#include <linux/version.h>
#include <linux/sched/mm.h>
#include <asm/irq_regs.h>
#include <linux/kallsyms.h>
#include <linux/kprobes.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("zhaoxin");
MODULE_DESCRIPTION("Module for kernel get time.");
MODULE_VERSION("1.0");

extern void shmem_set_enable_output_path(void);
extern void shmem_set_disable_output_path(void);

static int __init testtmpfs_init(void)
{
    shmem_set_enable_output_path();
    return 0;
}

static void __exit testtmpfs_exit(void)
{
    shmem_set_disable_output_path();
}

module_init(testtmpfs_init);
module_exit(testtmpfs_exit);

2.3 结果展示

我们先在/dev/shm里创建文件夹和创建新的文件都可以打印出加的调试打印:

如果我们自己mount一个tmpfs的分区,然后在里面创建文件并不会打印出/dev/shm目录下才会打印的上图里的打印,而是打印下图里的not /dev/shm字样:

三、源码分析和相关细节分析

3.1 tmpfs文件系统和shmem接口的应用

shmem.c文件是服务于系统里的所有tmpfs文件系统里的操作。而tmpfs系统在系统里的情况非常普遍:

而我们感兴趣的往往是/dev/shm这样的使用shm的接口的程序应用,有关的例子如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>           // For O_* constants
#include <sys/mman.h>       // For shm_open, mmap
#include <unistd.h>         // For close
#include <sys/stat.h>       // For mode constants
#include <sys/types.h>
#include <errno.h>

int main() {
    const char *name = "/my_shm"; // 共享内存的名称
    const size_t size = 1024*1024*1024;      // 共享内存的大小
    int shm_fd;                    // 共享内存文件描述符
    void *ptr;                     // 指向共享内存的指针

    // 创建共享内存对象
    shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666);
    if (shm_fd == -1) {
        perror("shm_open");
        exit(EXIT_FAILURE);
    }

    // 调整共享内存的大小
    if (ftruncate(shm_fd, size) == -1) {
        perror("ftruncate");
        exit(EXIT_FAILURE);
    }

    // 将共享内存映射到进程的地址空间
    ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
    if (ptr == MAP_FAILED) {
        perror("mmap");
        exit(EXIT_FAILURE);
    }

    getchar();

    // 锁定共享内存区域中的页
    if (mlock(ptr, size) != 0) {
        perror("mlock");
        exit(EXIT_FAILURE);
    }

    getchar();

    // 写入数据到共享内存
    sprintf((char*)ptr, "Hello, Shared Memory!");

    // 读取数据
    printf("Data from shared memory: %s\n", (char *)ptr);

    // 解锁共享内存区域
    if (munlock(ptr, size) != 0) {
        perror("munlock");
        exit(EXIT_FAILURE);
    }

    // 解除映射
    if (munmap(ptr, size) == -1) {
        perror("munmap");
        exit(EXIT_FAILURE);
    }

    // 关闭共享内存对象
    if (close(shm_fd) == -1) {
        perror("close");
        exit(EXIT_FAILURE);
    }

    // 删除共享内存对象
    if (shm_unlink(name) == -1) {
        perror("shm_unlink");
        exit(EXIT_FAILURE);
    }

    return 0;
}

3.2 shmem.c里的shmem_mknod函数

tmpfs系统下的创建文件夹和创建文件的操作都会走到shmem_mknod函数里来,如下图依次是创建文件夹和创建文件的函数shmem_mkdir和shmem_create,这两个函数最终都是调用的shmem_mknod函数:

所以,我们在shmem_mknod函数里加监测逻辑是能覆盖tmpfs文件系统里的创建文件夹和创建文件的行为。

3.3 shmem.c里的shmem_link和shmem_symlink

shmem.c里还有一些其他接口,如下图里的shmem_link是指硬链接,shmem_symlink是指软链接:

有关硬链接和软链接及相关的inode,dentry的实验见之前的博客 关于inode,dentry结合软链接及硬链接的实验-CSDN博客。

3.4 shmem_mknod函数里增加的“判断是不是/dev/shm下的mknod事件”的逻辑

我们在shmem_mknod里增加了判断逻辑,检查是否是/dev/shm下的mknod事件,用的依次判断是否是/dev和/shm这两级super_block的mnt_mountpoint,而获取mnt_mountpoint的这块核心逻辑在之前的博客 获取inode的完整路径包含挂载的路径_内核如何通过inode获取完整路径-CSDN博客 里进行了分析:

在shmem.c里使用了该get_dentry_by_sb_currns函数进行了几级mountpoint的dentry的记录,然后判断完整的mountpoint的dentry链是否是/dev/shm:

如果判断到是/dev/shm这个完整路径,就记录下来这个super_block指针:

这样,接下来的判断就会更加快了:

相关文章:

  • Linux——基础开发工具
  • 【第43节】实验分析windows异常分发原理
  • 低功耗设计:Level Shift的种类(以SAED EDK 32/28nm工艺库为例)
  • ubuntu上,e1000e,i1210有线网卡驱动安装
  • 从暴力到动态规划再到双指针:使用 Java 探索接雨水问题的不同解法
  • 处理Long类型长度超长导致前端精度丢失问题
  • Python用户管理系统深度解析(附源码):从类设计到安全实现的完整指南
  • 「数据可视化 D3系列」入门第一章:Hello D3.js
  • 数据库实战篇,JSON对象在Kooboo中的实际应用(二)
  • SQL注入之时间盲注攻击流程详解
  • SLAM文献之DM-VIO: Delayed Marginalization Visual-Inertial Odometry
  • 大模型之Hugging Face
  • 信奥还能考吗?未来三年科技特长生政策变化
  • 【开发教程】学生团队项目开发协调管理文档库构建以及使用指南
  • #4 为什么要物联以及 物联网的整体结构
  • linux tracepoint系列宏定义(TRACE_EVENT,DEFINE_TRACE等)展开过程分析之三 define_trace.h头文件
  • 【blender小技巧】Blender导出带贴图的FBX模型,并在unity中提取材质模型使用
  • telepresence使用指南
  • 【LH-开发记录】
  • Dockerfile 学习指南和简单实战
  • 女子隐私被“上墙”莫名遭网暴,网警揪出始作俑者
  • 天问三号计划2028年前后发射实施,开放20千克质量资源
  • 詹妮弗·劳伦斯、罗伯特·帕丁森新片入围戛纳主竞赛单元
  • 中华人民共和国和肯尼亚共和国关于打造新时代全天候中非命运共同体典范的联合声明
  • 佩斯科夫:俄美总统会晤正在筹备中,未设定停火最后期限
  • 福特中国CFO:依然坚信中国市场,上海帮助公司吸引到人才