kernel | 不想老是编译内核?sysfs和debugfs了解一下

news2024/12/23 14:35:32

编译内核是一件让大家都抗拒的事情,因为编译一次内核需要的时间成本比较漫长,而且如果每次代码的微小改动或者想要额外调用某一个函数执行某一个动作就要不断的编译内核的话,就相当于CPU大量的时间都用在了idle,开发效率将会是相当的低。

我们总是希望自己能够掌握自己想要调试的程序的一些状态从而来判断程序有没有正常的工作。在简单的场景下,我们仅需要使用printf大法,就可以打印出程序的轨迹,但是在复杂场景下,似乎没有那么好使,内核中的各个模块大家都一起向你打印出程序的轨迹,每时每刻都有洪水一般的日志输出,运行一小段时间后,光是日志文件都有十几个G,还得筛选出我们需要的,也是挺麻烦的一件事。

即使使用打印的方法,我们可以观察到程序运行的轨迹,但是我们依旧无法让程序去执行一些额外的操作,我们仅仅能知道程序运行到了这里,仅此而已。假如有一些复杂的情景,我们需要修改程序的一些状态量,从而使得代码逻辑切换,打印大法就无能为力了,诶,有一个办法是重新编译内核,然后继续开始无限循环......

有什么办法可以打破这种循环,使得我们能搭建用户态与内核态之间的桥梁,让内核模块的信息能够在用户态中交互?今天我们就来了解一下sysfs和debugfs,两个特殊的in-memory文件系统。

sysfs

简介

Linux 2.6 引入了sysfs文件系统,它把连接在系统上的设备和总线组织为一个分级的文件,它允许内核代码经由一个in-memory文件系统把信息报出到用户进程中,sysfs具有严格的目录组织形式,并构成了内核数据结构的内部组织的基础。在这种文件系统中产生的文件大多数是ASCII文件,通常每个文件有一个值,这些特征保证了被出报的信息的准确性且易被访问使得sysfs成为2.6内核的 最直观、最有用的特性之一。

sysfs是内核对象、属性及它们的相互关系的一种表现机制。一方面,在内核中,提供了sysfs相应功能的编程接口支持,另一方面,在用户态提供了对应的用户接口用于查看和操作所映射的内核对象条目。

内部 <--- sysfs ---> 外部
内核对象 <---> 目录
对象属性 <---> 常规文件
内核关系 <---> 软连接

用户接口

既然sysfs是目录、文件、软连接组成的集合而且它也是一个文件系统,那么就可以在shell中浏览和操作。sysfs的挂载点一般是/sys目录:

/sys$ tree -L 1
.
├── block 块IO子系统
├── bus   总线
├── class 按功能划分的设备类
├── dev   存放块设备和字符设备的主次设备号
│   ├── block 
│   └── char
├── devices  存放连接到系统的所有设备
├── firmware  
├── fs    文件系统
├── hypervisor
├── kernel debugfs挂载在这个目录之下
├── module 一些模块的交互
└── power

/sys路径下是一定数量的目录,这些目录代表了注册了sysfs的主要的子系统。所有目录项相当于sysfs中的中间节点,表示一个内核对象,所有的常规文件表示当前所在的内核对象所具备的某个属性。

属性根据它的读写权限不同,它可以是Read-Only、Write-Only或者Read-Write的。例如我们到/sys/bus/scsi目录下:

/sys/bus/scsi$ ll
total 0
drwxr-xr-x  4 root root    0 11月  3 01:02 ./
drwxr-xr-x 50 root root    0 11月  3 01:02 ../
drwxr-xr-x  2 root root    0 11月  3 01:02 devices/
drwxr-xr-x  4 root root    0 11月 29 19:54 drivers/
-rw-r--r--  1 root root 4096 11月 29 19:54 drivers_autoprobe
--w-------  1 root root 4096 11月 29 19:54 drivers_probe
--w-------  1 root root 4096 11月 29 19:54 uevent

