Linux内核中的proc文件系统

简介:

Linux内核中的proc文件系统

简介

procfs文件系统是内核中的一个特殊文件系统。它是一个虚拟文件系统: 它不是实际的存储设备中的文件,而是存在于内存中。procfs中的文件是用来允许用户空间的程序访问内核中的某些信息(比如进程信息在 /proc/[0-9]+/中),或者用来做调试用途(/proc/ksyms,这个文件列出了已经登记的内核符号,这些符号给出了变量或函数的地址。每行给出一个符号的地址,符号名称以及登记这个符号的模块。程序ksyms、insmod和kmod使用这个文件。它还列出了正在运行的任务数,总任务数和最后分配的PID。)

这个文档描述了内核中procfs文件系统的使用。它以介绍所有和管理文件系统相关的函数开始。在函数介绍后,它还展示了怎么和用户空间通信,和一些小技巧。在文档的最后,还给出了一个完整的例子。

注意/proc/sys中的文件属于sysctl文件,它们不属于procfs文件系统,被另外一套完全不同的api管理。

seq_file

procfs在处理大文件时有点笨拙。为了清理procfs文件系统并且使内核编程简单些,引入了seq_file机制。seq_file机制提供了大量简单的接口去实现大内核虚拟文件。

seq_file机制适用于你利用结构序列去创建一个返回给用户空间的虚拟文件。要使用seq_file机制,你必须创建一个”iterator”对象,这个对象指向这个序列,并且能逐个指向这个序列中的对象,此外还要能输出这个序列中的任一个对象。它听起来复杂,实际上,操作过程相当简单。接下来将用实际的例子展示到底怎么做。

首先,你必须包含头文件<Linux/seq_file.h>。接下来,你必须创建迭代器方法:start, next, stop, and show。

start方法通常被首先调用。这个方法的函数原型是:


  
  
  1. void *start(struct seq_file *sfile, loff_t *pos); 

sfile没什么作用,通常被忽略。pos参数是一个整型,表示从哪个位置开始读。关于位置的定义完全取决于函数实现;它不一定要是结果文件中的一个字节位置。 由于seq_file机制通常是利用一个特定的结构序列实现的,所以位置通常是一个指向序列中下一个结构体的指针。在wing驱动中,每一个设备表示序列中的一个结构,所以,入参pos代表g_pstWingDevices数组的索引。因此,在wing驱动中start方法的实现为:


  
  
  1. static void *wing_seq_start(struct seq_file *s, loff_t *pos) 
  2.     if (*pos >= g_iWingDevicesNum) 
  3.         return NULL; /* No more to read */ 
  4.     return g_pstWingDevices + *pos; 

返回值如果不为NULL,代表一个可以被迭代器使用的私有数据。

next函数应该移动迭代器到下一个位置,如果序列中没有数据,返回NULL。这个方法的函数原型为:


  
  
  1. void *next(struct seq_file *sfile, void *v, loff_t *pos); 

这里,参数v代表上一个函数调用(可能是start函数,或者是next函数)返回的迭代器,, 参数pos是文件中的当前位置。next函数应该改变pos的指向,具体是逐步改变还是跳跃改变取决于迭代器的工作机制。next函数在wing驱动中的实现为:


  
  
  1. static void* wing_seq_next(struct seq_file *s, void *v, loff_t *pos) 
  2.     (*pos)++; 
  3.     if (*pos >= g_iWingDevicesNum) 
  4.         return NULL
  5.     return g_pstWingDevices + *pos; 

当内核停止了迭代器的工作,它调用stop函数清理现场:


  
  
  1. void stop(struct seq_file *sfile, void *v); 

wing驱动没有清理工作要做,所以stop函数为空。


  
  
  1. void wing_seq_stop(struct seq_file *sfile, void *v) 

要是seq_file代码在调用start和stop时不执行睡眠或是非原子的操作,那么这种机制将毫无意义。你要保证从start函数调用到stop函数调用是很短暂的。因此,在开始函数中获得一个信号量或者自旋锁是比较安全的做法。要是seq_file其他方法是原子的,整个调用链必须是原子的。

