如何使用内核API函数 proc_create?

1. 问题来源

看到一个null pointer dereference的demo使用了这个函数。

2. 概述

Proc文件系统
Proc File System是一个虚拟的文件系统,可以理解为内核对用户开放的接口,让内核和用户进程进行数据交换 (读取内核进程的数据,修改内核参数等):

cat /proc/cpuinfo

Creating a new Proc file
To create a proc file system we need to implement a simple interface – file_operation.We can implement more than 20 functions but the common operations are read, write.

To register the interface use the function proc_create.

要创建一个Proc file需要实现file_operation结构体,主要实现read和write就可以了。然后通过proc_create来注册。将模块注册到内核后,就能在/proc/目录找到我们的文件。
对该文件进行读写就能实现用户进程与内核的通信。

3. 示例

mydev.c:

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kernel.h>   
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#define BUFSIZE  100


MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Liran B.H");

static int irq=20;
module_param(irq,int,0660);

static int mode=1;
module_param(mode,int,0660);

static struct proc_dir_entry *ent;

static ssize_t mywrite(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) 
{
    int num,c,i,m;
    char buf[BUFSIZE];
    if(*ppos > 0 || count > BUFSIZE)
        return -EFAULT;
    if(copy_from_user(buf, ubuf, count))
        return -EFAULT;
    num = sscanf(buf,"%d %d",&i,&m);
    if(num != 2)
        return -EFAULT;
    irq = i; 
    mode = m;
    c = strlen(buf);
    *ppos = c;
    return c;
}

static ssize_t myread(struct file *file, char __user *ubuf,size_t count, loff_t *ppos) 
{
    char buf[BUFSIZE];
    int len=0;
    if(*ppos > 0 || count < BUFSIZE)
        return 0;
    len += sprintf(buf,"irq = %d\n",irq);
    len += sprintf(buf + len,"mode = %d\n",mode);
    
    if(copy_to_user(ubuf,buf,len))
        return -EFAULT;
    *ppos = len;
    return len;
}

static struct file_operations myops = 
{
    .owner = THIS_MODULE,
    .read = myread,
    .write = mywrite,
};

static int simple_init(void)
{
    ent=proc_create("mydev",0666,NULL,&myops);
    printk(KERN_ALERT "hello...\n");
    return 0;
}

static void simple_cleanup(void)
{
    proc_remove(ent);
    printk(KERN_WARNING "bye ...\n");
}

module_init(simple_init);
module_exit(simple_cleanup);

Makefile:

obj-m += mydev.o

modules:
    $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
    
clean:
    $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

output:

invincible@ubuntu:~/Desktop/my_mods/mydev$ make
make -C /lib/modules/4.4.0-112-generic/build M=/home/invincible/Desktop/my_mods/mydev modules
make[1]: Entering directory '/usr/src/linux-headers-4.4.0-112-generic'
  CC [M]  /home/invincible/Desktop/my_mods/mydev/mydev.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/invincible/Desktop/my_mods/mydev/mydev.mod.o
  LD [M]  /home/invincible/Desktop/my_mods/mydev/mydev.ko
make[1]: Leaving directory '/usr/src/linux-headers-4.4.0-112-generic'
invincible@ubuntu:~/Desktop/my_mods/mydev$ insmod mydev.ko 
insmod: ERROR: could not insert module mydev.ko: Operation not permitted
invincible@ubuntu:~/Desktop/my_mods/mydev$ sudo insmod mydev.ko 
invincible@ubuntu:~/Desktop/my_mods/mydev$ ls /proc/my*
/proc/mydev
invincible@ubuntu:~/Desktop/my_mods/mydev$ echo "32 6" > /proc/mydev
invincible@ubuntu:~/Desktop/my_mods/mydev$ cat /proc/mydev 
irq = 32
mode = 6
invincible@ubuntu:~/Desktop/my_mods/mydev$ 

user_app.c:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
 
void main(void)
{
    char buf[100];
    int fd = open("/proc/mydev", O_RDWR);
    read(fd, buf, 100);
    puts(buf);
 
    lseek(fd, 0 , SEEK_SET);
    write(fd, "33 4", 5);
    
    lseek(fd, 0 , SEEK_SET);
    read(fd, buf, 100);
    puts(buf);
}   

output:

invincible@ubuntu:~/Desktop/my_mods/mydev$ gcc user_app.c -o user_app
invincible@ubuntu:~/Desktop/my_mods/mydev$ sudo insmod mydev.ko 
invincible@ubuntu:~/Desktop/my_mods/mydev$ ./user_app 
irq = 20
mode = 1

irq = 33
mode = 4

invincible@ubuntu:~/Desktop/my_mods/mydev$ 

4. 补充

proc_create是在kernel 3.10以及之后的版本中新增的,用于替换之前的create_proc_entry

kernel 3.9

include/linux/proc_fs.h

extern struct proc_dir_entry *create_proc_entry(const char *name, umode_t mode,
                        struct proc_dir_entry *parent);
      
static inline struct proc_dir_entry *create_proc_entry(const char *name,
    umode_t mode, struct proc_dir_entry *parent) { return NULL; }



fs/proc/generic.c
struct proc_dir_entry *create_proc_entry(const char *name, umode_t mode,
                                         struct proc_dir_entry *parent)
{
        struct proc_dir_entry *ent;
        nlink_t nlink;


        if (S_ISDIR(mode)) {
                if ((mode & S_IALLUGO) == 0)
                        mode |= S_IRUGO | S_IXUGO;
                nlink = 2;
        } else {
                if ((mode & S_IFMT) == 0)
                        mode |= S_IFREG;
                if ((mode & S_IALLUGO) == 0)
                        mode |= S_IRUGO;
                nlink = 1;
        }


        ent = __proc_create(&parent, name, mode, nlink);
        if (ent) {
                if (proc_register(parent, ent) < 0) {
                        kfree(ent);
                        ent = NULL;
                }
        }
        return ent;
}
EXPORT_SYMBOL(create_proc_entry);

kernel 3.10

include/linux/proc_fs.h

extern struct proc_dir_entry *proc_create_data(const char *, umode_t,
                           struct proc_dir_entry *,
                           const struct file_operations *,
                           void *);
            
static inline struct proc_dir_entry *proc_create(
    const char *name, umode_t mode, struct proc_dir_entry *parent,
    const struct file_operations *proc_fops)
{
    return proc_create_data(name, mode, parent, proc_fops, NULL);
}


fs/proc/generic.c
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)
{
    struct proc_dir_entry *pde;
    if ((mode & S_IFMT) == 0)
        mode |= S_IFREG;

    if (!S_ISREG(mode)) {
        WARN_ON(1); /* use proc_mkdir() */
        return NULL;
    }

    if ((mode & S_IALLUGO) == 0)
        mode |= S_IRUGO;
    pde = __proc_create(&parent, name, mode, 1);
    if (!pde)
        goto out;
    pde->proc_fops = proc_fops;
    pde->data = data;
    if (proc_register(parent, pde) < 0)
        goto out_free;
    return pde;
out_free:
    kfree(pde);
out:
    return NULL;
}
EXPORT_SYMBOL(proc_create_data);

两者的区别主要就是proc_createfile_operation作为参数传递,而proc_create_data是创建了proc_dir_entry之后再设置file_operation

本文主要内容来自参考资料1

5. 参考资料

1. LINUX KERNEL DEVELOPMENT – CREATING A PROC FILE AND INTERFACING WITH USER SPACE
2. The Linux Kernel Module Programming Guide

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容

  • mean to add the formatted="false" attribute?.[ 46% 47325/...
    ProZoom阅读 2,689评论 0 3
  • 必备的理论基础 1.操作系统作用: 隐藏丑陋复杂的硬件接口,提供良好的抽象接口。 管理调度进程,并将多个进程对硬件...
    drfung阅读 3,525评论 0 5
  • feisky云计算、虚拟化与Linux技术笔记posts - 1014, comments - 298, trac...
    不排版阅读 3,813评论 0 5
  • 寻。 亲觅,身临。 别往日,看如今。 风雨坚毅,昼夜殷勤。 扬鞭逐壮志,策马踏征尘。 纵使荆棘遍地,亦要不负心魂。...
    霙愔阅读 261评论 15 4
  • 1.前言 在Dart库中,有两种实现异步编程的方式(Future和Stream),使用它们只需要在代码中引入dar...
    巧巧的二表哥阅读 18,277评论 6 45