proc文件系统与ctl_table

2023-12-05 12:32
文章标签 table 文件系统 proc ctl

本文主要是介绍proc文件系统与ctl_table,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

三、系统参数数据结构的组织



系统参数的数据结构是按层次结构进行组织的,最高一层是root,下面的一层依次

kernel、net、proc、fs、debug、dev。它们下面再分层,例如net下面又分为core

、ipv4等,这样依次类推,直到最底层。

树中各个层次的结点,不管是父结点还是子结点,都用一个结构ctl_table组成的数

组来表示。所有的系统参数构成一个由结构ctrl_table描述的具有层次结构的树。

下面是ctl_table的定义:



struct ctl_table

{

>>>>结点标识,同一层的结点用不同的数字来标识

int ctl_name; /* Binary ID */

>>>>如果支持proc,则该结点在/proc/sys下面的proc项目名称

const char *procname; /* Text ID for /proc/sys, or zero */

>>>>实际的系统参数在内核中的数据结构

void *data;

int maxlen;

mode_t mode;

>>>>子结点的clt_table结构指针

ctl_table *child;

>>>>对/proc/sys下面的文件读写的时候将调用这个例程

proc_handler *proc_handler; /* Callback for text formatting */

>>>>用sysctl读写系统参数时候,将调用这个例程

ctl_handler *strategy; /* Callback function for all r/w */

>>>>指向在/proc/sys中的结点(proc文件系统数据结构)

struct proc_dir_entry *de; /* /proc control block */

void *extra1;

void *extra2;

};



父子结点之间层次的维护就是靠上面的child域。先看看所有系统参数在内核中的层

次结构:

struct ctl_table_header

{

ctl_table *ctl_table;

struct list_head ctl_entry;

};



ctl_table_header结构用来维护ctr_table树的双向链表表头.



static struct ctl_table_header root_table_header =

{ root_table, LIST_HEAD_INIT(root_table_header.ctl_entry) };



>>>>这里定义了root的ctl_table数组

static ctl_table root_table[] = {

{CTL_KERN, "kernel", NULL, 0, 0555, kern_table},

{CTL_VM, "vm", NULL, 0, 0555, vm_table},

{CTL_NET, "net", NULL, 0, 0555, net_table},

{CTL_PROC, "proc", NULL, 0, 0555, proc_table},

{CTL_FS, "fs", NULL, 0, 0555, fs_table},

{CTL_DEBUG, "debug", NULL, 0, 0555, debug_table},

{CTL_DEV, "dev", NULL, 0, 0555, dev_table},

{0}

};



>>>>下面是net_table数组

ctl_table net_table[] = {

{NET_CORE, "core", NULL, 0, 0555, core_table},

{NET_802, "802", NULL, 0, 0555, e802_table},

{NET_ETHER, "ethernet", NULL, 0, 0555, ether_table},

{NET_IPV4, "ipv4", NULL, 0, 0555, ipv4_table},

....................................................

{0}

};



上面的ctl_table都是目录结点,所以data域并不对应于内存中的一个数据结构,下面的

是子结点,它们的data域都对应内存中的一个数据结构,例如字符数组、整数等:



>>>>下面是ipv4_table



ctl_table ipv4_table[] = {

{NET_IPV4_TCP_TIMESTAMPS, "tcp_timestamps",

&sysctl_tcp_timestamps, sizeof(int), 0644, NULL,

&proc_dointvec},

........................

{NET_IPV4_FORWARD, "ip_forward",

&ipv4_devconf.forwarding, sizeof(int), 0644, NULL,

&ipv4_sysctl_forward,&ipv4_sysctl_forward_strategy},



{NET_IPV4_DEFAULT_TTL, "ip_default_ttl",

&sysctl_ip_default_ttl, sizeof(int), 0644, NULL,

&proc_dointvec},

..........................

{0}

};





四、基本例程的分析



1.register_sysctl_table



内核导出了register_sysclt_table函数,通过它可以将一个ctl_table注册到系统参数

树中,先看看它的实现过程:



struct ctl_table_header *register_sysctl_table(ctl_table * table,

int insert_at_head)

