Proc文件系统

一、概述

    Linux的procfs文件系统是一个虚拟文件系统,是一种特殊文件系统,用于显示进程信息和内核进程。目前,虽然/proc仍然被广泛使用,但是内核2.6及以上的版本,大部分系统信息都已经被迁移至sysfs的伪文件系统,sysfs是在procfs基础上的优化,该文件系统通常安装在/sys下。
    我们知道,Linux文件类型大致可以分为三类:【普通文件】、【目录文件】、【伪文件】。其中,与其他文件系统不同的是,伪文件不是用来存储数据的,比如/proc存储于内存中,不占用磁盘空间,而其他文件系统则存储在磁盘上,如果列出/proc文件系统中的目录信息,可以发现大多数文件的字节长度为0(因为不会存储于磁盘之上,而是存储于内存上),但是如果查看文件内容却会发现其中包含了信息:
  
    并且,这些文件中大多数文件的时间及日期属性通常为当前系统时间和日期,这跟它们随时会被刷新有关(存储于RAM)。
    最初开发procfs文件系统时为了提供有关系统中进程的信息,存储着当前内核运行状态的一系列特殊文件,用户可以通过这些文件查看有关系统硬件及当前正在运行进程的信息,甚至可以通过更改其中某些文件来改变内核的运行状态。
    为了查看及使用上的方便,这些文件通常会按照相关性进行分类存储于不同的目录甚至于子目录中,如/proc/scsi目录中存储的就是当前系统上所有SCSI设备的相关信息,/proc/N中存储的则是系统当前正在运行的进程(pid = N)的相关信息,当进程结束后其相关目录也会相应被删除。这些目录信息我们在后面再详细学习。

二、目录介绍

1、常见目录

/proc/buddyinfo  --  每个内存区中的每个order有多少块可用,和内存碎片问题有关
/proc/cmdline  --  启动时传递给kernel的参数信息
/proc/cpuinfo  --  cpu的信息
/proc/crypto  --  内核使用的所有已安装的加密密码及细节
/proc/devices  --  已经加载的设备并分类
/proc/dma  --  已注册使用的ISA DMA频道列表
/proc/execdomains  --  Linux内核当前支持的execution domains
/proc/fb  --  帧缓冲设备列表,包括数量和控制它的驱动
/proc/filesystems  --  内核当前支持的文件系统类型
/proc/interrupts   --  每个IRQ中断数
/proc/iomem  --  每个物理设备当前在系统内存中的映射
/proc/ioports  --  一个设备的输入输出所使用的注册端口范围
/proc/kcore  --  代表系统的物理内存,存储为核心文件格式,里边显示的是字节数,等于RAM大小加上4kb
/proc/kmsg  --  记录内核生成的信息,可以通过/sbin/klogd或/bin/dmesg来处理
/proc/loadavg  --  根据过去一段时间内CPU和IO的状态得出的负载状态,与uptime命令有关
/proc/locks  --  内核锁住的文件列表
/proc/mdstat  --  多硬盘,RAID配置信息(md=multiple disks)
/proc/meminfo  --  RAM使用的相关信息
/proc/misc  --  其他的主要设备(设备号为10)上注册的驱动
/proc/modules  --  所有加载到内核的模块列表
/proc/mounts  --  系统中使用的所有挂载
/proc/mtrr  --  系统使用的Memory Type Range Registers (MTRRs)
/proc/partitions  --  分区中的块分配信息
/proc/pci  --  系统中的PCI设备列表
/proc/slabinfo  --  系统中所有活动的 slab 缓存信息
/proc/stat  --  所有的CPU活动信息
/proc/sysrq-trigger  --  使用echo命令来写这个文件的时候,远程root用户可以执行大多数的系统请求关键命令,就好像在本地终端执行一样。要写入这个文件,需要把/proc/sys/kernel/sysrq不能设置为0。这个文件对root也是不可读的 /proc/sys:可在此处找到可动态配置的内核选项 /proc/uptime  --  系统已经运行了多久
/proc/swaps  --  交换空间的使用情况
/proc/version  --  Linux内核版本和gcc版本
/proc/bus  --  系统总线(Bus)信息,例如pci/usb等
/proc/driver  --  驱动信息
/proc/fs  --  文件系统信息
/proc/ide  --  ide设备信息
/proc/irq  --  中断请求设备信息
/proc/net  --  网卡设备信息
/proc/scsi  --  scsi设备信息
/proc/tty  --  tty设备信息
/proc/net/dev  --  显示网络适配器及统计信息
/proc/vmstat  --  虚拟内存统计信息
/proc/vmcore  --  内核panic时的内存映像
/proc/diskstats  --  取得磁盘信息
/proc/schedstat  --  kernel调度器的统计信息
/proc/zoneinfo  --  显示内存空间的统计信息,对分析虚拟内存行为很有用