我们可以发现,该路径下存在3个常规文件,是scsi总线的三个属性,这三个属性我们之前也有了解过,如果触发drivers_probe,则系统将会触发对所输入设备名的探测probe,但是它是Write-Only的,如果我们尝试去读取,会出现以下的报错:

$ cat drivers_probe 
cat: drivers_probe: Permission denied


在root用户可以执行,会触发对host1设备的探测:
# echo host1 > drivers_probe

同时,我们还可以发现,所有的属性结点的大小都是4096B,对应一页大小,实际上也是如此,操作系统为每一个属性固定分配一页空间。

但是,和常见的文件系统不同,用户接口是没有办法创建文件或者目录项的!这也很好理解,sysfs文件系统存在的目的是为了进行内核和用户的信息交换,你就算能创建一个文件或者目录项,操作系统又怎么知道你这个属性存在的意义是啥呢,或者说,用户态是无法去创建一个内核对象及其属性的。

即使是root用户,也会permission denied
/sys/bus/scsi: touch a
touch: cannot touch 'a': Permission denied

所以,sysfs所展示的都是内核通过内核编程接口,预先准备好的属性值。

内核编程接口

sysfs的目的是要能与内核空间进行信息的交互,人是活的内核是死的,一定只能让人去交换内核已经设置好的,愿意给你看的信息,这些信息就在内核编程中体现。

对于sysfs的内核常规文件而言,不能把它当作一个我们狭义所说的文件去处理,例如,对于RW文件,并不是说我们今天echo进去的内容就是明天cat出来的内容。在内核中,我们的一个echo和cat操作,将会触发一个已经预设好的函数。对于Read-Only的文件(属性),它将具有一个show方法,将在cat该属性的时候被调用;对于Write-Only的文件(属性),它将具有一个store方法,将在echo该属性的时候被调用;而对于Read-Write的文件(属性),它将同时具有以上的两个方法。

例如,我们不难找到,我们刚才例子中操作调用了对drivers_probe这个属性的store方法:

static ssize_t drivers_probe_store(struct bus_type *bus,
           const char *buf, size_t count)
{
  struct device *dev;
  int err = -EINVAL;


  dev = bus_find_device_by_name(bus, NULL, buf);
  if (!dev)
    return -ENODEV;
  if (bus_rescan_devices_helper(dev, NULL) == 0)
    err = count;
  put_device(dev);
  return err;
}

那内核是如何知道如果要访问这个属性,就应该调用这个函数的呢?sysfs提供了一系列的宏定义。

注册了一个bus的属性,名为drivers_probe
static BUS_ATTR_WO(drivers_probe);
宏定义展开为:
struct bus_attribute bus_attr_drivers_probe = 
{ .attr = { .name = "drivers_probe", .mode = 0200 }, 
.store = drivers_probe_store, }


mode = 0200 表示write-only
mode = 0444 表示read-only
mode = 0644 表示read-write


sysfs提供的宏定义,用于定义对应的结构体:
#define __ATTR(_name, _mode, _show, _store) {        \
  .attr = {.name = __stringify(_name),        \
     .mode = VERIFY_OCTAL_PERMISSIONS(_mode) },    \
  .show  = _show,            \
  .store  = _store,            \
}


#define __ATTR_RO(_name) {            \
  .attr  = { .name = __stringify(_name), .mode = 0444 },    \
  .show  = _name##_show,            \
}


#define __ATTR_WO(_name) {            \
  .attr  = { .name = __stringify(_name), .mode = 0200 },    \
  .store  = _name##_store,          \
}


#define __ATTR_RW(_name) __ATTR(_name, 0644, _name##_show, _name##_store)

对应的结构体被定义好后,调用sysfs提供的方法,将它加入到对应的位置。