在这些函数调用中,内核调用call函数向内核空间输出特性的信息。这个函数的函数原型是:


  
  
  1. int show(struct seq_file *sfile, void *v); 

这个方法应该创建序列中由指示器v指定项的输出。不能使用printk,而是使用以下这些特定函数:


  
  
  1. int seq_printf(struct seq_file *sfile, const char *fmt, ...) 

这个函数是seq_file机制中类似于printf的实现;它使用通常的格式字符串和参数组成输出字符串。你必须把show函数中的seq_file结构体传给这个函数。如果它返回一个非零的值,表示buffer已经填充好,输出被丢出去了。在大多数实现中,都选择忽略返回值。


  
  
  1. int seq_putc(struct seq_file *sfile, char c); 
  2.  
  3. int seq_puts(struct seq_file *sfile, const char *s); 

这两个函数相当于用户层的putc和puts。


  
  
  1. int seq_escape(struct seq_file *m, const char *s, const char *esc); 

这个函数是 seq_puts 的对等体, 除了 s 中的任何也在 esc 中出现的字符以八进制格式打印. esc 的一个通用值是”\t\n\”, 它使内嵌的空格不会搞乱输出和可能搞乱 shell 脚本.


  
  
  1. int seq_path(struct seq_file *sfile, struct vfsmount *m, struct dentry *dentry, char *esc); 

这个函数能够用来输出和给定命令项关联的文件名子. 它在设备驱动中不可能有用;我们是为了完整在此包含它.

wing设备中的show函数例子:


  
  
  1. static int wing_seq_show(struct seq_file *s, void *v) 
  2.  
  3.  
  4.     ST_Wing_Dev_Type* pDev = (ST_Wing_Dev_Type* ) v; 
  5.  
  6.     seq_printf(s, "\nThis Device is %i\n", pDev->iData); 
  7.  
  8.     return 0; 
  9.  

在我的例子中,我将一个ST_Wing_Dev_Type结构体表示为迭代器。

上面就是完整的迭代器操作,wing驱动必须将它们打包到一起好连接到procfs文件系统。首先要做的就是利用它们组成一个seq_operations结构体:


  
  
  1. static struct seq_operations s_stWingSeqOps = { 
  2.  
  3.     .start = wing_seq_start, 
  4.  
  5.     .next = wing_seq_next, 
  6.  
  7.     .stop = wing_seq_stop, 
  8.  
  9.     .show = wing_seq_show 
  10.  
  11. }; 

有了这个结构,我们必须创建一个内核能理解的文件实现。我们不使用前面说过的read_proc方法;在使用seq_file时, 最好在一个稍低的级别上连接到procfs。这意味着创建一个file_operations(和字符设备一样的结构),这个结构实现了内核对文件的reads和seeks操作。幸运的是,这个操做很简单。首先创建一个把文件和seq_file方法联接起来的open方法:


  
  
  1. static int wing_proc_open(struct inode *inode, struct file *file) 
  2.     return seq_open(file, &s_stWingSeqOps); 

调用seq_open函数的时候将文件和上面定义的序列操作关联到一起。open是唯一要我们实现的函数接口,所以我们的file_operations结构体是:


  
  
  1. static struct file_operations s_stWingProcFops = { 
  2.     .owner = THIS_MODULE, 
  3.     .open = wing_proc_open, 
  4.     .read = seq_read, 
  5.     .llseek = seq_lseek, 
  6.     .release = seq_release 
  7. }; 

最后我们要在procfs文件系统中创建文件:


  
  
  1. proc_create("wingdevices", 0644, NULL, &s_stWingProcFops); 

关键结构体