2、进程N的信息

/proc/N  -- pid为N的进程信息,每个运行着的进程都有着这么一个目录。
/proc/N/attr -- 提供了安全相关的属性,可读可写,以支持安全模块如SELinux等,需配置CONFIG_SECURITY。 
/proc/N/cmdline  --  进程启动命令
/proc/N/cwd  --  链接到进程当前工作目录
/proc/N/environ  --  进程环境变量列表
/proc/N/exe  --  链接到进程的执行命令文件
/proc/N/fd  --  包含进程相关的所有的文件描述符
/proc/N/maps  --  与进程相关的内存映射信息
/proc/N/mem  --  指代进程持有的内存,不可读
/proc/N/root  --  链接到进程的根目录
/proc/N/stat  --  进程的状态
/proc/N/statm  --  进程使用的内存的状态
/proc/N/status  --  进程状态信息,比stat/statm更具可读性
/proc/self  --  链接到当前正在运行的进程

3、/proc/sys目录详解

    与/proc下其他文件的【只读】属性不同的是,管理员可以对/proc/sys子目录中的许多文件内容进行修改以更改内核的运行特性,事先可以使用【ls -l】命令查看某文件是否“可写入”。写入操作通常使用类似于“echo DATA > /path/to/your/filename”的格式进行。需要注意的是,即使文件可写入,其一般也不可以使用编辑器进行编辑。
    这里重点说下,/proc/sys/dev子目录。此目录为系统上特殊设备提供参数信息文件的目录,其不同设备的信息文件分别存储于不同的子目录中,如大多数系统上都会具有的/proc/sys/dev、/cdrom和/proc/sys/dev/raid(如果内核编译时开启了支持raid的功能)目录,其内存储的通常是系统上cdrom和raid的相关参数信息文件。
    这里来看一个通过/proc文件系统调整相关的内核配置的示例,
                                            允许ip转发   /proc/sys/net/ipv4/ip_forward
                                            禁止ping/proc/sys/net/ipv4/icmp_echo_ignore_all

     可以在命令行下直接往上述两个“文件”里头写入"1"来实现相关配置,如果写入"0"将取消相关配置。不过在系统重启以后,这些配置将恢复默认设置,所以,如果想让这些修改生效,可以把下面的配置直接写入/etc/profile文件,或者其他随系统启动而执行的程序文件中。
echo 1 > /proc/sys/net/ipv4/ip_forward  
echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all 

三、从/proc中获取相关的性能参数

1、cpu使用率

     这里要从/proc/stat中提取四个数据:用户模式(user)、低优先级的用户模式(nice)、内核模式(system)以及空闲的处理器时间(idle)。它们均位于/proc/stat文件的第一行。CPU的利用率使用如下公式来计算。

CPU利用率   =   100   *(user   +   nice   +   system)/(user   +   nice   +   system   +   idle)

2、内存使用率

这里需要从/proc/meminfo文件中提取两个数据,当前内存的使用量(cmem)以及内存总量(amem)。
内存使用百分比   =   100   *   (cmem   /   umem)

3、网络利用率

为了得到网络利用率的相关数据,需要从/proc/net/dev文件中获得两个数据:从本机输出的数据包数,流入本机的数据包数。它们都位于这个文件的第四行。

性能收集程序开始记录下这两个数据的初始值,以后每次获得这个值后均减去这个初始值即为从集群启动开始从本节点通过的数据包。

利用上述数据计算出网络的平均负载,方法如下:
平均网络负载   =   (输出的数据包+流入的数据包)   /   2