例如,drivers_probe属性,它通过bus_create_file方法加入sysfs:
static int add_probe_files(struct bus_type *bus)
{
  int retval;


  retval = bus_create_file(bus, &bus_attr_drivers_probe);
  if (retval)
    goto out;


  retval = bus_create_file(bus, &bus_attr_drivers_autoprobe);
  if (retval)
    bus_remove_file(bus, &bus_attr_drivers_probe);
out:
  return retval;
}
实际调用 sysfs_create_file 方法:
int bus_create_file(struct bus_type *bus, struct bus_attribute *attr)
{
  int error;
  if (bus_get(bus)) {
    error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);
    bus_put(bus);
  } else
    error = -EINVAL;
  return error;
}

sysfs中创建/删除文件、修改文件名、修改文件夹名等,都有对应的方法执行。但是具体的实现细节不是我们这里着重讨论的内容。同时,除了ACSII类型的属性文件,还有二进制类型的属性文件,它额外还支持了mmap方法等,也不是本文的重点内容。

总结

通过内核编程接口定义的属性及配套操作,在用户态通过用户接口,就可以调用到对应预设的方法,显示出属性的内容或者执行特定的功能。

debugfs

debugfs顾名思义则更加适应于调试场景它提供了一种在运行时获取和修改内核状态的机制,它与sysfs十分相似。它通常用于开发和调试过程中,以便开发人员能够查看和修改内核数据。而Sysfs主要用于设备管理和配置,有通用设备模型的影射的意义,它提供了设备的信息和状态,以及设备的控制接口,同时,sysfs限定了每个属性是一个值,debugfs则更加灵活,当然,对于相同的功能而言,一定要在sysfs中实现也是可以的,上线前把调试代码删了就好了。

在debugfs中开发可以更加的放飞自我,反正人家都明说了,我就是来debug的,开发者可以在debugfs里面放入更灵活、更多的信息,它可以做到对一个内核中的值进行修改、提供更多的方法、导出数据块、导出数组等。

启用debugfs

544f8334b24abe3b0896f144bf9667ee.png

debugfs由CONFIG_DEBUG_FS内核编译选项控制,它是一个默认打开的编译选项,所以一般的发行版内核中已经支持了debugfs并且自动挂载:

debugfs on /sys/kernel/debug type debugfs (rw,nosuid,nodev,noexec,relatime)

利用debugfs导出基本数据类型变量

debugfs可以将内核中基本整数类型的变量导出为单个文件,在用户空间中可以直接对其读写(如使用cat、echo命令),只要权限允许即可。支持的类型有:u8, u16, u32, u64, size_t和 bool。其中bool类型在内核中要定义为u32类型,在用户空间中对应的文件内容则显示为Y或N。

static struct dentry *root_d = debugfs_create_dir("exam_debugfs", NULL); //在debugfs根目录下创建新目录exam_debugfs,然会新建目录的目录项指针
static u8 var8;
debugfs_create_u8("var-u8", 0664, root_d, &var8); //在exam_debugfs中创建变量var8对应的文件,名为var-u8,权限为0664
static u32 varbool;
debugfs_create_bool("var-bool", 0664, root_d, &varbool); //bool变量

debugfs挂载的路径需要root权限才可以访问:

21cdc72d18cefc82c2a4f164a7c87c21.png

例如,我们可以查看nvme0n1对应的tags使用情况,就可以通过以下方式:

/sys/kernel/debug/block/nvme0n1/hctx0# cat tags_bitmap 
00000000: 0000 0000 0000 0000 0000 0000 0000 0000
00000010: 0000 0000 0000 0000 0000 0000 0000 0000
00000020: 0000 0000 0000 0000 0000 0000 0000 0000
00000030: 0000 0000 0000 0000 0000 0000 0000 0000
00000040: 0000 0000 0000 0000 0000 0000 0000 0000
00000050: 0000 0000 0000 0000 0000 0000 0000 0000
00000060: 0000 0000 0000 0000 0000 0000 0000 0000
00000070: 0000 0000 0000 0000 0000 0000 0000 0000

