没有时间把一件事情做好,却有时间把一件事情反复做!
全部博文(191)
分类: LINUX
2015-12-17 17:29:37
原文地址:proc文件系统相关内容 作者:visualfan
proc文件系统和sysfs文件系统类似,是虚拟文件系统,存在于内存中,用于内核和用户程序交互等,查看内核信息。
基于/proc文件系统如上所述的特殊性,其内的文件也常被称作虚拟文件,并具有一些独特的特点。例如,其中有些文件虽然使用查看命令查看时会返回大量信息,但文件本身的大小却会显示为0字节。此外,这些特殊文件中大多数文件的时间及日期属性通常为当前系统时间和日期,这跟它们随时会被刷新(存储于RAM中)有关。
为了查看及使用上的方便,这些文件通常会按照相关性进行分类存储于不同的目录甚至子目录中,如/proc/scsi目录中存储的就是当前系统上所有SCSI设备的相关信息,/proc/N中存储的则是系统当前正在运行的进程的相关信息,其中N为正在运行的进程(可以想象得到,在某进程结束后其相关目录则会消失)。
大多数虚拟文件可以使用文件查看命令如cat、more或者less进行查看,有些文件信息表述的内容可以一目了然,但也有文件的信息却不怎么具有可读性。其中用户程序ps、top、free等的内容就来自proc文件系统中的信息。
一、内核信息
二、用户进程信息
/proc目录中包含许多以数字命名的子目录,这些数字表示系统当前正在运行进程的进程号,里面包含对应进程相关的多个信息文件。
三、proc编程
与sysfs虚拟文件系统相比,proc的使用没有sysfs那么组织严谨,更加随意,因此在内核模块中用proc文件系统与用户空间进行信息交互还是很方便的。
在内核模块中使用proc,需要包含头文件
其中常用的函数包括:
struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *parent);
name: 文件名称
mode:文件属性,常用S_IWUSR | S_IRUGO
parent:父目录,即创建的文件在那个目录下,若为NULL,则在/proc目录下创建文件
2. 创建一个只读的文件:
struct proc_dir_entry* create_proc_read_entry(const char* name, mode_t mode, struct proc_dir_entry* parent, read_proc_t* read_proc, void* data);
read_proc是读操作回调函数,data是多个文件共享一个操作函数时区分文件的私有数据
3. 创建一个目录:
struct proc_dir_entry *proc_mkdir(const char *name, struct proc_dir_entry *parent);
参数意义同上,只是这个函数在proc文件系统中创建的是一个目录
4. 创建一个符号链接:
struct proc_dir_entry* proc_symlink(const chat *name, struct proc_dir_entry *parent, const char *dest)
在parent目录下创建符号链接,效果如同:ln –s dest name
5. 创建一个设备节点
struct proc_dir_entry* proc_mknod(const char *name, mode_t mode, struct proc_dir_entry *parent, kdev_t rdev)
在parent目录下创建一个设备节点,其中rdev可以由MKDEV宏生成,mode参数必须包含S_IFBLK或S_IFCHR表示创建的是块设备还是字符设备。
效果如同:mknod --mode=mode name rdev
6. 移除proc文件系统中的入口
void remove_proc_entry(const char *name, struct proc_dir_entry *parent);
上面创建的所有对象,都可以通过这个函数移除
为了对proc文件系统中的文件进行读写操作,需要设置相应的回调函数,在返回的对应proc文件的struct proc_dir_entry结构中,设置其中的:
entry->read_proc和entry->write_proc参数。
其中entry->read_proc的函数原型为:
int read_func(char* page, char** start, off_t off, int count, int* eof, void* data); 从内核读取数据
内核返回的数据需要写入page,其中page为内核地址,off和count分别是写入在page中的偏移地址和可以写入的最大字节,则两个参数主要是用于more和less命令,一般若内容较少可以忽略这两个参数。若这两个参数被使用,则需要设置eof为1来表示已到达文件尾部。
data参数用于同一个read_func函数为多个proc文件服务时,进行区分所操作的proc文件,并可以存放私有数据。
这个函数的返回值是复制到page内存中的字节数。
entry->write_proc的函数原型为:
int write_func(struct file* file, const char* buffer, unsigned long count, void* data); 写入数据到内核
参数count表示可以从buffer中访问的最大字节数,由于buffer是用户空间内存,需要使用copy_from_user复制buffer中的数据到内核空间。file参数一般被忽略,而data同上也是用于区分多个文件时的操作对象。
参数data的使用实例一般为:
struct proc_dir_entry* entry;
struct my_file_data *file_data; //文件数据
file_data = kmalloc(sizeof(struct my_file_data), GFP_KERNEL); //分配空间
entry->data = file_data; //这里是重点,需要将其赋值给对应proc文件的proc_dir_entry结构,在read和write是才能区分
int foo_read_func(char *page, char **start, off_t off, int count, int *eof, void *data)
{
int len;
if(data == file_data) {
/* 操作对应的文件*/
}
else {
/* 操作其他文件*/
}
return len;
}
需要注意的是,若entry->data的参数是动态分配的,在调用remove_proc_entry时,需要先将对应的data空间释放。
#include
#include
#include
#include
#include
#include#define MODULE_VERSION "1.0"
#define MODULE_NAME "procfs_example"
#define FOOBAR_LEN 8/*文件私有数据结构*/
struct fb_data_t {
char name[FOOBAR_LEN + 1];
char value[FOOBAR_LEN + 1];
};static struct proc_dir_entry *example_dir, *foo_file, *bar_file, *jiffies_file, *tty_device, *symlink;
struct fb_data_t foo_data, bar_data;/*读取内核jiffies*/
static int proc_read_jiffies(char *page, char **start, off_t off, int count, int *eof, void *data)
{
int len;len = sprintf(page, "jiffies = %ld\n", jiffies);
return len;
}/*读取foo和bar文件*/
static int proc_read_foobar(char *page, char **start, off_t off, int count, int *eof, void *data)
{
int len;
struct fb_data_t *fb_data = (struct fb_data_t *)data;len = sprintf(page, "%s = ’%s’\n", fb_data->name, fb_data->value);
return len;
}/*写入foo和bar文件*/
static int proc_write_foobar(struct file *file, const char *buffer, unsigned long count, void *data)
{
int len;
struct fb_data_t *fb_data = (struct fb_data_t *)data;if(count > FOOBAR_LEN)
len = FOOBAR_LEN;
else
len = count;
if(copy_from_user(fb_data->value, buffer, len))
return -EFAULT;fb_data->value[len] = ’\0’;
return len;
}/*模块初始化函数*/
static int __init init_procfs_example(void)
{
int rv = 0;
/* 在/proc下创建一个父目录 */
example_dir = proc_mkdir(MODULE_NAME, NULL);
if(example_dir == NULL) {
rv = -ENOMEM;
goto out;
}/* 创建读取jiffies的proc文件 */
jiffies_file = create_proc_read_entry("jiffies", 0444, example_dir, proc_read_jiffies, NULL);
if(jiffies_file == NULL) {
rv = -ENOMEM;
goto no_jiffies;
}/* 创建foo和bar文件,使用同一读写回调函数 */
foo_file = create_proc_entry("foo", 0644, example_dir);
if(foo_file == NULL) {
rv = -ENOMEM;
goto no_foo;
}
/*设置foo文件的私有数据和读写回调函数*/
strcpy(foo_data.name, "foo");
strcpy(foo_data.value, "foo");
foo_file->data = &foo_data;
foo_file->read_proc = proc_read_foobar;
foo_file->write_proc = proc_write_foobar;
/* 创建bar文件 */
bar_file = create_proc_entry("bar", 0644, example_dir);
if(bar_file == NULL) {
rv = -ENOMEM;
goto no_bar;
}/*设置bar私有数据和读写回调函数*/
strcpy(bar_data.name, "bar");
strcpy(bar_data.value, "bar");
bar_file->data = &bar_data;
bar_file->read_proc = proc_read_foobar;
bar_file->write_proc = proc_write_foobar;/* 创建设备文件 */
tty_device = proc_mknod("tty", S_IFCHR | 0666, example_dir, MKDEV(5, 0));
if(tty_device == NULL) {
rv = -ENOMEM;
goto no_tty;
}/* 创建符号链接 */
symlink = proc_symlink("jiffies_too", example_dir, "jiffies");
if(symlink == NULL) {
rv = -ENOMEM;
goto no_symlink;
}printk(KERN_INFO "%s %s initialised\n", MODULE_NAME, MODULE_VERSION);
return 0;no_symlink:
remove_proc_entry("tty", example_dir);
no_tty:
remove_proc_entry("bar", example_dir);
no_bar:
remove_proc_entry("foo", example_dir);
no_foo:
remove_proc_entry("jiffies", example_dir);
no_jiffies:
remove_proc_entry(MODULE_NAME, NULL);
out:
return rv;
}/*模块卸载函数*/
static void __exit cleanup_procfs_example(void)
{
remove_proc_entry("jiffies_too", example_dir);
remove_proc_entry("tty", example_dir);
remove_proc_entry("bar", example_dir);
remove_proc_entry("foo", example_dir);
remove_proc_entry("jiffies", example_dir);
remove_proc_entry(MODULE_NAME, NULL);
printk(KERN_INFO "%s %s removed\n",
MODULE_NAME, MODULE_VERSION);
}module_init(init_procfs_example);
module_exit(cleanup_procfs_example);
MODULE_AUTHOR("Erik Mouw");
MODULE_DESCRIPTION("procfs examples");