四、/proc信息采集工具

    proc的魅力在于它包含了你可能想知道的关于一个进程的任何信息,你只需要简单地从中获取。/usr/include/sys/procfs.h文件中定义了两个结构,prstatus和prpsinfo,从中可以获取指定进程的很多信息。下面是个例子,开发者想知道他的应用程序究竟占用了多少内存。简单!ls /proc就可以知道了。但是,他还想知道更多细节,他需要知道总的映像大小、常驻部分的大小、堆区(heap)大小、栈区(stack)大小。此外,他希望能够定期跟踪这些数据信息,类似vmstat(1M)那种方式。如上所述,听起来象是一个令人生畏的任务。(Solaris 2.6开始这两个结构定义在/usr/include/sys/old_procfs.h文件中
   然而,通过使用/proc文件系统,我们可以使这项编程挑战变得容易些。这个工具称做memlook,将显示指定PID对应的内存统计信息。此外,可以在命令行上指定一个时间间隔,以便定期重新检测内存利用信息。源码如下:
/*
* @(#)memlook.c 1.0 10 Nov 1997
* Robert Owen Thomas [email]robt@cymru.com[/email]
* memlook.c -- A process memory utilization reporting tool.
*
* gcc -Wall -O3 -o memlook memlook.c
*/
#pragma ident "@(#)memlook.c 1.0 10 Nov 1997 Robert Owen Thomas [email]robt@cymru.com[/email]"

#include <stdio.h>;
#include <stdlib.h>;
#include <sys/types.h>;
#include <sys/stat.h>;
#include <sys/signal.h>;
#include <sys/syscall.h>;
#include <sys/procfs.h>;
#include <sys/param.h>;
#include <unistd.h>;
#include <fcntl.h>;

int counter = 10;
int    showUsage (const char*);
void getInfo   (int, int);

int main (int argc,char* argv[])
{
    int  fd, pid, timeloop = 0;
    char pidpath[BUFSIZ];  /* /usr/include/stdio.h: #define BUFSIZ 1024 */

    switch ( argc )
    {
    case 2:
        break;
    case 3:
        timeloop = atoi( argv[2] );
        break;
    default:
        showUsage( argv[0] );
        break;
    }  /* end of switch */
    pid = atoi( argv[1] );
    sprintf( pidpath, "/proc/%-d", pid );  /* -表示向左靠 */
    if ( ( fd = open( pidpath, O_RDONLY ) ) < 0 )
    {
        perror( pidpath );
        exit( 1 );
    }
    if ( 0 < timeloop )
    {
        for ( ; ; )
        {
            getInfo( fd, pid );
            sleep( timeloop );
        }
    }
    getInfo( fd, pid );
    close( fd );
    exit( 0 );
}  /* end of main */

int showUsage ( const char * progname )
{
    fprintf( stderr, "%s: usage: %s < PID >; [time delay]\n", progname, progname );
    exit( 3 );
}  /* end of showUsage */

void getInfo ( int fd, int pid )
{
    prpsinfo_t prp;
    prstatus_t prs;

    if ( ioctl( fd, PIOCPSINFO, &prp ) < 0 )
    {
        perror( "ioctl" );
        exit( 5 );
    }
    if ( ioctl( fd, PIOCSTATUS, &prs ) < 0 )
    {
        perror( "ioctl" );
        exit( 7 );
    }
    if ( counter >; 9 )
    {
        fprintf( stdout, "PID\tIMAGE\t\tRSS\t\tHEAP\t\tSTACK\n" );
        counter = 0;
    }
    fprintf( stdout, "%u\t%-9u\t%-9u\t%-15u\t%-15u\n", pid, 
             ( unsigned int )prp.pr_bysize, ( unsigned int )prp.pr_byrssize, 
             ( unsigned int )prs.pr_brksize, ( unsigned int )prs.pr_stksize );
    counter++;
}  /* end of getInfo */

     演示了一次简单的输出:
$ memlook 245
PID     IMAGE           RSS             HEAP            STACK
245     1499136         1044480         24581           8192

     需要注意:memlook的作者再这里利用了ioctl(),而不是直接读取/proc下的文件,这样做的好处在于即使系统升级后/proc布局改变,内核中相应ioctl cmd支持也随之改变,对于应用层的开发者,接口一样,源代码可平稳移植。事实上从作者前面举例来看, memlook.c是在Solaris 2.6以前的版本上开发的,但我并未修改就可以直接用在Solaris 2.6上,虽然此时/proc布局已经发生重大变化。
      仔细阅读prstatus和prpsinfo结构,寻找那些你敢兴趣的成员。在未能真正掌握这种技术之前不要针对/proc文件系统使用write()或者ioctl()。针对特定进程胡乱做write()调用,结果未知。

    如上,当试图获取进程状态信息,进行动态调试的时候,基于/proc文件系统开发的工具将会帮助我们更加简便的获取需要的信息。

五、集成到/proc文件系统

        Linux内核提供了很多proc文件系统的API,我们可以在proc目录下构建自定义的文件目录。这里先来了解几个API,相关API的定义在fs/proc/generic.c中,并在include/linux /proc_fs.h中引用。

1、struct proc_dir_entry数据结构

 /*这还没有完全实现。这个想法是为了在这些proc_dir_entries中创建一个内存树(就像实际的/proc文件系统一样 ),
 这样我们就可以动态地 向/proc添加新文件。
 * The "next" pointer creates a linked list of one /proc directory,
 * while parent/subdir create the directory structure (every
 * /proc file has a parent, but "subdir" is NULL for all
 * non-directory entries).
 */
struct proc_dir_entry {
	unsigned int low_ino;
	umode_t mode;
	nlink_t nlink;
	kuid_t uid;
	kgid_t gid;
	loff_t size;
	const struct inode_operations *proc_iops;
	const struct file_operations *proc_fops;//默认的file_operations
	struct proc_dir_entry *next, *parent, *subdir;//链表
	void *data;
	atomic_t count;		/* use count */
	atomic_t in_use;	/* number of callers into module in progress; */
			/* negative -> it's going away RSN */
	struct completion *pde_unload_completion;
	struct list_head pde_openers;	/* who did ->open, but not ->release */
	spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */
	u8 namelen;
	char name[];
};
    kernel5.15中,此结构体有些变化:


2、目录API

       在proc文件系统内创建或者删除一个目录。
API名称 Description说明
struct proc_dir_entry *proc_mkdir(const char *name,  struct proc_dir_entry *parent) 在proc文件系统内创建一个directory, name为该directory 名称, parent为其归属的哪个parent directory,如果为NULL,则创建的directory位于/proc根目录内 
struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode,  struct proc_dir_entry *parent, void *data) 在proc文件系统内创建一个directory, 与上述API相比多了一个参数data,代表的是传入到entry中的私有数据,后面处理需要用到
struct proc_dir_entry *proc_mkdir_mode(const char *name, umode_t mode,     struct proc_dir_entry *parent) 在proc文件系统内创建一个directory, 与上述API相比多了一个参数mode, 代表由驱动程序指定该enntry 权限模式 不采用默认权限
void proc_remove(struct proc_dir_entry *de) 删除传入的entry
void remove_proc_entry(const char *name, struct proc_dir_entry *parent) 根据名称删除entry, 其中paren为要产出的entry的父entry,parent一定要传对 否则就会因找不到而无法删除。如果parent为NULL,则位于/proc根目录
     使用示例:
        创建一个directory最简单的一个用例:  /proc 根目录下创建一个my_proc 目录,代码如下:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/device.h>
