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))

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值