{

struct ctl_table_header *tmp;

>>>>分配一个ctl_table_header结构

tmp = kmalloc(sizeof(struct ctl_table_header), GFP_KERNEL);

......................

tmp->ctl_table = table;

INIT_LIST_HEAD(&tmp->ctl_entry);

>>>>将其链接到list_head的双向链表中

if (insert_at_head)

list_add(&tmp->ctl_entry, &root_table_header.ctl_entry);

else

list_add_tail(&tmp->ctl_entry, &root_table_header.ctl_entry);



>>>>注册到/proc/sys中

#ifdef CONFIG_PROC_FS

>>>>系统初始化的时候,proc_sys_root = proc_mkdir("sys", 0);

>>>>添加一项/proc/sys目录项

register_proc_table(table, proc_sys_root);

#endif

return tmp;

}



2.register_proc_table



/* Scan the sysctl entries in table and add them all into /proc */

static void register_proc_table(ctl_table * table, struct proc_dir_entry *ro

ot)

{

struct proc_dir_entry *de;

int len;

mode_t mode;

for (; table->ctl_name; table++) {

/* Can't do anything without a proc name. */

if (!table->procname)

continue;

/* Maybe we can't do anything with it... */

>>>>如果没有处理句柄,而且为子结点,则处理ctl_table数组的下一个元素。

>>>>对子结点是一定要处理句柄的

if (!table->proc_handler && !table->child) {

printk(KERN_WARNING "SYSCTL: Can't register %s/n",

table->procname);

continue;

}

len = strlen(table->procname);

mode = table->mode;

de = NULL;

>>>>有处理句柄,为一个子结点

if (table->proc_handler)

mode |= S_IFREG;

else {

>>>>没有处理句柄,则为一个目录结点

mode |= S_IFDIR;

>>>>查找有没有匹配的项,有则退出循环

for (de = root->subdir; de; de = de->next) {

if (proc_match(len, table->procname, de))

break;

}

/* If the subdir exists already, de is non-NULL */

}

>>>>对子结点,在相应的proc目录下添加一项

if (!de) {

de = create_proc_entry(table->procname, mode, root);

if (!de)

continue;

>>>>proc数据结构的data域指向该ctl_table结构

de->data = (void *) table;



>>>>对于子结点,赋值proc的文件操作例程

if (table->proc_handler) {

de->proc_fops = &proc_sys_file_operations;

de->proc_iops = &proc_sys_inode_operations;

}

}

table->de = de;



>>>>如果为目录,则递归添加它下面的子结点

if (de->mode & S_IFDIR)

register_proc_table(table->child, de);

}

}





3. create_proc_entry



该函数创建一个proc_dir_entry结构,然后注册到/proc/sys目录下

struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,

struct proc_dir_entry *parent)

{

struct proc_dir_entry *ent = NULL;

const char *fn = name;

int len;

len = strlen(fn);



>>>>分配一个proc_dir_entry结构,名称由name指向,并放在该结构的后面

ent = kmalloc(sizeof(struct proc_dir_entry) + len + 1, GFP_KERNEL);

if (!ent)

goto out;

memset(ent, 0, sizeof(struct proc_dir_entry));

>>>>填写名称

memcpy(((char *) ent) + sizeof(*ent), fn, len + 1);

>>>>name域指向名称所在的内存地址

ent->name = ((char *) ent) + sizeof(*ent);

ent->namelen = len;

>>>>对目录结点

if (S_ISDIR(mode)) {

if ((mode & S_IALLUGO) == 0)

mode |= S_IRUGO | S_IXUGO;



>>>>为proc赋文件操作接口例程

ent->proc_fops = &proc_dir_operations;

ent->proc_iops = &proc_dir_inode_operations;

ent->nlink = 2;

} else {

if ((mode & S_IFMT) == 0)

mode |= S_IFREG;

if ((mode & S_IALLUGO) == 0)

mode |= S_IRUGO;

ent->nlink = 1;

}

ent->mode = mode;



>>>>将这一项注册到proc中

proc_register(parent, ent);

out:

return ent;

}





4.proc_sys_file_operations中的处理例程