翻阅内核代码:

static const struct blk_mq_debugfs_attr blk_mq_debugfs_hctx_attrs[] = {
  {"state", 0400, hctx_state_show},
  {"flags", 0400, hctx_flags_show},
  {"dispatch", 0400, .seq_ops = &hctx_dispatch_seq_ops},
  {"busy", 0400, hctx_busy_show},
  {"ctx_map", 0400, hctx_ctx_map_show},
  {"tags", 0400, hctx_tags_show},
  {"tags_bitmap", 0400, hctx_tags_bitmap_show},
  {"sched_tags", 0400, hctx_sched_tags_show},
  {"sched_tags_bitmap", 0400, hctx_sched_tags_bitmap_show},
  {"io_poll", 0600, hctx_io_poll_show, hctx_io_poll_write},
  {"dispatched", 0600, hctx_dispatched_show, hctx_dispatched_write},
  {"queued", 0600, hctx_queued_show, hctx_queued_write},
  {"run", 0600, hctx_run_show, hctx_run_write},
  {"active", 0400, hctx_active_show},
  {"dispatch_busy", 0400, hctx_dispatch_busy_show},
  {"type", 0400, hctx_type_show},
  {},
};


上方定义了如下的结构体数组:
struct blk_mq_debugfs_attr {
  const char *name;
  umode_t mode;
  int (*show)(void *, struct seq_file *);
  ssize_t (*write)(void *, const char __user *, size_t, loff_t *);
  /* Set either .show or .seq_ops. */
  const struct seq_operations *seq_ops;
};


于是我们刚才调用了tags_bitmap的show函数:
static int hctx_tags_bitmap_show(void *data, struct seq_file *m)
{
  struct blk_mq_hw_ctx *hctx = data;
  struct request_queue *q = hctx->queue;
  int res;


  res = mutex_lock_interruptible(&q->sysfs_lock);
  if (res)
    goto out;
  if (hctx->tags)
    sbitmap_bitmap_show(&hctx->tags->bitmap_tags->sb, m);
  mutex_unlock(&q->sysfs_lock);


out:
  return res;
}

blk-mq对每一个硬件队列都创建了一个硬件队列路径,并配置了上述相同的属性及方法:

void blk_mq_debugfs_register_hctx(struct request_queue *q,
          struct blk_mq_hw_ctx *hctx)
{
  struct blk_mq_ctx *ctx;
  char name[20];
  int i;


  snprintf(name, sizeof(name), "hctx%u", hctx->queue_num);
  hctx->debugfs_dir = debugfs_create_dir(name, q->debugfs_dir);


  debugfs_create_files(hctx->debugfs_dir, hctx, blk_mq_debugfs_hctx_attrs);


  hctx_for_each_ctx(hctx, ctx, i)
    blk_mq_debugfs_register_ctx(hctx, ctx);
}

同样的,debugfs也提供了若干方法,用于实现路径的管理、文件的创建/删除等等,相关的接口搜索引擎中都有,不是本文的重点。

总结

无论是使用sysfs还是debugfs,我们都可以实现内核和用户的信息交换,并可以向内核中注入函数,两者的学习成本实际上非常接近,但是由于sysfs有设备模型的影射含义且debugfs提供的功能更强劲,在我看来,我更倾向于开发者在开发调试过程中使用debugfs,如果能合理地使用debugfs(甚至完全没有必要去了解它们到底是如何工作的,会用就行),显而易见的一定能够提升内核代码开发调试效率。


参考:

https://www.cnblogs.com/pangblog/p/3283508.html

https://zhuanlan.zhihu.com/p/475213617

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

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

相关文章

推荐几款python在线学习和电子书网站

学习python的过程中&#xff0c;虽然下载了很多的电子书&#xff0c;但是在学习过程中基本上都是通过一些在线网站或者在线电子书进行的。 下面给大家推荐几个在线学习教程网站和电子书网站。 《菜鸟教程》 一句话介绍&#xff1a;很多初学者的选择 网址&#xff1a;https:…

