关于linux 内核建立proc文件夹的问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于linux 内核建立proc文件夹的问题相关的知识,希望对你有一定的参考价值。

proc_mkdir(const char *name, struct proc_dir_entry *parent)
第二个参数linux内核已经有一些预定义的目录,这些目录在哪定义的,有朋友知道吗?譬如说 proc_net 就是已经定义好的"proc/net/",但是这是哪个头文件定义的呢?2.6内核。

在早期的 2.6内核(比如2.6.11),是定义在 include/linux/proc_fs.h 中,比如

extern struct proc_dir_entry proc_root;
extern struct proc_dir_entry *proc_root_fs;
extern struct proc_dir_entry *proc_net;
extern struct proc_dir_entry *proc_net_stat;
extern struct proc_dir_entry *proc_bus;
extern struct proc_dir_entry *proc_root_driver;
extern struct proc_dir_entry *proc_root_kcore;

而这些全局变量的真正定义和初始化,则是在 fs/proc/root.c里,比如:

变量声明
L21 struct proc_dir_entry *proc_net, *proc_net_stat, *proc_bus, *proc_root_fs, *proc_root_driver;

在 void __init proc_root_init(void) 这个函数中,内核创建了proc文件系统的根目录及net,driver等子目录,比如在这个函数中,你可以看到类似于这样的语句,就是用来创建这些目录的

L55 proc_net = proc_mkdir("net", NULL);

L69 proc_root_driver = proc_mkdir("driver", NULL);

但是我看了一下,在最新的内核中 (比如2.6.34),proc fs这个模块改动了不少,已经无法用上面的方法来找到 proc_net 这些全局变量了,不过说实话这些变量意义并不大,毕竟在 proc_mkdir的时候,只要用全路径名就可以了,比如
proc_mkdir("test", proc_net)
其实和
proc_mkdir("net/test", NULL)
没有区别的。
参考技术A 系统默认文件夹,可以查看系统信息:cat /proc/cpuinfo

Linux内核模块编程-proc文件系统进阶

proc作为输入

关于proc的一些基本概念和基本使用以及一些核心数据结构可以参考我的上一篇文章
在上一篇文章中提到了利用proc作为内核空间传递数据给用户空间的一种方式,那么proc能否作为用户空间传递数据给内核空间的一种手法呢?答案当然是可以的,熟悉linux的读者应该都知道我们不光光可以读取proc文件中的内容还是可以向proc文件中写入要传递的数据的,那么如何通过proc来向内核输入信息呢,通过查看proc_dir_entry发现,在这个proc核心数据结构中不仅仅有一个read_proc的函数指针,而且还有一个write_proc函数指针。恭喜你猜对了,通过这个钩子函数的确就可以实现利用proc向内核空间传递数据,但是随着内核的发展,内核的开发者采用了另外一种方式来提供proc的读写钩子函数。在2,6内核中两种方式都是可以的,后来笔者发现在我使用的3.10内核上居然已经没有第一种了。

2.6系列的内核提供了两种方法来操作proc
一种就是:

    read_proc_t *read_proc;
    write_proc_t *write_proc;

第二种就是:

    struct inode_operations * proc_iops;
    const struct file_operations * proc_fops;
    可以参考linux内核文件系统相关的知识

可是到了3.10系列内核后就只有第二种了,所以为了兼容不同内核版本,在操作proc的时候最好按照标准的VFS的接口形式来操作VFS两种不同版本内核对proc_dir_entry的定义
下面是在2.6内核以及3.10内核中关于proc_dir_entry这个核心数据结构的定义的区别。

struct proc_dir_entry 
    unsigned int low_ino;
    unsigned short namelen;
    const char *name;
    mode_t mode;
    nlink_t nlink;
    uid_t uid;
    gid_t gid;
    loff_t size;
    struct inode_operations * proc_iops;
    const struct file_operations * proc_fops;
    get_info_t *get_info;
    struct module *owner;
    struct proc_dir_entry *next, *parent, *subdir;
    void *data;
    read_proc_t *read_proc;
    write_proc_t *write_proc;
    atomic_t count;     /* use count */
    int deleted;        /* delete flag */
    void *set;
;
2.6.18的内核
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;
    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[];
;
3.10的内核