struct file_operations proc_sys_file_operations = {

read: proc_readsys,

write: proc_writesys,

};

它们都调用下面当局,不同在于第一个参数不同,对proc_readsys为0,对proc_write为

1.



static ssize_t do_rw_proc(int write, struct file * file, char * buf,

size_t count, loff_t *ppos)

{

struct proc_dir_entry *de;

struct ctl_table *table;

de = (struct proc_dir_entry*) file->f_dentry->d_inode->u.generic_ip;

if (!de || !de->data)

return -ENOTDIR;

table = (struct ctl_table *) de->data;

......................

>>>>最终调用了clt_table的处理例程。

error = (*table->proc_handler) (table, write, file, buf, &res);

}



五、sysctl的分析



extern asmlinkage long sys_sysctl(struct __sysctl_args *args)

{

struct __sysctl_args tmp;

>>>>将程序的__sysctl_args传到内核空间

>>>> struct __sysctl_args {

>>>> int *name;

>>>> int nlen;

>>>> void *oldval;

>>>> size_t *oldlenp;

>>>> void *newval;

>>>> size_t newlen;

>>>> unsigned long __unused[4];

>>>> };

if (copy_from_user(&tmp, args, sizeof(tmp)))

return -EFAULT;

error = do_sysctl(tmp.name, tmp.nlen, tmp.oldval, tmp.oldlenp,

tmp.newval, tmp.newlen);

......................

}



sysctl调用do_sysctl:



int do_sysctl(int *name, int nlen, void *oldval, size_t *oldlenp,

void *newval, size_t newlen)

{

struct list_head *tmp;

........................

tmp = &root_table_header.ctl_entry;

do {

>>>>根据指向ctl_table的指针tmp,得到指向ctl_table_head的指针

>>>>方法是将tmp指针向低地址移动,因为ctl_table在ctl_table_head的高地址部分

struct ctl_table_header *head =

list_entry(tmp, struct ctl_table_header, ctl_entry);

int error = parse_table(name, nlen, oldval, oldlenp,

newval, newlen, head->ctl_table,

&context);

.........................

tmp = tmp->next;

} while (tmp != &root_table_header.ctl_entry);

return -ENOTDIR;

}



static int parse_table(int *name, int nlen,

void *oldval, size_t *oldlenp,

void *newval, size_t newlen,

ctl_table *table, void **context)

{

int n;

>>>>下面的repeat根据name数组,按层次编历ctl_table树,从最高层一直到底层

>>>>直到找到匹配的子结点

repeat:

if (!nlen)

return -ENOTDIR;

>>>>取得名称数组中的一项

if (get_user(n, name))

return -EFAULT;

>>>>对同层次中的所有的结点,查找与n(name)相同的项

for ( ; table->ctl_name; table++) {

if (n == table->ctl_name || table->ctl_name == CTL_ANY) {

int error;

>>>>是目录,还需要继续编历,得到下层的结点后,跳转到repeat

if (table->child) {

if (ctl_perm(table, 001))

return -EPERM;

if (table->strategy) {

error = table->strategy(

table, name, nlen,

oldval, oldlenp,

newval, newlen, context);

if (error)

return error;

}

name++;

nlen--;

table = table->child;

goto repeat;

}

>>>>找到最终的子结点,则调用do_sysctl_strategy

error = do_sysctl_strategy(table, name, nlen,

oldval, oldlenp,

newval, newlen, context);

return error;

}

}

return -ENOTDIR;

}





int do_sysctl_strategy (ctl_table *table,

int *name, int nlen,

void *oldval, size_t *oldlenp,

void *newval, size_t newlen, void **context)

{

>>>>如果ctl_table定义了strategy处理例程,就调用它



if (table->strategy) {

rc = table->strategy(table, name, nlen, oldval, oldlenp,

newval, newlen, context);

if (rc < 0)

return rc;

if (rc > 0)

return 0;

}

>>>>如果是获取

copy_to_user(oldval, table->data, len))

>>>>如果是设置

copy_from_user(table->data, newval, len))

}

这篇关于proc文件系统与ctl_table的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/457574

相关文章

