linux proc

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

1.创建目录:

  1. struct proc_dir_entry *proc_mkdir(const char *name,  
  2.                 struct proc_dir_entry *parent);  

2.创建proc文件:

  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文件/目录:

  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文件的读写:

  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 name
	mode_t mode;				// mode permissions
	uid_t uid;				// File's user id
	gid_t gid;				// File's group id
	struct inode_operations *proc_iops;	// Inode operations functions
	struct file_operations *proc_fops;	// File operations functions
	struct proc_dir_entry *parent;		// Parent directory
	...
	read_proc_t *read_proc;			// /proc read function
	write_proc_t *write_proc;		// /proc write function
	void *data;				// Pointer to private data
	atomic_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/proc
proc_net/proc/net
proc_bus/proc/bus
proc_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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值