目录

Proc文件系统

创建一个新的Proc文件

实现读取处理程序

与用户空间交换数据

实现写处理程序

用户空间应用


在第一篇文章中,我们构建了一个具有初始化和退出功能的简单内核模块 ,并介绍了内核编程中的基本概念。

接下来,我们添加了一个内核模块参数来配置内核模块数据

在本文中,我们将使用procfs(/ proc)文件创建用户空间应用程序的第一个界面

Proc文件系统


Proc是用于与内核内部数据结构接口的伪文件系统。作为用户,您可以使用proc文件进行系统诊断-CPU,内存,中断等。您还可以配置许多参数,例如调度程序参数,内核对象,内存等

与proc的常见交互是使用cat和shell中的echo。

例如:

# cat /proc/cpuinfo
# echo "50"> /proc/sys/kernel/sched_rr_timeslice_ms

创建一个新的Proc文件


要创建proc文件系统,我们需要实现一个简单的接口– file_operation

我们可以实现20多个功能,但常见的操作是读取,写入。要注册接口,请使用proc_create函数

基本结构是:

#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  100MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Liran B.H");static struct proc_dir_entry *ent;static ssize_t mywrite(struct file *file, const char __user *ubuf,size_t count, loff_t *ppos)
{printk( KERN_DEBUG "write handler\n");return -1;
}static ssize_t myread(struct file *file, char __user *ubuf,size_t count, loff_t *ppos)
{printk( KERN_DEBUG "read handler\n");return 0;
}static struct file_operations myops =
{.owner = THIS_MODULE,.read = myread,.write = mywrite,
};static int simple_init(void)
{ent=proc_create("mydev",0660,NULL,&myops);return 0;
}static void simple_cleanup(void)
{proc_remove(ent);
}module_init(simple_init);
module_exit(simple_cleanup);

如果构建并插入模块,将看到一个新文件/ proc / mydev,可以使用cat和echo测试读写操作(仅查看内核日志消息)

# echo "test" > /proc/mydev
bash: echo: write error: Operation not permitted# cat /proc/mydev
# dmesg | tail -2
[  694.640306] write handler
[  714.661465] read handler

实现读取处理程序


读取处理程序接收4个参数:

  • 文件对象–具有打开的文件详细信息(权限,位置等)的每个过程结构
  • 用户空间缓冲区
  • 缓冲区大小
  • 要求的位置(输入和输出参数)

要实现read回调,我们需要:

  • 检查要求的位置
  • 从请求的位置向用户缓冲区填充数据(最大大小<=缓冲区大小)
  • 返回我们填充的字节数。

例如,用户运行以下代码:

int fd = open("/proc/mydev", O_RDWR);len = read(fd,buf,100);
len = read(fd,buf,50);

在第一次调用read时,我们得到用户缓冲区,大小= 100,po​​sition = 0,我们需要从位置0开始最多填充100个字节的缓冲区,更新该位置并返回我们写入的字节数。如果我们用100个字节填充缓冲区,并在下一次读取调用时返回100,则得到用户缓冲区,大小为50,位置为100

假设我们有2个模块参数,并且我们想在proc读取处理程序中返回它们的值,我们编写以下代码:

static int irq=20;
module_param(irq,int,0660);static int mode=1;
module_param(mode,int,0660);static ssize_t myread(struct file *file, char __user *ubuf,size_t count, loff_t *ppos)
{char buf[BUFSIZE];int len=0;printk( KERN_DEBUG "read handler\n");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;
}

这是一个简单的实现,我们检查这是否是第一次调用read(pos = 0),并且用户缓冲区大小大于BUFSIZE,否则返回0(文件末尾)

然后,我们构建返回的缓冲区,将其复制到用户,更新位置并返回我们编写的数字

构建并插入模块,可以使用cat命令对其进行测试:

# sudo insmod ./simproc.ko irq=32 mode=4
# cat /proc/mydev
irq = 32
mode = 4

与用户空间交换数据


在内核代码中,不能仅在用户空间提供的地址与内核空间中的缓冲区的地址之间使用memcpy

  • 对应于完全不同的地址空间(由于虚拟内存)。
  • 用户空间地址可以换出到磁盘。
  • 用户空间地址可能无效(用户空间进程试图访问未经授权的数据)。

您必须在读写文件操作代码中使用专用功能:

#include <asm/uaccess.h>
unsigned long copy_to_user(void __user *to,const void *from, unsigned long n);
unsigned long copy_from_user(void *to,const void __user *from,unsigned long n);

实现写处理程序


写入处理程序类似于读取处理程序。唯一的区别是用户缓冲区类型是const char指针。我们需要将数据从用户缓冲区复制到请求的位置,并返回复制的字节数

在此示例中,我们想使用一个简单的命令设置两个值:

# echo "32 6" > /proc/mydev

第一个值为irq编号,第二个为模式。

写入处理程序的代码:

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;
}

再次,我们检查这是否是首次调用write(位置0),然后使用copy_from_user将数据从用户地址空间转移到内核地址空间。我们提取值,检查错误,更新位置并返回收到的字节数

完整的模块代码:

#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  100MODULE_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",0660,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);

注意:要实现更复杂的proc条目,请使用 seq_file包装器

