The proc File System:
在这篇摘抄的文章里,我们主要介绍一下此文件系统是如何工作的,以及现有的网络代码是如何使用这个文件系统的,以及我们在程序
中如何写,使用proc entry。
Overview:
proc文件系统在很多程序中被频繁的使用,尽管它被设计成像其它文件系统那样有目录结构和inode,但事实上,是一些注册了的函数
,它们提供了重要的系统变量的信息。
proc目录有很多的子目录-每一个运行中的进程一个以及一些关于子系统的,如文件系统,接口,终端,网络(/proc/net)。在
/proc主目录中也有许多的文件-interrupts,ioports,loadavg等等。在每个进程的子目录中的文件描述此进程的一些信息,如command
line, current working directory, status等等。
内核监视所有对proc文件系统的访问,用特殊的函数来替代普通的文件操作函数。当一个在/proc目录下的文件被创建时,它向内核注
册一些函数来告诉内核如果本文件被读或者写的话,应该如何操作。许多的file只允许读操作,向控制台打印一些状态信息而已。
需要注意的一点是:内核在每次读操作时,呼叫产生状态信息的函数,因此如果没有一次读完的情况下,接下来的读操作可能会覆盖原
有的信息,所以我们最好一次读完状态信息,最好的方法是提供一个大的缓冲区,如PAGE_SIZE字节大小最好,可以一次读完。
Network proc Files:
arp 显示neighbor table(arp_tbl);ip地址/硬件地址,硬件类型,设备,标志等。(arp_get_info():net/ipv4/arp.c)
dev 显示每个注册了的设备的统计信息。
dev_stat 显示接收到/丢弃的包的数量以及FASTROUTE统计信息(dev_proc_stats():net/core/dev.c)
netstat display sync cookie, pruning, and ICMP statistics(netstat_get_info():net/ipv4/proc.c)
raw 为每一个已经打开的原始套接字根据struct proto raw_prot来显示地址,队列以及超时信息。(
get_netinfo():net/ipv4/proc.c)
route 显示FIB表(main_table),the interface, address, gateway, flags, and usage information
(fib_get_procinfo()):net/ipv4/fib_frontend.c)
rt_cache 显示routing cache(rt_hash_table), the interface, address, gateway, usage,source, and other
infomation. (rt_cache_get_info() : net/ipv4/route.c)
sockstat display number of sockets that have been used and some statistics on how many were TCP, UDP,
and RAW(afinet_get_info() : net/ipv4/proc.c)
tcp display address, queue, and timeout information for each open TCP socket form struct proto tcp_prot
(get__netinfo() : net/ipv4/proc.c)
udp display address, queue, and timeout information for each open UDP socket form struct proto udp_prot
(get__netinfo() : net/ipv4/proc.c)
注册proc文件:
这段中我们介绍一个最简单的注册只读proc文件的方法(只适合linux 2.0及其以后的版本),我们可以通过实现file_operations
和inode_operations结构来使它具有更多的功能。但是后者比我们提到的第一种方法复杂,可以看看源代码来得知后者如何实现。下面提及的
方法:定义一个函数,然后registering/unregisteriing这个函数到内核中,提供了绝大多数测试、跟踪系统资源的方法。但要注意:只有
内核能注册proc文件,用户可以创建/安装内核模块(root用户身份)。下面的过程假设已经安装上了linux源代码,并且内核被编译成允许使
用模块。
格式化提供信息的函数(Formatting a Function to Provide Information)
static int read_proc_function(char *buf, char **start, off_t offset, int len, int unused)
以上是linux内核在它读proc文件时要调用的函数原型。其中最重要的一个参数时buf,一个指向缓冲区的指针,供内核储存输出的信
息。其它的参数一般不会改变。
一般来说,这个函数打印一个头部(header),然后遍历表,打印出它的内容(使用sprintf),并返回结果字符串的长度,唯一的限
制是buf不能超过PAGE_SIZE大小(PAGE_SIZE最小是4kb)。
创建proc条目:
因为是文件系统的一部分,所以这个entry需要一个inode,可以使用proc_dir_entry结构来搞定。
#include <linux/proc_fs.h>
struct proc_dir_entry new_proc_entry = {
0, // low_ino - inode number (0 for dynamic)
5, // namelen - length of entry name
"entry", // name
S_IFREG | S_IRUGO // mode
1, // nlinks
0, // uid - owner
0, // gid - group
0, // size - not used
NULL, // ops - inode operations (use default)
&read_proc_function // read_proc - address of read function
// leave rest blank!
}
以上得我们可以复用,只需要改改name, namelen和read_proc字段就可以了。不过内核定义了许多得条目,使用它们自己得inode
number(如PROC_NET_ROUTE),注意不要与之冲突。(在include/linux/proc_fs.h)
注册一个proc条目:
int proc_register(struct proc_dir_entry *dir, struct proc_dir_entry *entry)
int proc_net_register(struct proc_dir_entry *entry)
dir指向一个entry所属的目录-&proc_root和proc_net(include/proc_fs.h)可能是最有用的。entry是指向自己的指针,以上
两个函数是相同的,除了第二个使用proc/net做为目录。返回0(成功)或者EAGAIN(没有可利用的inode)。
卸载一个proc条目:
int proc_unregister(struct proc_dir_entry *dir, int inode)
int proc_net_unregister(int inode)
dir是文件(条目)驻留的目录,inode是此文件的inode编号(proc_dir_entry.low_ino),第二个函数使用/proc/net目录。返
回0(成功)或者EAGAIN(没有可利用的inode)。
例子:
simple_entry.c
/* simple_entry.c
*
* This program provides an example of how to install an entry into the
* /proc File System. All this entry does is display some statistical
* information about IP.
*/
#define MODULE
#include <linux/module.h>
/* proc_fs.h contains proc_dir_entry and register/unregister prototypes */
#include <linux/proc_fs.h>
/* ip.h contains the ip_statistics variable */
#include <net/ip.h>
/* show_ip_stats
* this function is what the /proc FS will call when anything tries to read
* from the file /proc/simple_entry - it puts some of the kernel global
* variable ip_statistics's contents into the return buffer
*/
int
show_ip_stats(char *buf, char **start, off_t offset, int len, int unused)
{
len = sprintf(buf, "Some IP Statistics:/nIP Forwarding is ");
if( ip_statistics.IpForwarding )
len += sprintf(buf+len, "on/n");
else
len += sprintf(buf+len, "off/n");
len += sprintf(buf+len, "Default TTL: %lu/n", ip_statistics.IpDefaultTTL);
len += sprintf(buf+len, "Frag Creates: %lu/n", ip_statistics.IpFragCreates);
/* this could show more... */
return len;
} /* show_ip_stats */