#include <linux/proc_fs.h>
MODULE_LICENSE("GPL v2");
#define MY_PROC "my_proc"
static struct proc_dir_entry * my_proc_entry=NULL;
static int __init my_proc_init(void)
{
   my_proc_entry = proc_mkdir(MY_PROC,NULL);
   if (NULL == my_proc_entry)
   {
       return -ENODEV;
   }
    return 0;
}
static void __exit my_proc_exit(void)
{
   remove_proc_entry(MY_PROC, my_proc_entry);
}
module_init(my_proc_init);
module_exit(my_proc_exit);
MODULE_AUTHOR("hzk");
MODULE_DESCRIPTION("A simple moudule for my proc");
MODULE_VERSION("V1.0");

3、文件API

        file 类别API主要是用于在proc目录内创建fie,用于读取或者修改内核数据,主要API类型如下:
API名称 Description说明
struct proc_dir_entry *proc_create(const char *name, umode_t mode,    struct proc_dir_entry *parent,      const struct proc_ops *proc_ops) 创建名称为name的 proc文件,mode为文件模式包括权限等, parent为该文件所属的目录, proc_ops为对该文件操作封装,主要包括读写等功能
struct proc_dir_entry *proc_create_single_data(const char *name, umode_t mode,    struct proc_dir_entry *parent,  int (*show)(struct seq_file *, void *), void *data) 创建名称为name的 proc文件,mode为文件,parent为该文件所属的目录,一般使用该方法创建的proc文件是只读,所以仅仅提供了读show操作 , data为要读取的数据
struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,
        struct proc_dir_entry *parent,    const struct proc_ops *proc_ops, void *data)