用户空间应用


您可以打开文件并使用读/写功能来测试模块。每次操作后,请不要忘记将位置基准移至0:

#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);
}   

源代码:https://github.com/dev-area/proctest

或者:https://github.com/Rtoax/test/tree/master/linux/proc/proctest

Linux内核开发:创建proc文件并与用户空间接口相关推荐

  1. linux信号子系统,Linux时间子系统之(三):用户空间接口函数

    Linux时间子系统之(三):用户空间接口函数 作者:linuxer 发布于:2014-12-24 15:48 分类:时间子系统 一.前言 从应用程序的角度看,内核需要提供的和时间相关的服务有三种: ...

  2. 【Linux 内核 内存管理】内存管理架构 ② ( 用户空间内存管理 | malloc | ptmalloc | 内核空间内存管理 | sys_brk | sys_mmap | sys_munmap)

    文章目录 一.用户空间内存管理 ( malloc / free / ptmalloc / jemalloc / tcmalloc ) 二.内核空间内存管理 1.内核内存管理系统调用 ( sys_brk ...

  3. linux包含绝对路径头文件,linux-kernel - 访问用户空间内存访问函数(如access_ok(),get_from_user())需要包含的头文件的确切路径。 - 堆栈内存溢出...

    我在linux-headers-3.2.0-49中搜索了用户内存访问功能. 它在uaccess.h头文件中定义,但是uaccess.h文件太多. 下面是我在linux-headers-3.2.0-49 ...

  4. 嵌入式系统Linux内核开发工程师必须掌握的三十道题

    嵌入式系统Linux内核开发工程师必须掌握的三十道题 如果你能正确回答以下问题并理解相关知识点原理,那么你就可以算得上是基本合格的Linux内核开发工程师,试试看! 1) Linux中主要有哪几种内核 ...

  5. Linux内核开发_3_busybox

    Busybox简介 busybox是一个开源项目,集合了Linux的常用命令,且体积非常小,一般被用于嵌入式Linux上. busybox在编写初期的思想就是简洁,小巧,强悍,并且设计时从系统资源考虑 ...

  6. 使用 /sys 文件系统访问 Linux 内核:比/proc 更为理想的访问内核数据的途径

    使用 /sys 文件系统访问 Linux 内核:比/proc 更为理想的访问内核数据的途径 作者: feng 日期: 2010/12/22发表评论 (0)查看评论 程 任全 (crquan@gmail ...

  7. 万字整理,肝翻Linux内存管理所有知识点【Linux内核开发人员必学】都是精髓

    Linux的内存管理可谓是学好Linux的必经之路,也是Linux的关键知识点,有人说打通了内存管理的知识,也就打通了Linux的任督二脉,这一点不夸张.有人问网上有很多Linux内存管理的内容,为什 ...

  8. 值得学习的linux内核开发

    [推荐阅读] 深入linux内核架构--进程&线程 了解Docker 依赖的linux内核技术 浅析linux内核网络协议栈--linux bridge 内核编程常常看起来像是黑魔法,而在亚瑟 ...

  9. 驱动开发--创建设备文件--控制LED灯

    目录 1.手动创建设备文件 2.应用程序如何将数据传递给驱动 3.控制LED灯: 4.应用层控制灯 5.自动创建设备节点 1.手动创建设备文件 cat  /proc/devices 查看主设备号 su ...

最新文章

  1. Mysql探究之null与not null
  2. IOS-获取Model(设备型号)、Version(设备版本号)、app(程序版本号)等
  3. TreeSet源码解析
  4. CRF++ 特征工程
  5. spark-submit使用及说明
  6. C++关键字(1)——const
  7. 从0开始 Java实习 黑白棋
  8. 嵌入式linux mongodb,小白在Ubuntu安装mongoDB与mongo-c-driver
  9. 搭建Servlet在线视频
  10. 利用URL拼接爬取获取有道翻译内容
  11. 如何把IDEA项目与上传到Git中
  12. 游戏音效下载网站大全
  13. 金庸群侠传修改器链接服务器,《金庸群侠传》6合1版修改器
  14. Unity实现打飞碟小游戏
  15. 世界杯的狂欢也是黑灰产的狂欢?
  16. python oled_树莓派使用 OLED 屏显示图片及文字
  17. 谈谈对品牌机和组装机的看法
  18. 计算机软件服务行业规范,行业标准信息服务平台
  19. keep alive是什么?
  20. 如何退出python命令行

热门文章

  1. matlab dynprog,动态规划算法
  2. 《android开发艺术探索》读书笔记(五)--RemoteViews
  3. CGAL4.4+VC2008编译
  4. 策略模式 是一种好策略 -07
  5. Crystal Report動態顯示圖片
  6. 两个服务器 文件比较,使用linux的comm命令比较两个已排过序的文件
  7. 指定开局五子棋c语言代码,C语言案例:控制台版本的五子棋游戏【代码】
  8. 什么舱位_把“订舱位”说成book warehouse position,外企不会要你
  9. 春风app一直显示服务器内部错误,CHANGES.md
  10. oracle12输出文件性能慢,输出选项 (Oracle Solaris Studio 12.2:性能分析器)