Golang使用minio替代文件系统的实战教程

《Golang使用minio替代文件系统的实战教程》本文讨论项目开发中直接文件系统的限制或不足,接着介绍Minio对象存储的优势,同时给出Golang的实际示例代码,包括初始化客户端、读取minio对... 目录文件系统 vs Minio文件系统不足:对象存储:miniogolang连接Minio配置Min

vue2实践:el-table实现由用户自己控制行数的动态表格

需求 项目中需要提供一个动态表单,如图: 当我点击添加时,便添加一行;点击右边的删除时,便删除这一行。 至少要有一行数据,但是没有上限。 思路 这种每一行的数据固定,但是不定行数的,很容易想到使用el-table来实现,它可以循环读取:data所绑定的数组,来生成行数据,不同的是: 1、table里面的每一个cell,需要放置一个input来支持用户编辑。 2、最后一列放置两个b

使用jetty和mongodb做个简易文件系统

使用jetty和mongodb做个简易文件系统 - ciaos 时间 2014-03-09 21:21:00   博客园-所有随笔区 原文   http://www.cnblogs.com/ciaos/p/3590662.html 主题  MongoDB  Jetty  文件系统 依赖库: 1,jetty(提供http方式接口) 2,mongodb的java驱动(访问mo

通过Ajax请求后台数据,返回JSONArray(JsonObject),页面(Jquery)以table的形式展示

点击“会商人员情况表”,弹出层,显示一个表格,如下图: 利用Ajax和Jquery和JSONArray和JsonObject来实现: 代码如下: 在hspersons.html中: <!DOCTYPE html><html><head><meta charset="UTF-8"><title>会商人员情况表</title><script type="text/javasc

UVa 10820 Send a Table (Farey数列欧拉函数求和)

这里先说一下欧拉函数的求法 先说一下筛选素数的方法 void Get_Prime(){ /*筛选素数法*/for(int i = 0; i < N; i++) vis[i] = 1;vis[0] = vis[1] = 0;for(int i = 2; i * i < N; i++)if(vis[i]){for(int j = i * i; j < N; j += i)vis[j] =

css-table

设置table的文字不换行:给th,td添加white-space: nowrap; 设置单元格内容及其边框的距离:使用html的cellpadding属性,还有一种方式设置padding。在CSS中,table, th, td{padding:0;}效果等同于cellpadding="0″。 设置table的单元格边距:border-spacing如果定义一个 length 参数,那么定义的是水

Kubernetes集群安装、配置glusterfs文件系统

环境介绍: 3台Centos 7.4系统节点,已经部署好Kubernetes,同时复用这3台机器作为gluster存储节点: hostIPK8s roleg1-nasp12.12.10.11master + nodeg3-nasp12.12.10.13nodeg4-nasp12.12.10.14node 安装、配置glusterfs: 在物理主机上采用yum安装的方式,步骤如下: 1

react antd table expandable defaultExpandAllRows 不生效问题

原因:defaultExpandAllRows只会在第一次渲染时触发 解决方案:渲染前判断table 的datasource 数据是否已准备好 {pageList.length > 0 ? (<TablerowSelection={rowSelection}columns={columns}dataSource={pageList}style={{ marginTop: 24 }}pagina

Ubuntu构建只读文件系统

本文介绍Ubuntu构建只读文件系统。 嵌入式系统使用过程中,有时会涉及到非法关机(比如直接关机,或意外断电),这可能造成文件系统损坏,为了提高系统的可靠性,通常将根文件系统设置为只读,将其他需要读写的数据放置在另外开辟的一个磁盘分区,本文针对嵌入式系统常用的Ubuntu操作系统构建只读文件系统。 1.基本原理 1)OverlayFS简介 OverlayFS(Overlay File Sy

el-table 封装表格(完整代码-实时更新)

最新更新时间: 2024年9月6号 1. 添加行内编辑、表头搜索 <template><!-- 简单表格、多层表头、页码、没有合并列行 --><div class="maintenPublictable"element-loading-background="rgba(255,255,255,0.5)"><!--cell-style 改变某一列行的背景色 --><!-- tree-props