Linux文件系统:1、sysfs详解

目录

🍅点击这里查看所有博文

  随着自己工作的进行,接触到的技术栈也越来越多。给我一个很直观的感受就是,某一项技术/经验在刚开始接触的时候都记得很清楚。往往过了几个月都会忘记的差不多了,只有经常会用到的东西才有可能真正记下来。存在很多在特殊情况下有一点用处的技巧,用的不多的技巧可能一个星期就忘了。

  想了很久想通过一些手段把这些事情记录下来。也尝试过在书上记笔记,这也只是一时的,书不在手边的时候那些笔记就和没记一样,不是很方便。

  很多时候我们遇到了问题,一般情况下都是选择在搜索引擎检索相关内容,这样来的也更快一点,除非真的找不到才会去选择翻书。后来就想到了写博客,博客作为自己的一个笔记平台倒是挺合适的。随时可以查阅,不用随身携带。

  同时由于写博客是对外的,既然是对外的就不能随便写,任何人都可以看到。经验对于我来说那就只是经验而已,公布出来说不一定我的一些经验可以帮助到其他的人。遇到和我相同问题时可以少走一些弯路。

  既然决定了要写博客,那就只能认真去写。不管写的好不好,尽力就行。千里之行始于足下,一步一个脚印,慢慢来 ,写的多了慢慢也会变好的。权当是记录自己的成长的一个过程,等到以后再往回看时,就会发现自己以前原来这么菜😂。

  本系列博客所述资料均来自互联网,并不是本人原创(只有博客是自己写的)。出于热心,本人将自己的所学笔记整理并推出相对应的使用教程,方面其他人学习。为国内的物联网事业发展尽自己的一份绵薄之力,没有为自己谋取私利的想法。若出现侵权现象,请告知本人,本人会立即停止更新,并删除相应的文章和代码。

前置知识

介绍

  sysfs 是 Linux 内核提供的一种虚拟文件系统,用于向用户空间提供关于系统硬件设备和驱动程序的信息。它以文件和目录的形式呈现,可以通过读取和写入这些文件来与系统硬件进行交互和配置。

  sysfs被挂载与根文件系统的/sys目录下,其主要目的是提供一种统一的接口,使用户空间程序能够方便地获取和管理系统硬件设备的信息。

root@sdxlemur:/sys# ls
block     bus       class     dev       devices   firmware  fs        kernel    module    power

支持的设备

  它将系统中的硬件设备和驱动程序表示为一个层次结构的目录树,每个设备和驱动程序都有对应的目录和文件。驱动的内核对象所对应的文件被集中存放在/sys/devices/platform目录下。每个设备都有一个唯一的目录,目录的名称是设备的唯一标识符

