H07_Linux调试方式二:procfs分析
文章目录
内核版本 | Linux4.4 |
---|---|
平台 | |
作者 | sky huang |
procfs也是伪文件系统的一种,并不是一个真正的文件系统。procfs是Linux内核信息的抽象文件接口,大量内核中的信息以及可调参数都被作为常规文件映射到一个目录树中,这样我们就可以简单直接的通过echo或cat这样的文件操作命令对系统信息进行查取和调整。
其说明文档可以参考Documentation/filesystems/proc.txt
proc目录如何创建
在kernel/init/main.c 中
asmlinkage __visible void __init start_kernel(void)
{
...
proc_root_init();
...
}
其中proc_root_init的定义如下:
void __init proc_root_init(void)
{
int err;
proc_init_inodecache();
err = register_filesystem(&proc_fs_type);
...
}
在上面的函数中,proc_init_inodecache函数里面有一个函数[kmem_cache_create]比较关键,可以参考一下。其中err = register_filesystem(&proc_fs_type);就会创建proc目录,其他的proc文件都是在这个路径创建的。
到此内核关于proc创建的过程就梳理完成了。下面我们将分析一些具体的函数使用过程。
proc关键函数分析
proc_mkdir函数
struct proc_dir_entry *proc_mkdir(const char *name,
struct proc_dir_entry *parent)
{
return proc_mkdir_data(name, 0, parent, NULL);
}
name:就是要创建的文件夹名称。
parent:是要创建节点的父节点。也就是要在哪个文件夹之下创建新文件夹,需要将那个文件夹的proc_dir_entry传入。
如果是在/proc目录下创建文件夹,parent为NULL。
例如 /proc/driver 目录就是在函数proc_root_init通过proc_mkdir(“driver”, NULL);创建的。以下这些都是用来创建目录的。
proc_mkdir("fs", NULL);
proc_mkdir("driver", NULL);
proc_mkdir("bus", NULL);
proc_create_data函数
proc_create_data原型说明
struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,
struct proc_dir_entry *parent,
const struct file_operations *proc_fops,
void *data)
入口参数解析:
const char *name: //name为传递想要创建的/proc下的文件名
umode_t mode: //mode为建立的文件所拥有的读写等权限
struct proc_dir_entry *parent: //parent为你要在哪个文件夹下建立名字为name的文件,如:init_net.proc_net是要在/proc/net/下建立文件
const struct file_operations *proc_fops: //proc_fops为struct file_operations *指针(重点)
void *data //data保存似有数据的指针,如不要为NULL
在rtc驱动调用proc_create_data的实例分析
在rtc中,我们会看到devm_rtc_device_register,其整个函数的调用顺序如下:
devm_rtc_device_register
–>rtc_device_register
-->rtc_proc_add_device
-->proc_create_data(“driver/rtc”, 0, NULL, &rtc_proc_fops, rtc);
通过上面的函数之后,就会生成这个文件proc/driver/rtc 当我们cat的时候就会获取到相关的信息。
rk3399_all:/ # cat proc/driver/rtc
[ 4341.358317] rtc:hym8563_rtc_read_time
[ 4341.359020] rtc: hym8563_rtc_read_time: hym8563_rtc_read_time: timertc_time : 13:12:21
rtc_date = 00 : 2017-01-01
alrm_time : 12:00:0017/0
alrm_date : 2017-01-02
alarm_IRQ1/01 : no
alrm_pending : no
update IRQ enabl 13ed : no
periodic IRQ enabled:12: : no
periodic IRQ frequency21
: 1
max user IRQ frequency : 64
24hr : yes
proc_create函数
proc_create函数原型
proc_create函数的底层逻辑其实就是proc_create_data函数。
static inline struct proc_dir_entry *proc_create(
const char *name, umode_t mode, struct proc_dir_entry *parent,
const struct file_operations *proc_fops)
{
return proc_create_data(name, mode, parent, proc_fops, NULL);
}
参数1:name就是要创建的文件名。
参数2:mode是文件的访问权限,以UGO的模式表示(如0666)。
参数3:parent与proc_mkdir中的parent类似。也是父文件夹的proc_dir_entry对象。
参数4:proc_fops就是该文件的操作函数了。
proc_create实例
比如在gt9xx的触摸屏驱动中,就有一个proc_create用来创建一个虚拟的文件,用来跟芯片做在线升级用的。其中read和write就用来对config配置信息读写。
gt91xx_config_proc = proc_create(GT91XX_CONFIG_PROC_FILE, 0664, NULL, &config_proc_ops);
if (gt91xx_config_proc == NULL)
{
GTP_ERROR("create_proc_entry %s failed\n", GT91XX_CONFIG_PROC_FILE);
}
else
{
GTP_INFO("create proc entry %s success", GT91XX_CONFIG_PROC_FILE);
}
static const struct file_operations config_proc_ops = {
.owner = THIS_MODULE,
.read = gt91xx_config_read_proc,
.write = gt91xx_config_write_proc,
};
另外一个用的比较的多的就是proc/meminfo,可以参考下面的内容。
/proc/meminfo源码分析
有时候我们需要了解系统的一些硬件以及运行情况,可以通过cat /proc/meminfo 来获取相关信息。具体每一项就不需要仔细了解了吧。看名字就能猜到。
rk3399_all:/ # cat proc/meminfo
MemTotal: 3938084 kB
MemFree: 2509328 kB
MemAvailable: 3356368 kB
Buffers: 30892 kB
Cached: 879972 kB
SwapCached: 0 kB
Active: 547520 kB
Inactive: 635056 kB
Active(anon): 275828 kB
Inactive(anon): 69592 kB
Active(file): 271692 kB
Inactive(file): 565464 kB
Unevictable: 256 kB
Mlocked: 256 kB
SwapTotal: 520908 kB
SwapFree: 520908 kB
Dirty: 28 kB
Writeback: 0 kB
AnonPages: 272040 kB
Mapped: 476264 kB
Shmem: 73708 kB
Slab: 142904 kB
SReclaimable: 111920 kB
SUnreclaim: 30984 kB
KernelStack: 11840 kB
PageTables: 13548 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 2489948 kB
Committed_AS: 29647408 kB
VmallocTotal: 258867136 kB
VmallocUsed: 0 kB
VmallocChunk: 0 kB
CmaTotal: 16384 kB
CmaFree: 15208 kB
meminfo原型说明
这个 如何创建的呢,可以参考源码fs/proc/meminfo.c
static const struct file_operations meminfo_proc_fops = {
.open = meminfo_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int __init proc_meminfo_init(void)
{
proc_create("meminfo", 0, NULL, &meminfo_proc_fops);
return 0;
}
fs_initcall(proc_meminfo_init);
当我们执行cat /proc/meminfo的时候就会去调用meminfo_proc_open函数,从而获取到对应的物理信息。
在上面的这个代码中,有一个函数fs_initcall,值得我们学习,感兴趣的朋友可以自行学习。
如何通过内核代码修改鲁大师和安兔兔获取到的内存大小
在meminfo.c有如下函数
static int meminfo_proc_show(struct seq_file *m, void *v)
在这个函数中,有如下的代码块,如果我们需要调整内存大小,则只需要调整MemTotal后面的数据大小即可。
seq_printf(m,
"MemTotal: %8lu kB\n"
"MemFree: %8lu kB\n"
"MemAvailable: %8lu kB\n"
"Buffers: %8lu kB\n"
"Cached: %8lu kB\n"
"SwapCached: %8lu kB\n"
"Active: %8lu kB\n"
"Inactive: %8lu kB\n"
"Active(anon): %8lu kB\n"
"Inactive(anon): %8lu kB\n"
"Active(file): %8lu kB\n"
"Inactive(file): %8lu kB\n"
比如,如果我们需要修改为2G内存。
-
```diff diff --git a/fs/proc/meminfo.c b/fs/proc/meminfo.c index 5aa847a..b450f8b 100644 --- a/fs/proc/meminfo.c +++ b/fs/proc/meminfo.c @@ -107,6 +107,7 @@ static int meminfo_proc_show(struct seq_file *m, void *v) #endif , (unsigned long)2048000, K(i.freeram), K(i.bufferram), K(cached), ``` 注意,以上只是用来欺骗上层获取数据的一种方式,仅供测试使用。不建议实际使用。
自己编写c语言获取meminfo信息
我们也可以尝试写一个实际的代码来读取相关信息。
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main(int agrc, char **argv)
{
char mem_buf[128] = {'\0'};
FILE *Stream;
int MemTotal_info;
Stream = popen("cat /proc/meminfo | grep MemTotal | awk {'print $2'}", "r");
fread(mem_buf, sizeof(char), sizeof(mem_buf), Stream);
MemTotal_info = atoi(mem_buf); //将字符串转换为int类型。
printf("MemTotal size:%d(kb)\n", MemTotal_info);
pclose(Stream);
return 0;
}
编译然后运行可以发现以下结果。
ubuntu:~/study/proc$ gcc -o meminfo meminfo.c
ubuntu:~/study/proc$ ./meminfo
MemTotal size:24369396(kb)