struct proc_dir_entry代表的是/proc目录下的一个目录或者文件,他是procfs文件系统的主要结构体,它的定义在/fs/internal.h中:


  
  
  1. /* 
  2.  * This is not completely implemented yet. The idea is to 
  3.  * create an in-memory tree (like the actual /proc filesystem 
  4.  * tree) of these proc_dir_entries, so that we can dynamically 
  5.  * add new files to /proc. 
  6.  * 
  7.  * The "next" pointer creates a linked list of one /proc directory, 
  8.  * while parent/subdir create the directory structure (every 
  9.  * /proc file has a parent, but "subdir" is NULL for all 
  10.  * non-directory entries). 
  11.  */ 
  12. struct proc_dir_entry { 
  13.     unsigned int low_ino; 
  14.     umode_t mode; 
  15.     nlink_t nlink; 
  16.     kuid_t uid; 
  17.     kgid_t gid; 
  18.     loff_t size
  19.     const struct inode_operations *proc_iops; 
  20.     const struct file_operations *proc_fops; 
  21.     struct proc_dir_entry *next, *parent, *subdir; 
  22.     void *data; 
  23.     atomic_t count;     /* use count */ 
  24.     atomic_t in_use;    /* number of callers into module in progress; */ 
  25.             /* negative -> it's going away RSN */ 
  26.     struct completion *pde_unload_completion; 
  27.     struct list_head pde_openers;   /* who did ->open, but not ->release */ 
  28.     spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */ 
  29.     u8 namelen; 
  30.     char name[]; 
  31. }; 

主要接口

procfs应该包含的头文件<linux/proc_fs.h>。

在3.x内核中procfs主要接口有:

  • proc_symlink
  • proc_mkdir
  • proc_mkdir_data
  • proc_mkdir_mode
  • proc_create_data
  • proc_create
  • proc_set_size
  • proc_set_user
  • PDE_DATA
  • proc_get_parent_data
  • proc_remove
  • remove_proc_entry
  • remove_proc_subtree

proc_mkdir

说明:在/proc下创建目录

函数原型:


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

参数:

  • name

要创建的目录名称

  • parent

父目录,如果为NULL,表示直接在/proc下面创建目录。

proc_mkdir_data

说明:在/proc下创建目录

函数原型:


  
  
  1. struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode, struct proc_dir_entry *parent, void *data) 

参数:

  • name

要创建的目录名称

  • mode

指定要创建目录的权限

  • parent

父目录,如果为NULL,表示直接在/proc下面创建目录。

  • data

proc_create_data

说明:创建proc虚拟文件系统文件

函数原型:


  
  
  1. struct proc_dir_entry *proc_create_data(const char *name, umode_t mode, struct proc_dir_entry *parent, const struct file_operations *proc_fops, void *data) 

参数:

  • name

你要创建的文件名。

  • mode

为创建的文件指定权限

  • parent

为你要在哪个文件夹下建立名字为name的文件,如:init_net.proc_net是要在/proc/net/下建立文件。

  • proc_fops

为struct file_operations

  • data

保存私有数据的指针,如不要为NULL。