创建名称为name的 proc文件,mode为文件,parent为该文件所属的目录,与proc_create函数相比较 多个data参数,指明传入的私有数据即要操作的数据
int remove_proc_subtree(const char *name, struct proc_dir_entry *parent) 删除parent目录内名称为name的子目录内的所有文件
void proc_remove(struct proc_dir_entry *de) 删除传入的entry
void remove_proc_entry(const char *name, struct proc_dir_entry *parent) 根据名称删除entry, 其中paren为要产出的entry的父entry,parent一定要传对 否则就会因找不到而无法删除。如果parent为NULL,则位于/proc根目录
    使用示例:
    使用proc_create_single_dataAPI创建两个文件分别为proc_1和proc_2,各种的私有数据分别为my_proc_1_data和my_proc_2_data,用例代码如下:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/device.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
MODULE_LICENSE("GPL v2");
#define MY_PROC "my_proc"
static struct proc_dir_entry * my_proc_entry=NULL;
static DEFINE_MUTEX(my_proc_lock);
static int my_proc_1_data=1;
static int my_proc_2_data=2;
static int my_proc_1_show(struct seq_file *s, void *v)
{
    int * value = s->private;
    seq_printf(s, "proc_1 value:%d\n", *value);
    return 0;
}
static int my_proc_2_show(struct seq_file *s, void *v)
{
    int * value = s->private;
    seq_printf(s, "proc_2 value:%d\n", *value);
    return 0;
}
static int __init my_proc_init(void)
{
   my_proc_entry = proc_mkdir(MY_PROC, NULL);
   if (NULL == my_proc_entry)
   {
       return -ENODEV;
   }
    proc_create_single_data("proc_1", 0, my_proc_entry, my_proc_1_show, &my_proc_1_data);
    proc_create_single_data("proc_2", 0, my_proc_entry, my_proc_2_show, &my_proc_2_data);
    return 0;
}
static void __exit my_proc_exit(void)
{
   remove_proc_entry(MY_PROC, NULL);
}
module_init(my_proc_init);
module_exit(my_proc_exit);
MODULE_AUTHOR("hzk");
MODULE_DESCRIPTION("A simple moudule for my proc");
MODULE_VERSION("V1.0");

4、proc_ops接口

    proc_ops结构是对proc文件操作的封装,主要包括文件打开,读写等常用操作,该文件定义在include\linux\proc_fs.h文件中
struct proc_ops {
	unsigned int proc_flags;
	int	(*proc_open)(struct inode *, struct file *);
	ssize_t	(*proc_read)(struct file *, char __user *, size_t, loff_t *);
	ssize_t	(*proc_write)(struct file *, const char __user *, size_t, loff_t *);
	loff_t	(*proc_lseek)(struct file *, loff_t, int);
	int	(*proc_release)(struct inode *, struct file *);
	__poll_t (*proc_poll)(struct file *, struct poll_table_struct *);
	long	(*proc_ioctl)(struct file *, unsigned int, unsigned long);
#ifdef CONFIG_COMPAT
	long	(*proc_compat_ioctl)(struct file *, unsigned int, unsigned long);
#endif
	int	(*proc_mmap)(struct file *, struct vm_area_struct *);
	unsigned long (*proc_get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
} __randomize_layout;

5、seq_file接口

    在早期的/proc文件系统的实现中,都是由驱动开发人员在创建proc文件时直接提供read,write方法,驱动开发人员直接操纵buf,但是存在这样一个问题:当读写文件时,文件内容较多的时候,buf经常不够,很容易造成越界现象,因此后来经过改进seq_file 接口,该接口通过创建一个简单的迭代器对象,该对象用来表示项目序列中的位置,每前进一步,该对象输出序列中的一个项目,这样每此迭代只显示一个迭代。
接口API description描述
ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) 读取文件
int seq_open(struct file *file, const struct seq_operations *op) 打开文件
loff_t seq_lseek(struct file *file, loff_t offset, int whence) lseek操作
int single_release(struct inode *inode, struct file *file) 单次迭代释放
int seq_release(struct inode *inode, struct file *file) 多次迭代释放
int single_open(struct file *file, int (*show)(struct seq_file *, void *),    void *data) 单次迭代打开
int single_open_size(struct file *file, int (*show)(struct seq_file *, void *),   void *data, size_t size) 代词迭代打开,包括数据大小
int single_open_size(struct file *file, int (*show)(struct seq_file *, void *),     void *data, size_t size) 写操作
struct list_head *seq_list_start(struct list_head *head, loff_t pos) seq list迭***始
struct list_head *seq_list_start_head(struct list_head *head, loff_t pos) seq list头
struct list_head *seq_list_next(void *v, struct list_head *head, loff_t *ppos) seq 下次迭代