kubernetes(K8s)(Namespace、Pod、Deployment、Service资源的基本操作)-04

Namespace Namespace是kubernetes系统中的一种非常重要资源&#xff0c;它的主要作用是用来实现多套环境的资源隔离或者多租户的资源隔离。 默认情况下&#xff0c;kubernetes集群中的所有的Pod都是可以相互访问的。但是在实际中&#xff0c;可能不想让两个Pod之间进行互相的…

Spring三级缓存处理循环依赖的过程

Spring三级缓存 Spring三级缓存是什么&#xff1f; 一级缓存&#xff1a;单例池。存放的是完整的Bean对象。经过完整的生命周期。二级缓存&#xff1a;存放需要提前暴露的Bean对象。也就不完整的Bean对象。需要提前暴露就是指&#xff0c;可能会被循环依赖。(这里可能需要用代…

Day46力扣打卡

最近一直在做以前的题&#xff0c;刷题量都没有怎么增长&#xff0c;感觉自己算法一直不太行&#xff0c;但也只能菜就多练了。 打卡记录 由子序列构造的最长回文串的长度&#xff08;区间DP&#xff09; 链接 第二次刷这道题&#xff0c;相比上回思路来的很快&#xff0c;但…

C# 用代码设置受保护的Excel

写在前面 在导出Excel文件的时候&#xff0c;为了防止文件内容被篡改&#xff0c;这时候就需要对Excel设置工作簿保护和工作表保护&#xff0c;本文使用的是Spire.XLS的免费版本来实现&#xff0c;免费版本是受限的&#xff0c;但是一般情况下已经够用了。 通过NuGet引入Free…

Centos7安装docker、java、python环境

文章目录 前言一、docker的安装二、docker-compose的安装三、安装python3和配置pip3配置python软链接&#xff08;关键&#xff09; 四、Centos 7.6操作系统安装JAVA环境 前言 每次vps安装docker都要看网上的文章&#xff0c;而且都非常坑&#xff0c;方法千奇百怪&#xff0c…

使用 JDBC 连接 Neo4j(头歌)

文章目录 第1关&#xff1a;连接 Neo4j &#xff08;JDBC&#xff09;任务描述相关知识完成 JDBC 环境设置连接 Neo4j 对数据进行查询 编程要求测试说明答案测试前准备代码文件 第1关&#xff1a;连接 Neo4j &#xff08;JDBC&#xff09; 任务描述 本关任务&#xff1a;使用…

Logstash使用指南

介绍 Logstash是一个开源数据收集引擎&#xff0c;具有实时管道功能。它可以动态地将来自不同数据源的数据统一起来&#xff0c;并将数据标准化到你所选择的目的地。尽管Logstash的早期目标是搜集日志&#xff0c;现在它的功能已完全不只于此。任何事件类型都可以加入分析&…

【课设大报告】 基于matlab gui设计的三极管学习系统

目录 设计背景 1.1设计目的 2.设计方法 2.1使用软件工具 2.2设计流程 3.模块界面设计及代码分析 3.1登录 3.2注册 3.3主菜单 3.4三极管介绍 3.5进制转换 3.6单位换算 3.7绘图 4.不足和改善 5.总结 设计背景 1.1设计目的 本程序旨在帮助为了帮助学生了解三极管…

借助文档控件Aspose.Words,在 C# 中比较两个 PDF 文件

在当今的数字世界中&#xff0c;管理和比较文档是一项至关重要的任务&#xff0c;尤其是在商业和法律领域。在 C# 中处理 PDF 文档时&#xff0c;Aspose.Words for .NET 提供了用于比较 PDF 文档的强大解决方案。在这篇博文中&#xff0c;我们将探讨如何在 C# 应用程序中比较 P…

笔记64:Bahdanau 注意力

