linux proc

来源:互联网 发布:hyper v 网络设置 编辑:程序博客网 时间:2024/05/29 12:33

in Linux, proc文件系统是一个虚拟文件系统,用户和应用程序可以通过proc文件系统得到当前的一些系统信息,并可以改变一些内核的参数。/proc下的文件是一种特殊文件,不能够像一般文件一样创建删除。下面讨论proc文件的创建、删除与读写。

1.创建目录:

[c] view plaincopy
  1. struct proc_dir_entry *proc_mkdir(const char *name,  
  2.                 struct proc_dir_entry *parent);  

2.创建proc文件:

[c] view plaincopy
  1. struct proc_dir_entry *create_proc_entry( const char *name,  mode_t mode,  
  2.                 struct proc_dir_entry *parent );  

create_proc_entry函数用于创建一个一般的proc文件,其中name是文件名,比如“hello”,mode是文件模式,parent是要创建的proc文件的父目录(若parent = NULL则创建在/proc目录下)。

3.删除proc文件/目录:

[c] view plaincopy
  1. void remove_dir_entry(const char *name, struct proc_dir_entry *parent);  

参数同proc_mkdir()函数类似

4.创建可读写的proc文件

proc文件实际上是一个叫做proc_dir entry的struct(定义在proc_fs.h),该struct中有int read_proc和int write_proc两个元素,要实现proc的文件的读写就要给这两个元素赋值。但这里不是简单地将一个整数赋值过去就行了,需要实现两个回调函数。

在用户或应用程序访问该proc文件时,就会调用这个函数,实现这个函数时只需将想要让用户看到的内容放入page即可。

写回调函数原型:int mod_write( struct file *filp, const char __user *buff, unsigned long len, void *data );

在用户或应用程序试图写入该proc文件时,就会调用这个函数,实现这个函数时需要接收用户写入的数据(buff参数)。


写一个模块测试proc文件的读写:

[c] view plaincopy
  1. #include linux/module.h  
  2. #include linux/kernel.h  
  3. #include linux/proc_fs.h  
  4. #include linux/sched.h  
  5. #include asm/uaccess.h  
  6.    
  7. #define STRINGLEN 1024  
  8.    
  9. char global_buffer[STRINGLEN];  
  10.    
  11. struct proc_dir_entry *example_dir, *hello_file;  
  12.    
  13. int proc_read_hello(char *page, char **start, off_t off, int count, int *eof,  
  14.                 void *data)  
  15.         int len;  
  16.         len sprintf(page, global_buffer); //把global_buffer的内容显示给访问者  
  17.         return len;  
  18.  
  19.    
  20. int proc_write_hello(struct file *file, const char *buffer, unsigned long count,  
  21.                 void *data)  
  22.         int len;  
  23.    
  24.         if (count STRINGLEN)  
  25.                 len STRINGLEN – 1;  
  26.         else  
  27.                 len count;  
  28.    
  29.           
  30.         copy_from_user(global_buffer, buffer, len);  
  31.         global_buffer[len] \0′;  
  32.         return len;  
  33.  
  34.    
  35. static int __init proc_test_init(void 
  36.         example_dir proc_mkdir("proc_test"NULL);  
  37.         hello_file create_proc_entry("hello"S_IRUGO, example_dir);  
  38.         strcpy(global_buffer, "hello");  
  39.         hello_file->read_proc proc_read_hello;  
  40.         hello_file->write_proc proc_write_hello;  
  41.         return 0;  
  42.  
  43.    
  44. static void __exit proc_test_exit(void 
  45.         remove_proc_entry("hello"example_dir);  
  46.         remove_proc_entry(“proc_test”, NULL);  
  47.  
  48.    
  49. module_init(proc_test_init);  
  50. module_exit(proc_test_exit);  


写入proc文件

echo "Hello from kernel" /proc/proc_test/hello'

读取proc文件内容,将看到屏幕上显示了我们写入的字符串:Hello from kernel

cat /proc/proc_test/hello
</pre><pre name="code" style="white-space: normal; color: rgb(70, 70, 70); font-size: 14px; line-height: 21px; background-color: rgb(255, 255, 255);">内核编程函数总结
</pre><pre name="code" style="white-space: normal; color: rgb(70, 70, 70); font-size: 14px; line-height: 21px; background-color: rgb(255, 255, 255);">
一、头文件:
    #include
二、创建与删除proc文件:
    创建普通文件:
        1.创建不可读写的普通文件
            
            struct proc_dir_entry* create_proc_entry (const char *name,mode_t mode,struct proc_dir_entry *parent);
            @name :要创建的文件名
            @mode :要创建的文件的属性 默认0755
            @parent :这个文件的父目录
        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);
            @name :要创建的文件名
            @mode :要创建的文件的属性 默认0755
            @parent :这个文件的父目录
            @read_proc :当用户读这个文件时,内核调用的函数
            @data :传给read_proc的参数
        3.创建符号连接
            
            struct proc_dir_entry * proc_symlink (const char *name,struct proc_dir_entry *parent,const char *dest);
            @name :要创建的文件名
            @parent :这个文件的父目录
            @dest :符号连接的目标文件
        4.创建目录
            
            struct proc_dir_entry * proc_mkdir (const char *name,struct proc_dir_entry *parent);
            @name :要创建的目录名
            @parent :这个目录的父目录
        5.删除文件或目录
            
            void remove_proc_entry (const char *name,struct proc_dir_entry *parent);
            @name :要删除的文件或目录名
            @parent :所在的父目录      
    、读写proc文件
        为了能让用户读写添加的proc文件,需要挂接上读写回调函数:read_proc和write_proc
        例:
            struct proc_dir_entry * entry;
            entry->read_proc = read_proc_foo;
            entry->write_proc = write_proc_foo;
        1.读函数read_func
            
            int read_func (char *buffer,char **stat,off_t off,int count,int *peof,void *data);
            @buffer :把要返回给用户的信息写在buffer里,最大不超过PAGE_SIZE(一般4K)
            @stat :一般不使用
            @off :buffer的偏移量
            @count :用户要取的字节数
            @peof :读到文件尾时,把peof指向的位置置1
            @data :被多个proc文件定义为读时,通过data传递参数
        2.写函数write_func
            
            int write_func (struct file *file,const char *buffer,unsigned long count,void *data);
            @file :该proc文件对应的file结构,一般忽略。
            @buffer :待写的数据所在的位置
            @count :待写数据的大小
            @data :同read_func
    、seq_file编程接口
        include/linux/seq_file.h:
        struct seq_file{
            char *buf;
            size_t size;
            size_t from;
            size_t count;
            loff_t index;
            loff_t version;
            struct semaphore sem;
            struct seq_opertions *op;
            void *private;
        };
        seq_file结构会在seq_open函数调用中分配,然后作为参数传给每一个seq_file的操作函数。
        1.seq_file操作函数:
            struct seq_operations {
                void (*start) (struct seq_file *m,loff_t *pos);
                void (*stop) (struct seq_file *m,void *v);
                void (*next) (struct seq_file *m,void *v,loff_t *pos);
                void (*show) (struct seq_file *m,void *v);
            };
        2.四个操作函数作用:
            start:用来遍历连接对象的时候做初始化准备工作。
                    从pos得到偏移,返回一个连接对象的偏移。
                    也可以返回一个特殊值:SEQ_START_TOKEN
                    出错返回:ERR_PTR(error_code)
            stop:遍历结束时被调用。
                    主要做一些清理工作,如释放锁、释放内存
                    注意:如果调用了start,就必定会调用stop,即使start返回出错。
            next:遍历中寻找下一个连接对象。一般是下一个节点。
            show:对当前遍历到的连接对象或节点进行一些操作。
                    例如调用seq_xxx函数输出对象的一些内容。
        3.格式化输出函数
            seq_printf(struct seq_file *m,"格式化字符串",var):和printk很相似,只是多了个*m
            seq_putc (struct seq_file *m,char c);输出字符
            seq_puts (struct seq_file *m,const char *s);输出字符串
  
         seq_escape (struct seq_file *m,const char *s,const char
*esc);跟seq_puts类似,不同的是这个函数特殊处理字符串s中的特殊字符(esc指针指向的每个字符),这些特殊字符输出为八进制,其他字符
照常。
</pre><pre name="code" style="white-space: normal; color: rgb(70, 70, 70); font-size: 14px; line-height: 21px; background-color: rgb(255, 255, 255);">
</pre><pre name="code" style="white-space: normal; color: rgb(70, 70, 70); font-size: 14px; line-height: 21px; background-color: rgb(255, 255, 255);">
</pre><pre name="code" style="white-space: normal; color: rgb(70, 70, 70); font-size: 14px; line-height: 21px; background-color: rgb(255, 255, 255);">
</pre><div style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;"><p>其实自己一直感觉linux系统中的proc接口很好用,不需要写应用程序就可以测试那些驱动接口,只需要在超级终端中敲入命令,爽。今天把一直以来寄存在其他程序中的proc接口移植到自己的程序中,自己写了个proc接口,分享一下。</p></div><div style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">#include <linux/proc_fs.h>-----这个库文件必须</div><div style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">要在/proc目录下建立一个proc接口,需要调用create_proc_entry这个函数,〕</div><div style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">eg:</div><div style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">void test_proc_create(void){ proc_test_cmd = create_proc_entry("test",S_IWUSR,NULL); if (proc_expgpio_cmd)   proc_test_cmd->write_proc = write_proc_test_cmd;}</div><div style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">这个短短的函数就是用来在/proc目录下建立一个test,如果你用命令</div><div style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">ehco command > /proc/test</div><div style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">的话,就会调用到write_proc_test_cmd中,当然这个echo语句是可以传递参数进去的,</div><div style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">echo command parm1 parm2 > /proc/test</div><div style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;"> </div><div style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">今天我的代码中,我是通过parm1来选择不同的函数,用来测试不同的驱动接口,通过parm2甚至parm3等等用来传递参数到驱动接口,这样就间接地调用到了驱动接口中。</div><pre name="code" style="white-space: normal; color: rgb(70, 70, 70); font-size: 14px; line-height: 21px; background-color: rgb(255, 255, 255);">
<p style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;"><a target=_blank name="N101AD" style="color: rgb(51, 102, 153);">创建并删除 /proc 项</a></p><p style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">要在 /proc 文件系统中创建一个虚拟文件,请使用 <code>create_proc_entry</code> 函数。这个函数可以接收一个文件名、一组权限和这个文件在 /proc 文件系统中出现的位置。<code>create_proc_entry</code> 的返回值是一个 <code>proc_dir_entry</code> 指针(或者为 <em>NULL</em>,说明在 <code>create</code> 时发生了错误)。然后就可以使用这个返回的指针来配置这个虚拟文件的其他参数,例如在对该文件执行读操作时应该调用的函数。<code>create_proc_entry</code> 的原型和 <code>proc_dir_entry</code> 结构中的一部分如清单 7 所示。</p><br style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;" /><a target=_blank name="N101D4" style="color: rgb(51, 102, 153); font-family: Arial; font-size: 14px; line-height: 26px;"><strong>清单 7. 用来管理 /proc 文件系统项的元素</strong></a><br style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;" /><table border="0" cellspacing="0" cellpadding="0" style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px; width: 708px;"><tbody><tr><td class="code-outline"><pre class="displaycode" name="code" style="white-space: pre-wrap; word-wrap: break-word;">        struct proc_dir_entry *<strong>create_proc_entry</strong>( const char *name, mode_t mode,                                             struct proc_dir_entry *parent );struct <strong>proc_dir_entry</strong> {const char *name;// virtual file namemode_t mode;// mode permissionsuid_t uid;// File's user idgid_t gid;// File's group idstruct inode_operations *proc_iops;// Inode operations functionsstruct file_operations *proc_fops;// File operations functionsstruct proc_dir_entry *parent;// Parent directory...read_proc_t *read_proc;// /proc read functionwrite_proc_t *write_proc;// /proc write functionvoid *data;// Pointer to private dataatomic_t count;// use count...};void <strong>remove_proc_entry</strong>( const char *name, struct proc_dir_entry *parent );

稍后我们就可以看到如何使用 read_proc 和 write_proc 命令来插入对这个虚拟文件进行读写的函数。

要从 /proc 中删除一个文件,可以使用 remove_proc_entry 函数。要使用这个函数,我们需要提供文件名字符串,以及这个文件在 /proc 文件系统中的位置(parent)。这个函数原型如清单 7 所示。

parent 参数可以为 NULL(表示 /proc 根目录),也可以是很多其他值,这取决于我们希望将这个文件放到什么地方。表 1 列出了可以使用的其他一些父 proc_dir_entry,以及它们在这个文件系统中的位置。


表 1. proc_dir_entry 快捷变量
proc_dir_entry在文件系统中的位置proc_root_fs/procproc_net/proc/netproc_bus/proc/busproc_root_driver/proc/driver

回调函数

我们可以使用 write_proc 函数向 /proc 中写入一项。这个函数的原型如下:

int mod_write( struct file *filp, const char __user *buff,               unsigned long len, void *data );

filp 参数实际上是一个打开文件结构(我们可以忽略这个参数)。buff 参数是传递给您的字符串数据。缓冲区地址实际上是一个用户空间的缓冲区,因此我们不能直接读取它。len 参数定义了在 buff 中有多少数据要被写入。data参数是一个指向私有数据的指针(参见 清单 7)。在这个模块中,我们声明了一个这种类型的函数来处理到达的数据。

Linux 提供了一组 API 来在用户空间和内核空间之间移动数据。对于 write_proc 的情况来说,我们使用了copy_from_user 函数来维护用户空间的数据。

读回调函数

我们可以使用 read_proc 函数从一个 /proc 项中读取数据(从内核空间到用户空间)。这个函数的原型如下:

int mod_read( char *page, char **start, off_t off,              int count, int *eof, void *data );

page 参数是这些数据写入到的位置,其中 count 定义了可以写入的最大字符数。在返回多页数据(通常一页是 4KB)时,我们需要使用 start 和 off 参数。当所有数据全部写入之后,就需要设置 eof(文件结束参数)。与write 类似,data 表示的也是私有数据。此处提供的 page 缓冲区在内核空间中。因此,我们可以直接写入,而不用调用 copy_to_user

其他有用的函数

我们还可以使用 proc_mkdirsymlinks 以及 proc_symlink 在 /proc 文件系统中创建目录。对于只需要一个 read 函数的简单 /proc 项来说,可以使用 create_proc_read_entry,这会创建一个 /proc 项,并在一个调用中对read_proc 函数进行初始化。这些函数的原型如清单 8 所示。


清单 8. 其他有用的 /proc 函数
        /* Create a directory in the proc filesystem */struct proc_dir_entry <strong>*proc_mkdir</strong>( const char *name,                                     struct proc_dir_entry *parent );/* Create a symlink in the proc filesystem */struct proc_dir_entry *<strong>proc_symlink</strong>( const char *name,                                       struct proc_dir_entry *parent,                                       const char *dest );/* Create a proc_dir_entry with a read_proc_t in one call */struct proc_dir_entry *<strong>create_proc_read_entry</strong>( const char *name,                                                  mode_t mode,                                                  struct proc_dir_entry *base,                                                  read_proc_t *read_proc,                                                  void *data );/* Copy buffer to user-space from kernel-space */unsigned long <strong>copy_to_user</strong>( void __user *to,                              const void *from,                              unsigned long n );/* Copy buffer to kernel-space from user-space */unsigned long <strong>copy_from_user</strong>( void *to,                                const void __user *from,                                unsigned long n );/* Allocate a 'virtually' contiguous block of memory */void *<strong>vmalloc</strong>( unsigned long size );/* Free a vmalloc'd block of memory */void <strong>vfree</strong>( void *addr );/* Export a symbol to the kernel (make it visible to the kernel) */<strong>EXPORT_SYMBOL</strong>( symbol );/* Export all symbols in a file to the kernel (declare before module.h) */<strong>EXPORT_SYMTAB</strong>

                                             
0 0
原创粉丝点击