root@sdxlemur:/sys/devices/platform# ls
0.qcom,rmtfs_sharedmem           6005000.funnel                   680d000.funnel                   7003000.cti                      firmware:scm                     soc:qcom,ioss
0.ql,quectel-power               600f000.tpdm                     6870000.tpdm                     786070.dload_mode                icc-test.1.auto                  soc:qcom,ipa_fws
100000.clock-controller          6010000.cti                      6990000.tpdm                     793000.qrng                      power                            soc:qcom,lpm-levels
117004.qcom,gdsc                 6011000.cti                      69c1000.tpdm                     804000.qcom,sps-dma              qcom_gadget                      soc:qcom,memlat-cpugrp
117f000.dcc_v2                   6012000.cti                      69e1000.cti                      830000.uartb                     reg-dummy                        soc:qcom,mhi_dev_qrtr
143004.qcom,gdsc                 6013000.cti                      69e2000.funnel                   831000.serial                    regulatory.0                     soc:qcom,mhi_net_dev
1468f000.qcom,msm-imem           6014000.cti                      69e4000.cti                      836000.spi                       snd-soc-dummy                    soc:qcom,msm-cpufreq
1468f720.tz-log                  6015000.cti                      69e5000.cti                      837000.i2c                       soc:aop-msg-client               soc:qcom,msm-rtb
15000000.apps-smmu               6016000.cti                      6b00000.cti                      8804000.sdhci                    soc:clock-controller@0           soc:qcom,msm_gsi
1620000.interconnect             6017000.cti                      6b01000.cti                      8fee0000.reserved-memory         soc:cpu-pmu                      soc:qcom,rmnet-ipa
17810008.clock-controller        6018000.cti                      6b02000.cti                      90000000.qseecom                 soc:dummy_sink                   soc:qcom,sdx_ext_ipc
17811000.mailbox                 6019000.cti                      6b03000.cti                      90b0000.syscon                   soc:dummy_source                 soc:qcom,smem
17811000.syscon                  601a000.cti                      6b04000.funnel                   9200000.cache-controller         soc:gpio_leds                    soc:qcom,smp2p-modem@1799000c
17817000.qcom,wdt                601b000.cti                      6b05000.tmc                      9680000.interconnect             soc:hwevent                      soc:qcom,sps
17820000.timer                   601c000.cti                      6b06000.replicator               Fixed MDIO bus.0                 soc:hwlock                       soc:qcom-secure-buffer
17830000.rsc                     601d000.cti                      6b08000.tpda                     a0000000.qcom,pcie0_msi          soc:interconnect                 soc:qcom_smcinvoke
1b00000.nand                     601e000.cti                      6b09000.tpdm                     a600000.ssusb                    soc:mem_dump                     soc:qmi-tmd-devices
1c03000.mhi_dev                  601f000.cti                      6b0a000.tpdm                     b211200.rpmh-master-stats        soc:modem2_etm0                  soc:qmi-ts-sensors
1de0000.qcedev                   6041000.funnel                   6b0b000.tpdm                     c221000.qcom,mpm2-sleep-counter  soc:modem_etm0                   soc:qtee_shmbridge
1de0000.qcrypto                  6042000.funnel                   6b0c000.tpdm                     c222000.tsens                    soc:pcie_card_vreg               soc:quec,ql_lpm
1f40000.syscon                   6045000.funnel                   6b0d000.tpdm                     c310000.qcom,qmp-aop             soc:pil_scm_pas                  soc:reboot_reason
1fc0000.syscon                   6046000.replicator               6b0e000.tgu                      c370000.soc-sleep-stats          soc:psci                         soc:timer
3e00000.qcom,ipa                 6048000.tmc                      6b0f000.csr                      c440000.qcom,spmi                soc:qcom,cc-debug                soc:usb_nop_phy
4080000.qcom,mss                 6800000.tpdm                     6b11000.cti                      f100000.pinctrl                  soc:qcom,cpu0-cpu-ddr-lat        uevent
6001000.csr                      6803000.tpda                     6b12000.qcom,spmi-debug          ff0000.qcom,msm-eud              soc:qcom,glink                   waiting_for_supplier
6002000.stm                      6804000.funnel                   6c28000.tpdm                     ff4000.hsphy                     soc:qcom,glinkpkt
6004000.tpda                     680c000.funnel                   7002000.etm                      ff6000.ssphy                     soc:qcom,ion
读写操作

  在设备目录下,可以找到与设备相关的各种属性文件,这些属性文件包含了设备的各种信息,如设备的名称、供应商 ID、设备 ID、驱动程序信息、设备状态等。以modem为例,可通过4080000.qcom,mss/subsys0/state查询到modem的状态。

root@sdxlemur:/sys/devices/platform# cat ./4080000.qcom,mss/subsys0/state
ONLINE

  除了设备目录,sysfs 还包含了其他一些特殊的目录,如驱动程序目录、总线目录等。驱动程序目录包含了系统中加载的驱动程序的信息,总线目录包含了系统中支持的总线类型和相关设备的信息。

root@sdxlemur:/sys/bus# ls
amba          container     event_source  gprbus        iio           mipi-dsi      msm_subsys    pci_express   scsi          slimbus       spi           usb
clockevents   coresight     genpd         hid           mdio_bus      mmc           nvmem         platform      sdio          soc           spmi          workqueue
clocksource   cpu           gpio          i2c           mhi           mmc_rpmb      pci           rpmsg         serio         soundwire     typec

  同时,用户空间程序也可以通过写入 sysfs 中的文件来配置设备,如修改设备的参数、启用或禁用设备等。下面的例子通过echo指令直接修改dbgctl的值,可以控制qdss日志的输出。

root@sdxlemur:/sys/devices/platform/6048000.tmc/coresight-tmc-etr# cat ./dbgctl
0
root@sdxlemur:/sys/devices/platform/6048000.tmc/coresight-tmc-etr# echo 1 > ./dbgctl
root@sdxlemur:/sys/devices/platform/6048000.tmc/coresight-tmc-etr# cat ./dbgctl
1
添加配置

  上面的示例在内核中实现也非常简单,通过DEVICE_ATTR_RW宏注册一个dbgctl就行了,在用户态生成该属性的操作文件。针对它的读写动作都会被映射到dbgctl_show函数和dbgctl_store函数中。

static ssize_t dbgctl_show(struct device *dev,
			     struct device_attribute *attr, char *buf)
{
	struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
	return scnprintf(buf, PAGE_SIZE, "%d\n", drvdata->dbgctl);
}
static ssize_t dbgctl_store(struct device *dev,struct device_attribute *attr,const char *buf, size_t size)
{
	int ret;
	struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
	pr_info("[%s] dbgctl: %s\n", __func__, buf);
	if (strstr(buf, "0"))
		ret = ql_dbgctl_switch(drvdata,TMC_ETR_DBGCTL0);
	else if (strstr(buf, "1"))
		ret = ql_dbgctl_switch(drvdata,TMC_ETR_DBGCTL1);
	else if (strstr(buf, "2"))
		ret = ql_dbgctl_switch(drvdata,TMC_ETR_DBGCTL2);
	else
		ret = -EINVAL;
	return ret ? ret : size;;
}
static DEVICE_ATTR_RW(dbgctl);