seq file将操作将整个迭代进行封装 每次迭代顺序分为 start->show->next->stop一个完整过程,开发人员只需要实现open 和show方法即可,其他proc_ops结构只需要挂载seq即可。

其中进行show方法实现时,不能使用printk进行打印,而是使用seq_file提供的方法进行打印:

接口API description描述
void seq_printf(struct seq_file *m, const char *f, ...) 等价与printf函数,需要将show函数传入的seq_file结构传递给这个函数,如果seq_printf返回一个非零值,则意味着缓冲区已满
void seq_putc(struct seq_file *m, char c) 打印一个char
void seq_puts(struct seq_file *m, const char *s) 打印一个字符串
void seq_escape(struct seq_file *m, const char *s, const char *esc) 等价于seq_puts函数,若s中的某个字符也存在于esc参数,则该字符以八进制形式打印。传递给esc参数的常见值“\t\n\\”,可以避免要输出的空白字符弄乱屏幕或者迷惑shell脚本

使用seq接口时,一般proc_open函数需要开发自己实现,主要时调用seq_open函数将 seq_operations复制给file接口,以从proc_op便来接管整个操作

 seq按照迭代次数分为单次或者多次迭代,两者在proc_ops挂载时,使用pro_relase接口不同,下面分别展示单次和多次迭代用例

    使用示例:
  • 使用seq接口展示单次show用例,读取my_proc文件时,显示其中内核中的my_proc_data值:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/device.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
MODULE_LICENSE("GPL v2");
#define MY_PROC "my_proc"
static struct proc_dir_entry * my_proc_entry=NULL;
static int my_proc_show(struct seq_file *m, void * v)
{
    seq_printf(m,"my proc test\n");
    return 0;
}
static int my_proc_open(struct inode *inode, struct file *file)
{
    return single_open(file, my_proc_show, NULL);
}
static const struct proc_ops my_proc_ops = {
    .proc_open = my_proc_open,
    .proc_read = seq_read,
    .proc_lseek= seq_lseek,
    .proc_release = single_release,
};
static int __init my_proc_init(void)
{
   my_proc_entry = proc_create(MY_PROC,S_IRUGO, NULL, &my_proc_ops);
   if (NULL == my_proc_entry)
   {
       return -ENOMEM;
   }
    return 0;
}
static void __exit my_proc_exit(void)
{
   remove_proc_entry(MY_PROC, NULL);
}
module_init(my_proc_init);
module_exit(my_proc_exit);
MODULE_AUTHOR("hzk");
MODULE_DESCRIPTION("A simple moudule for my proc");
MODULE_VERSION("V1.0");
  • seq多次迭代用例

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/device.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
MODULE_LICENSE("GPL v2");
#define MY_PROC "my_proc"
static struct proc_dir_entry * my_proc_entry=NULL;
static DEFINE_MUTEX(my_proc_lock);
static int my_proc_data=0;
static void * my_proc_start(struct seq_file *s, loff_t *pos)
{
    mutex_lock(&my_proc_lock);
    printk(KERN_INFO "my_proc start:%d\n", *pos);
    if (*pos>5)
    {
        return NULL;
    }
    return (void *)&my_proc_data;
    }
static void *my_proc_next(struct seq_file *s, void *v, loff_t *pos)
{
    if (*pos>5)
    {
        return NULL;
    }
    my_proc_data++;
    (*pos)++;
    return (void *)&my_proc_data;
}
static int my_proc_show(struct seq_file *s, void *v)
{
    int * value = v;
    seq_printf(s, "my_proc value:%d\n", *value);
    return 0;
}
static void my_proc_stop(struct seq_file *s, void *v)
{
    my_proc_data = 0;
    mutex_unlock(&my_proc_lock);
}
static const struct seq_operations my_seq_ops = {
    .start = my_proc_start,
    .next  = my_proc_next,
    .stop  = my_proc_stop,
    .show  = my_proc_show,
};
static int my_proc_open(struct inode *inode, struct file *file)
{
    return seq_open(file, &my_seq_ops);
}
static const struct proc_ops my_proc_ops = {
    .proc_open = my_proc_open,
    .proc_read = seq_read,
    .proc_lseek= seq_lseek,
    .proc_release = seq_release,
};
static int __init my_proc_init(void)
{
   my_proc_entry = proc_create(MY_PROC,S_IRUGO, NULL, &my_proc_ops);
   if (NULL == my_proc_entry)
   {
       return -ENOMEM;
   }
    return 0;
}
static void __exit my_proc_exit(void)
{
   remove_proc_entry(MY_PROC, NULL);
}
module_init(my_proc_init);
module_exit(my_proc_exit);
MODULE_AUTHOR("hzk");
MODULE_DESCRIPTION("A simple moudule for my proc");
MODULE_VERSION("V1.0");