例子:


  
  
  1. ////////////////////////test.c//////////////////////////////////////// 
  2. #include <linux/init.h>   
  3. #include <linux/module.h>   
  4. #include <linux/types.h>   
  5. #include <linux/slab.h>   
  6. #include <linux/fs.h>   
  7. #include <linux/proc_fs.h>   
  8. #include <linux/seq_file.h>   
  9. #include <net/net_namespace.h>   
  10. #include <linux/mm.h>   
  11.  
  12. MODULE_LICENSE("GPL");   
  13.  
  14. typedef struct {       
  15.     int data1;       
  16.     int data2;   
  17. }ST_Data_Info_Type;   
  18.  
  19.  
  20. static ST_Data_Info_Type g_astDataInfo[2];   
  21.  
  22.  
  23.  
  24. static int test_proc_show(struct seq_file *m, void *v) 
  25.     ST_Data_Info_Type* pInfo = (ST_Data_Info_Type*)m->private; 
  26.     if(pInfo != NULL
  27.     { 
  28.         seq_printf(m, "%d----%d\n", pInfo->data1, pInfo->data2); 
  29.     } 
  30.     return 0; 
  31.  
  32.  
  33. static int test_proc_open(struct inode *inode, struct file *file)  
  34. {   
  35.     return single_open(file, test_proc_show, PDE_DATA(inode)); 
  36. }   
  37.  
  38.  
  39. static const struct file_operations dl_file_ops = {       
  40.     .owner = THIS_MODULE,       
  41.     .open    = test_proc_open, 
  42.     .read    = seq_read, 
  43.     .llseek  = seq_lseek, 
  44.     .release = single_release, 
  45. };   
  46.  
  47. static struct proc_dir_entry *s_pstRootTestDir; 
  48.  
  49.  
  50. void init_mem(void)   
  51. {      
  52.     /* create /proc/test */ 
  53.     s_pstRootTestDir = proc_mkdir("test"NULL); 
  54.     if (!s_pstRootTestDir) 
  55.         return
  56.  
  57.     g_astDataInfo[0].data1=1;       
  58.     g_astDataInfo[0].data2=2;   
  59.     proc_create_data("proc_test1", 0644, s_pstRootTestDir, &dl_file_ops, &g_astDataInfo[0]); 
  60.  
  61.     g_astDataInfo[1].data1=3;      
  62.     g_astDataInfo[1].data2=4;   
  63.     proc_create_data("proc_test2", 0644, s_pstRootTestDir, &dl_file_ops, &g_astDataInfo[1]);   
  64. }  
  65.  
  66. static int __init test_module_init(void)   
  67. {   
  68.     printk("[test]: module init\n"); 
  69.     init_mem();       
  70.     return 0;   
  71. }   
  72.  
  73. static void __exit test_module_exit(void)   
  74. {   
  75.     printk("[test]: module exit\n"); 
  76.     remove_proc_entry("proc_test1", s_pstRootTestDir);      
  77.     remove_proc_entry("proc_test2", s_pstRootTestDir); 
  78.     remove_proc_entry("test"NULL); 
  79. }   
  80. module_init(test_module_init);   
  81. module_exit(test_module_exit); 

proc_create

说明:创建proc虚拟文件系统文件

函数原型:


  
  
  1. struct proc_dir_entry *proc_create(const char *name, umode_t mode, struct proc_dir_entry *parent, const struct file_operations *proc_fops) 

参数:

  • name

你要创建的文件名。

  • mode

为创建的文件指定权限

  • parent

为你要在哪个文件夹下建立名字为name的文件,如:init_net.proc_net是要在/proc/net/下建立文件。

  • proc_fops

为struct file_operations

注意:这个接口和proc_create_data的区别在于他不能保存私有数据指针。

PDE_DATA

获取proc_create_data传入的私有数据。

proc_symlink

说明:这个函数在procfs目录下创建一个从name指向dest的符号链接. 它在用户空间等效为ln -s dest name。

函数原型:


  
  
  1. struct proc_dir_entry *proc_symlink(const char *name, struct proc_dir_entry *parent, const char *dest) 

参数:

  • name

原始符号。

  • parent

符号所在的目录。

  • dest

所要创建的符号链接名字。

remove_proc_entry

说明:删除procfs文件系统中的文件或者目录。

函数原型:


  
  
  1. void remove_proc_entry(const char *name, struct proc_dir_entry *parent) 

参数:

  • name

要删除的文件或者目录名。

  • parent

符号所在的目录,如果为NULL,表示在/proc目录下。







本文作者:佚名
来源:51CTO
目录
相关文章
|
17天前
|
安全 Linux 数据安全/隐私保护
深入Linux操作系统:文件系统和权限管理
在数字世界的海洋中,操作系统是连接用户与硬件的桥梁,而Linux作为其中的佼佼者,其文件系统和权限管理则是这座桥梁上不可或缺的结构。本文将带你探索Linux的文件系统结构,理解文件权限的重要性,并通过实际案例揭示如何有效地管理和控制这些权限。我们将一起航行在Linux的命令行海洋中,解锁文件系统的奥秘,并学习如何保护你的数据免受不必要的访问。
|
15天前
|
算法 Linux
深入探索Linux内核的内存管理机制
本文旨在为读者提供对Linux操作系统内核中内存管理机制的深入理解。通过探讨Linux内核如何高效地分配、回收和优化内存资源,我们揭示了这一复杂系统背后的原理及其对系统性能的影响。不同于常规的摘要,本文将直接进入主题,不包含背景信息或研究目的等标准部分,而是专注于技术细节和实际操作。
|
15天前
|
存储 缓存 网络协议
Linux操作系统的内核优化与性能调优####
本文深入探讨了Linux操作系统内核的优化策略与性能调优方法,旨在为系统管理员和高级用户提供一套实用的指南。通过分析内核参数调整、文件系统选择、内存管理及网络配置等关键方面,本文揭示了如何有效提升Linux系统的稳定性和运行效率。不同于常规摘要仅概述内容的做法,本摘要直接指出文章的核心价值——提供具体可行的优化措施,助力读者实现系统性能的飞跃。 ####
|
16天前
|
监控 算法 Linux
Linux内核锁机制深度剖析与实践优化####
本文作为一篇技术性文章,深入探讨了Linux操作系统内核中锁机制的工作原理、类型及其在并发控制中的应用,旨在为开发者提供关于如何有效利用这些工具来提升系统性能和稳定性的见解。不同于常规摘要的概述性质,本文将直接通过具体案例分析,展示在不同场景下选择合适的锁策略对于解决竞争条件、死锁问题的重要性,以及如何根据实际需求调整锁的粒度以达到最佳效果,为读者呈现一份实用性强的实践指南。 ####
|
16天前
|
缓存 监控 网络协议
Linux操作系统的内核优化与实践####
本文旨在探讨Linux操作系统内核的优化策略与实际应用案例,深入分析内核参数调优、编译选项配置及实时性能监控的方法。通过具体实例讲解如何根据不同应用场景调整内核设置,以提升系统性能和稳定性,为系统管理员和技术爱好者提供实用的优化指南。 ####
|
19天前
|
负载均衡 算法 Linux
深入探索Linux内核调度机制:公平与效率的平衡####
本文旨在剖析Linux操作系统内核中的进程调度机制,特别是其如何通过CFS(完全公平调度器)算法实现多任务环境下资源分配的公平性与系统响应速度之间的微妙平衡。不同于传统摘要的概览性质,本文摘要将直接聚焦于CFS的核心原理、设计目标及面临的挑战,为读者揭开Linux高效调度的秘密。 ####
32 3
|
21天前
|
负载均衡 算法 Linux
深入探索Linux内核调度器:公平与效率的平衡####
本文通过剖析Linux内核调度器的工作机制,揭示了其在多任务处理环境中如何实现时间片轮转、优先级调整及完全公平调度算法(CFS),以达到既公平又高效地分配CPU资源的目标。通过对比FIFO和RR等传统调度策略,本文展示了Linux调度器如何在复杂的计算场景下优化性能,为系统设计师和开发者提供了宝贵的设计思路。 ####
33 6
|
21天前
|
消息中间件 安全 Linux
深入探索Linux操作系统的内核机制
本文旨在为读者提供一个关于Linux操作系统内核机制的全面解析。通过探讨Linux内核的设计哲学、核心组件、以及其如何高效地管理硬件资源和系统操作,本文揭示了Linux之所以成为众多开发者和组织首选操作系统的原因。不同于常规摘要,此处我们不涉及具体代码或技术细节,而是从宏观的角度审视Linux内核的架构和功能,为对Linux感兴趣的读者提供一个高层次的理解框架。
|
22天前
|
缓存 并行计算 Linux
深入解析Linux操作系统的内核优化策略
本文旨在探讨Linux操作系统内核的优化策略,包括内核参数调整、内存管理、CPU调度以及文件系统性能提升等方面。通过对这些关键领域的分析,我们可以理解如何有效地提高Linux系统的性能和稳定性,从而为用户提供更加流畅和高效的计算体验。
29 2
|
22天前
|
缓存 网络协议 Linux
深入探索Linux操作系统的内核优化策略####
本文旨在探讨Linux操作系统内核的优化方法,通过分析当前主流的几种内核优化技术,结合具体案例,阐述如何有效提升系统性能与稳定性。文章首先概述了Linux内核的基本结构,随后详细解析了内核优化的必要性及常用手段,包括编译优化、内核参数调整、内存管理优化等,最后通过实例展示了这些优化技巧在实际场景中的应用效果,为读者提供了一套实用的Linux内核优化指南。 ####
45 1
下一篇
DataWorks