本地笔记地址&#xff1a;D:\work_file\&#xff08;4&#xff09;DeepLearning_Learning\03_个人笔记\3.循环神经网络\第10章&#xff1a;动手学深度学习~注意力机制 a a a a a a a a a a a

联想SR660V2服务器重置BMC后无法设置BMC管理IP

之前登录后台显示BMC账号密码不对&#xff0c;然后把BMC重置了&#xff0c;重置以后发现无法设置IP地址 联想官方的回复是&#xff1a; 1. 机器AC上电后&#xff0c;需等待7分钟左右, 再开机&#xff0c;让bmc完全启动。 2. 在UEFI及系统下配置bmc网络&#xff0c;需等待3分钟…

人工智能 - 目标检测:发展历史、技术全解与实战

目录 一、早期方法&#xff1a;滑动窗口和特征提取滑动窗口机制工作原理 特征提取方法HOG&#xff08;Histogram of Oriented Gradients&#xff09;SIFT&#xff08;Scale-Invariant Feature Transform&#xff09; 二、深度学习的兴起&#xff1a;CNN在目标检测中的应用CNN的…

基于单片机设计的激光测距仪(采用XKC-Kl200模块)

一、前言 随着科技的不断进步和应用需求的增加&#xff0c;测距仪成为了许多领域必备的工具之一。传统的测距仪价格昂贵、体积庞大&#xff0c;使用起来不够方便。本项目采用STC89C52单片机作为主控芯片&#xff0c;结合XKC-KL200激光测距模块和LCD1602显示器&#xff0c;实现…

长沙电信大楼火灾调查报告发布:系烟头引发。FIS来护航安全

近日&#xff0c;长沙电信大楼的火灾调查报告引起广泛关注。调查发现&#xff0c;火灾是由未熄灭的烟头引发&#xff0c;烟头点燃了室外平台的易燃物&#xff0c;迅速蔓延至整个建筑。这起悲剧再次提醒我们&#xff0c;小小的疏忽可能酿成大灾难。但如果我们能及时发现并处理这…

LeetCode刷题---打家劫舍问题

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C/C》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 一、打家劫舍 题目链接&#xff1a;打家劫舍 题目描述 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定…

Git——使用Git进行程序开发

主要介绍个人开发提交记录的主要流程&#xff0c;包括以下内容&#xff1a; 索引- 提交的暂存区。查看工作的状态和内部变更。如何读取用于描述变更的已扩展统一diff格式。支持查询和交互的提交&#xff0c;修改提交。创建、显示和选择&#xff08;切换&#xff09;分支。切换…

FIORI /N/UI2/FLP 始终在IE浏览器中打开 无法在缺省浏览器中打开

在使用/N/UI2/FLP 打开fiori 启动面板的时候&#xff0c;总是会在IE浏览器中打开&#xff0c;无法在缺省浏览器打开 并且URL中包含myssocntl 无法正常打开 启动面板 这种情况可以取消激活ICF节点/sap/public/myssocntl

(项目已开源)社区求助 哪位大佬能不能帮我 将box1 audio 和 box2 slider滑块 和 box3 歌词滚动区域 进行联动

(项目已开源)社区求助 哪位大佬能不能帮我 将box1 audio 和 box2 slider滑块 和 box3 歌词滚动区域 进行联动 链接&#xff1a;https://pan.baidu.com/s/16lpEW6L5jrHfhsG7EXocLw?pwdkryy 提取码&#xff1a;kryy <!--社区求助 哪位大佬能不能帮我 将box1 audio 和 box2 s…

拒绝烂尾,Arm二期持续更新中,Arm二期Roadmap

感谢大家的支持&#xff0c;感谢大家一直以来的鼓励。更感谢大家的包容。近期又更新了很多课程&#xff0c;这次都是总线和协议相关的&#xff0c;适合软件同学基础扫盲。 硬件的同学请自行考虑&#xff0c;相信你们研究的比我这深。同时呢也在规划一些直播讨论交流课&#xff…