6、symlink用例

        symlink接口主要提供一个软链接功能:

API名称 Description说明
struct proc_dir_entry *proc_symlink(const char *name,
        struct proc_dir_entry *parent, const char *dest)
创建名称为name的 proc 链接文件, parent为该文件所属的目录, dest为要链接为文件名
void proc_remove(struct proc_dir_entry *de) 删除传入的entry
void remove_proc_entry(const char *name, struct proc_dir_entry *parent) 根据名称删除entry, 其中paren为要产出的entry的父entry,parent一定要传对 否则就会因找不到而无法删除。如果parent为NULL,则位于/proc根目录
    symlink用例相对比较简单:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/device.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
MODULE_LICENSE("GPL v2");
#define MY_PROC "my_proc"
static struct proc_dir_entry * my_proc_entry=NULL;
static struct proc_dir_entry * my_proc_link =NULL;
static DEFINE_MUTEX(my_proc_lock);
static int my_proc_data=0;
static void * my_proc_start(struct seq_file *s, loff_t *pos)
{
    mutex_lock(&my_proc_lock);
    printk(KERN_INFO "my_proc start:%d\n", *pos);
    if (*pos>5)
    {
        return NULL;
    }
    return (void *)&my_proc_data;
    }
static void *my_proc_next(struct seq_file *s, void *v, loff_t *pos)
{
    if (*pos>5)
    {
        return NULL;
    }
    my_proc_data++;
    (*pos)++;
    return (void *)&my_proc_data;
}
static int my_proc_show(struct seq_file *s, void *v)
{
    int * value = v;
    seq_printf(s, "my_proc value:%d\n", *value);
    return 0;
}
static void my_proc_stop(struct seq_file *s, void *v)
{
    my_proc_data = 0;
    mutex_unlock(&my_proc_lock);
}
static const struct seq_operations my_seq_ops = {
    .start = my_proc_start,
    .next  = my_proc_next,
    .stop  = my_proc_stop,
    .show  = my_proc_show,
};
static int my_proc_open(struct inode *inode, struct file *file)
{
    return seq_open(file, &my_seq_ops);
}
static const struct proc_ops my_proc_ops = {
    .proc_open = my_proc_open,
    .proc_read = seq_read,
    .proc_lseek= seq_lseek,
    .proc_release = seq_release,
};
static int __init my_proc_init(void)
{
   my_proc_entry = proc_create(MY_PROC,S_IRUGO, NULL, &my_proc_ops);
   if (NULL == my_proc_entry)
   {
       return -ENOMEM;
   }
   my_proc_link =proc_symlink("my_link", NULL, MY_PROC);
   if (NULL == my_proc_link)
   {
       remove_proc_entry(MY_PROC, NULL);
       return -ENOMEM;
   }
   return 0;
}
static void __exit my_proc_exit(void)
{
   remove_proc_entry("my_link", NULL);
   remove_proc_entry(MY_PROC, NULL);
}
module_init(my_proc_init);
module_exit(my_proc_exit);
MODULE_AUTHOR("hzk");
MODULE_DESCRIPTION("A simple moudule for my proc");
MODULE_VERSION("V1.0");

7、小结

     现在内核发展要求一般不建议在proc文件再新添加文件,因为proc使用起来显得特别臃肿,由于没有THIS_MODULE,有可能会发生删除正在被使用的文件。另外一个问题就是同一个名字的注册问题。

    随着sysfs文件系统完善,一般如果添加新的配置项建议加入到sysfs文件系统中。





全部评论

相关推荐

牛客33727151号:不是哥们我以为驾照是段子呢
点赞 评论 收藏
分享
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务