1. /proc文件系统简介
/proc文件系统,这是内核模块和系统交互的两种主要方式之一。
/proc文件系统也是Linux操作系统的特色之一。
/proc文件系统不是普通意义上的文件系统,它是一个伪文件系统。
通过/proc,可以用标准Unix系统调用(比如open()、read()、write()、 ioctl()等等)访问进程地址空间。
可以用cat、more等命令查看/proc文件中的信息。
用户和应用程序可以通过/proc得到系统的信息,并可以改变内核的某些参数。
当调试程序或者试图获取指定进程状态的时候,/proc文件系统将是你强有力的支持者。通过它可以创建更强大的工具,获取更多信息wugang@wugang-desktop:/$ ls -l /proc/cpuinfo
-r--r--r-- 1 root root 0 2009-03-03 17:21 /proc/cpuinfo
wugang@wugang-desktop:/$ cat /proc/cpuinfo
processor : 0
vendor_id : GenuineIntel
cpu family : 15
model : 1
model name : Intel(R) Pentium(R) 4 CPU 1.70GHz
stepping : 2
cpu MHz : 1700.091
cache size : 256 KB
fdiv_bug : no
hlt_bug : no
f00f_bug : no
coma_bug : no
fpu : yes
fpu_exception : yes
cpuid level : 2
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm up pebs bts sync_rdtsc
bogomips : 3404.03
clflush size : 64
|
有些 /proc 的文件是经过编码的,不同的工具可以被用来解释这些编码过的信息并输出成可读的形式。这样的工具包括:'top', 'ps', 'apm' 等。
proc 文件系统可以被用于收集有用的关于系统和运行中的内核的信息。下面是一些重要的文件:
/proc/cpuinfo - CPU 的信息 (型号, 家族, 缓存大小等)
/proc/meminfo - 物理内存、交换空间等的信息
/proc/mounts - 已加载的文件系统的列表
/proc/devices - 可用设备的列表
/proc/filesystems - 被支持的文件系统
/proc/modules - 已加载的模块
/proc/version - 内核版本
/proc/cmdline - 系统启动时输入的内核命令行参数
2 /proc文件系统源码分析
在分析/proc文件系统时:先得对VFS文件系统模式,inode数据结构,VFS超级块有一定的认识。
在linux代码树中,所有文件系统放在/linux/fs目录中。2.1 /proc文件系统的注册
void __init proc_root_init(void)
{
int err = proc_init_inodecache();
if (err)
return;
err = register_filesystem(&proc_fs_type);
if (err)
return;
proc_mnt = kern_mount(&proc_fs_type);
err = PTR_ERR(proc_mnt);
if (IS_ERR(proc_mnt)) {
unregister_filesystem(&proc_fs_type);
return;
}
proc_misc_init();
proc_net = proc_mkdir("net", NULL);
proc_net_stat = proc_mkdir("net/stat", NULL);
#ifdef CONFIG_SYSVIPC
proc_mkdir("sysvipc", NULL);
#endif
proc_root_fs = proc_mkdir("fs", NULL);
proc_root_driver = proc_mkdir("driver", NULL);
proc_mkdir("fs/nfsd", NULL);
#if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE)
proc_mkdir("openprom", NULL);
#endif
proc_tty_init();
#ifdef CONFIG_PROC_DEVICETREE
proc_device_tree_init();
#endif
proc_bus = proc_mkdir("bus", NULL);
proc_sys_init();
}
|
可以看到,proc文件系统注册的过程:
1> 调用proc_init_inodecache()创建缓存。
2> 调用register_filesystem(&proc_fs_type),将proc文件类型加入到文件类型的单向链表中,如果发生错误,则返回。
3> 在proc目录下,创建文件目录。
2.2 /proc文件系统操作方法
1) proc_file_lseek
这个函数,用来实现lseek系统调用,其功能是设置file结构的->f_pos域。
2) proc_file_read
这个函数是file_operations结构中的成员,在后面我们将看到,在proc_dir_entry结构中实现的 file_operations和inode_operations将链接至VFS的inode中,因此,该函数将用来实现read系统调用。在这个函数中,首先根据file结构,得到相应的inode,然后由
3) proc_file_write与proc_file_read代码实现类似。
static ssize_t
proc_file_read(struct file *file, char __user *buf, size_t nbytes,
loff_t *ppos)
{
struct inode * inode = file->f_path.dentry->d_inode;
char *page;
ssize_t retval=0;
int eof=0;
ssize_t n, count;
char *start;
struct proc_dir_entry * dp;
unsigned long long pos;
/*
* Gaah, please just use "seq_file" instead. The legacy /proc
* interfaces cut loff_t down to off_t for reads, and ignore
* the offset entirely for writes..
*/
pos = *ppos;
if (pos > MAX_NON_LFS)
return 0;
if (nbytes > MAX_NON_LFS - pos)
nbytes = MAX_NON_LFS - pos;
dp = PDE(inode);
if (!(page = (char*) __get_free_page(GFP_KERNEL)))
return -ENOMEM;
while ((nbytes > 0) && !eof) {
count = min_t(size_t, PROC_BLOCK_SIZE, nbytes);
start = NULL;
if (dp->get_info) {
/* Handle old net routines */
n = dp->get_info(page, &start, *ppos, count);
if (n < count)
eof = ;
} else if (dp->read_proc) {
n = dp->read_proc(page, &start, *ppos,
count, &eof, dp->data);
} else
break;
if (n == 0) /* end of file */
break;
if (n < 0) { /* error */
if (retval == 0)
retval = n;
break;
}
|
if (start == NULL) {
if (n > PAGE_SIZE) {
printk(KERN_ERR
"proc_file_read: Apparent buffer overflow!\n");
n = PAGE_SIZE;
}
n -= *ppos;
if (n <= 0)
break;
if (n > count)
n = count;
start = page + *ppos;
} else if (start < page) {
if (n > PAGE_SIZE) {
printk(KERN_ERR
"proc_file_read: Apparent buffer overflow!\n");
n = PAGE_SIZE;
}
if (n > count) {
/*
* Don't reduce n because doing so might
* cut off part of a data block.
*/
printk(KERN_WARNING
"proc_file_read: Read count exceeded\n");
}
} else /* start >= page */ {
unsigned long startoff = (unsigned long)(start - page);
if (n > (PAGE_SIZE - startoff)) {
printk(KERN_ERR
"proc_file_read: Apparent buffer overflow!\n");
n = PAGE_SIZE - startoff;
}
if (n > count)
n = count;
}
n -= copy_to_user(buf, start < page ? page : start, n);
if (n == 0) {
if (retval == 0)
retval = -EFAULT;
break;
}
*ppos += start < page ? (unsigned long)start : n;
nbytes -= n;
buf += n;
retval += n;
}
free_page((unsigned long) page);
return retval;
}
1> 其中dp=PDE(inode),返回inode数据结构的首地址。相当于dp指向inode结构。
2> 判断pos是否有地址越出MAX_NON_LFS,和读出的数据地址大于MAX_NON_LFS。
3> 根据系列的判断,最后定位到copy_to_user(buf, start < page ? page : start, n)。把数据从内核存储空间拷贝到用户空间。
|
阅读(5597) | 评论(1) | 转发(1) |