可以看出在3.10内核中已经废除了第一种方式来操作proc文件了。下面以第二种方式来操作proc作为一个示列,通过这个示列来告诉读者如何去使用第二种方式来操作文件。

下面通过标准的VFS接口的形式来操作proc。

模块的初始化

#define MESSAGE_LENGTH 80
#define PROC_ENTRY_FILENAME "rw_test"
static char Message[MESSAGE_LENGTH];
struct proc_dir_entry *Our_Proc_File;

int init_module(void)

    int rv = 0;
    Our_Proc_File = create_proc_entry(PROC_ENTRY_FILENAME,0644,NULL);
//省去了这一行,改用VFS统一接口的形式操作proc
//  Our_Proc_File->read_proc = procfile_read;  
    Our_Proc_File->mode = S_IFREG | S_IRUGO;
    Our_Proc_File->uid = 0;
    Our_Proc_File->gid = 0;
    Our_Proc_File->size = 80; 
//标准的VFS接口形式
    Our_Proc_File->proc_iops = &Inode_Ops_4_Our_Proc_File;
    Our_Proc_File->proc_fops = &File_Ops_4_Our_Proc_File;
    printk(KERN_INFO "Trying to craete /proc/rw_test:\\n");
    if(Our_Proc_File == NULL) 
        rv = -ENOMEM;
        remove_proc_entry(PROC_ENTRY_FILENAME,proc_root);
        printk(KERN_INFO "Error: Could not initialize /proc/rw_test\\n");
     else 
        printk(KERN_INFO "Success!\\n");
     else 
        printk(KERN_INFO "Success!\\n");
    
    return rv;

模块的注销

void cleanup_module(void)

    remove_proc_entry(PROC_ENTRY_FILENAME,NULL);
    printk(KERN_INFO "/proc/test1 removed\\n");

VFS接口的注册

static struct file_operations File_Ops_4_Our_Proc_File = 
    .read = module_output,
    .write = module_input,
    .open = module_open,
    .release = module_close,
;
//在inode层面做了一个权限检测
static struct inode_operations Inode_Ops_4_Our_Proc_File = 
    .permission = module_permission,
;

open/close的实现

只是简单的增加和减小模块的引用计数
int module_open(struct inode *inode,struct file *file)

    try_module_get(THIS_MODULE);
    return 0;


int module_close(struct inode *inode,struct file *file)

    module_put(THIS_MODULE);
    return 0;

read/write实现

static ssize_t module_input(struct file *filp,const char* buff,size_t len,loff_t *off)

//写数据到内核空间,通过get_user将字符一个一个存入到内核空间的Message的字符数组中
    int i;
    for(i = 0;i < MESSAGE_LENGTH-1 && i < len;i++)
        get_user(Message[i],buff + i);

    Message[i] = '\\0';
    return i;


//读取内核空间的数据,将内核空间的Message字符数组的值通过put_user输出到用户空间的buffer中
static ssize_t module_output(struct file *filp,
            char *buffer,
            size_t length,
            loff_t * offset)


    static int finished = 0;
    int i;
    char message[MESSAGE_LENGTH + 30];
    if(finished) 
        finished = 0;
        return 0;
    
    sprintf(message,"Last input: %s",Message);
    for(i = 0;i < length && message[i];i++)
        put_user(message[i],buffer+i);

    finished = 1;
        return i;

权限检测

static int module_permission(struct inode *inode,int op) 

//如果当前用户UID为0,则运行操作proc文件,否则提示权限问题
    if(current->real_cred->uid == 0)
        return 0;

    return -EACCES;

载入模块进行测试

insmod procfs.ko
[root@localhost kernel_module]# echo "test" >> /proc/rw_test 
[root@localhost kernel_module]# cat /proc/rw_test 
Last input: test
普通用户读取:
[test@localhost ~]$ cat /proc/rw_test 
cat: /proc/rw_test: Permission denied
与50位技术专家面对面 20年技术见证,附赠技术全景图

以上是关于关于linux 内核建立proc文件夹的问题的主要内容,如果未能解决你的问题,请参考以下文章

/proc文件系统的作用

linux内核源码分析之proc文件系统

linux系统结构

关于Centos7以上系统硬件的一些常用查看命令

/proc文件系统的特点和/proc文件的说明

关于Linux的SUID位!