代码分析

注册宏

  针对上面说到的DEVICE_ATTR_RW,我们可以将其展开分析一下。本质上该宏就是定义了一个结构体。

// kernel/msm-5.4/include/linux/device.h
#define DEVICE_ATTR_RW(_name) \
	struct device_attribute dev_attr_##_name = __ATTR_RW(_name)

  进入到__ATTR_RW进一步分析,就知道了前面为什么读写dbgctl的函数分别是dbgctl_showdbgctl_store,在__ATTR中指定了内核属性文件的读写权限,同时在_name后面分别拼接_show_store用于指定文件所对应的读写函数。

// kernel/msm-5.4/include/linux/sysfs.h
#define __ATTR(_name, _mode, _show, _store) {				\
	.attr = {.name = __stringify(_name),				\
		 .mode = VERIFY_OCTAL_PERMISSIONS(_mode) },		\
	.show	= _show,						\
	.store	= _store,						\
}
#define __ATTR_RW(_name) __ATTR(_name, 0644, _name##_show, _name##_store)
驱动调用

  前面定义的结构体内容中的attr字段,在经过一系列的调用后被传输给device_register,用于创建一个设备。

static struct attribute *coresight_tmc_etr_attrs[] = {
	&dev_attr_dbgctl.attr,
	NULL,
};
static const struct attribute_group coresight_tmc_etr_group = {
	.attrs = coresight_tmc_etr_attrs,
};
static const struct attribute_group *coresight_tmc_etf_groups[] = {
	&coresight_tmc_mgmt_group,
	NULL,
};
static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
{
	struct coresight_desc desc = { 0 };
	xxxxx
	desc.type = CORESIGHT_DEV_TYPE_SINK;
	desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
	desc.groups = coresight_tmc_etf_groups;
	desc.ops = &tmc_etb_cs_ops;
	dev_list = &etb_devs;
	xxxxx
	drvdata->csdev = coresight_register(&desc);
	xxxxx
}

struct coresight_device *coresight_register(struct coresight_desc *desc)
{
	xxxxx
	csdev->ops = desc->ops;
	csdev->dev.groups = desc->groups;
	csdev->dev.parent = desc->dev;
	ret = device_register(&csdev->dev);
	if (ret) {
		put_device(&csdev->dev);
	}
	xxxxx
}
文件节点创建

  到目前为止,前面说到的各种函数调用都还只是驱动开发者写的接口。device_register就已经是linux自带的注册方法了,根据其调用栈追踪,可以看到device_create_file中会对attr->storeattr->show进行检查。确认参数没问题既进入到sysfs_create_file函数创建一个和驱动函数相关联的sysfs文件节点。

// kernel/msm-5.4/drivers/base/core.c
int device_register(struct device *dev)
{
	xxxxx
	device_initialize(dev);
	return device_add(dev);
	xxxxx
}
int device_add(struct device *dev)
{
	xxxxx
	error = device_create_file(dev, &dev_attr_uevent);
	if (error)
		goto attrError;
	error = device_add_class_symlinks(dev);
	if (error)
		goto SymlinkError;
	error = device_add_attrs(dev);
	xxxxx
}
int device_create_file(struct device *dev,const struct device_attribute *attr)
{
	int error = 0;
	if (dev) {
		WARN(((attr->attr.mode & S_IWUGO) && !attr->store),
			"Attribute %s: write permission without 'store'\n",
			attr->attr.name);
		WARN(((attr->attr.mode & S_IRUGO) && !attr->show),
			"Attribute %s: read permission without 'show'\n",
			attr->attr.name);
		error = sysfs_create_file(&dev->kobj, &attr->attr);
	}
	return error;
}

小结

  sysfs是基于文件系统的kernel对象视图,当内核创建某个对象时,它就会被注册进 sysfs 里,它的属性就会在 sysfs中出现。

  用户程序可以通过 readdir、read 函数读取这些属性,也可以通过 write 函数修改某些属性。重点在于sysfs中的内容是在内核里创建、销毁,内核控制着 sysfs 的生命周期。可以认为 sysfs 就是这些内核对象的观察窗口。

  那么本篇博客就到此结束了,这里只是记录了一些我个人的学习笔记,其中存在大量我自己的理解。文中所述不一定是完全正确的,可能有的地方我自己也理解错了。如果有些错的地方,欢迎大家批评指正。如有问题直接在对应的博客评论区指出即可,不需要私聊我。我们交流的内容留下来也有助于其他人查看,说不一定也有其他人遇到了同样的问题呢😂。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

遇雪长安

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值