今天我们介绍另一种用户内核空间通信的方法:proc文件系统。

proc文件系统作为linux提供的一种虚拟文件系统并不占用实际外围存储空间,它仅存在于内存中,系统断电即消失。proc文件系统最开始的设计主要是为满足内核向用户态进程报告其状态而设计,并没有为输入做规定和说明。随着发展,现在的proc文件系统已经演变成一个“用户-内核”空间半双工的通信方式了(虽然目前已经开始有点混乱了,但某些早期开发的软件代码中还在继续使用这个文件系统)。用户不但可以从proc文件系统中读取内核的相关状态信息,还可以向其中写入数据以改变内核的某些行为状态。

/proc目录里主要存放由内核控制的状态信息,一般会动态改变。如果你对/proc目录执行'ls -l' 命令可以看到大部分文件都是 0 字节,原因是 procfs和其他常规的文件系统一样把自己注册到虚拟文件系统层 (VFS)。直到当VFS调用它,请求文件、目录的i-node的时候,procfs才根据内核中的信息动态地建立相应的文件和目录。我的系统中proc目录的结构如下,每个人可能不一样,这取决于你编译内核时所打开的选项:

这些文件的解释和意义如下:

cmdline:系统启动时输入给内核命令行参数 
cpuinfo:CPU的硬件信息 (型号, 家族, 缓存大小等)  
devices:主设备号及设备组的列表,当前加载的各种设备(块设备/字符设备) 
dma:使用的DMA通道 
filesystems:当前内核支持的文件系统,当没有给 mount(1) 指明哪个文件系统的时候, mount(1) 就依靠该文件遍历不同的文件系统
interrupts :中断的使用及触发次数,调试中断时很有用 
ioports I/O:当前在用的已注册 I/O 端口范围 
kcore:该伪文件以 core 文件格式给出了系统的物理内存映象(比较有用),可以用 GDB 查探当前内核的任意数据结构。该文件的总长度是物理内存 (RAM) 的大小再加上 4KB
kmsg:可以用该文件取代系统调用 syslog(2) 来记录内核日志信息,对应dmesg命令
kallsym:内核符号表,该文件保存了内核输出的符号定义, modules(X) 使用该文件动态地连接和捆绑可装载的模块
loadavg:负载均衡,平均负载数给出了在过去的 1、 5,、15 分钟里在运行队列里的任务数、总作业数以及正在运行的作业总数。
locks:内核锁 。
meminfo物理内存、交换空间等的信息,系统内存占用情况,对应df命令。
misc:杂项 。
modules:已经加载的模块列表,对应lsmod命令 。
mounts:已加载的文件系统的列表,对应mount命令,无参数。
partitions:系统识别的分区表 。
slabinfo:sla池信息。
stat:全面统计状态表,CPU内存的利用率等都是从这里提取数据。对应ps命令。
swaps:对换空间的利用情况。 
version:指明了当前正在运行的内核版本。

/proc目录下常见的就是上述几个文件和目录。需要格外注意的就是三个黄色的目录:net、scsi和sys。sys目录是可写的,可以通过它来访问或修改内核的某些控制参数,sysctl命令接口会用到/proc/sys目录,这里我就不展开了,在介绍sysctl章节时详细讨论。而net和scsi则依赖于内核配置,协议栈和我们前面介绍过的Netfitler都在/proc/net目录下建立了各自的某些控制信息所对应的文件。如果系统不支持scsi,则 scsi目录就不存在。

接下来我们就来实践一下Linux所提供给我们的proc机制来完成用户和内核空间的通信。

如果要在/proc目录下创建一个目录,一般用:

struct proc_dir_entry *proc_mkdir(const char *name,

struct proc_dir_entry *parent)

其中,name为待创建的目录名;parent为待创建目录的上一级目录,如果为NULL则表示默认创建到/proc目录下。而如果要在/proc目录下创建一个文件,一般用:

struct proc_dir_entry *create_proc_entry(const char *name,

mode_t mode,

struct proc_dir_entry *parent)

name和parent的意义同proc_mkdir,mode指明了待创建文件的权限,即是否可读可写,或哪些用户可读,哪些用户可写。这两个函数均返回一个struct proc_dir_entry{}结构体的实例,该结构体定义在linux-2.6.21\include\linux\proc_fs.h文件中,如下:

点击(此处)折叠或打开

  1. struct proc_dir_entry {
  2. … …
  3. const struct inode_operations *proc_iops;
  4. const struct file_operations *proc_fops;
  5. … …
  6. read_proc_t *read_proc;
  7. write_proc_t *write_proc;
  8. … …
  9. };

比较重要的两个成员函数是read_procwrite_proc,分别指向proc目录下待创建的文件被“读”和“写”的时候的回调处理函数(重要)。

读函数的原型:

int read_proc(char *page, char **start, off_t off,int count, int *eof, void *data);

当我们通过诸如“cat”之类的命令来读取/proc目录下的文件内容时,内核会分配给proc读取程序一页大小的内存空间,即PAGE_SIZE大小,proc的驱动程序会自动将这块内存中的数据复制到用户空间,最终待访问的proc文件的read_proc回调函数会被调用,其中:

page:将需要传递给用户的数据复制到这个缓冲区地址里。这个缓冲区是内核空间的,不需要用户调用copy_to_user()之类的函数,系统会自动将page中的数据复制给用户。

start:在page所指向的缓冲区中需要复制给用户数据的起始地址。一般不使用。

off:用户打算读取文件时的偏移地址,即从这个地址开始读取。类似用户空间lseek移动文件指针的作用。

count:用户要求读取的字节数。

eof:如果读到文件结尾,当驱动程序的数据发送完毕时将这个值置为1,发送给用户,我们可以通过该值判断是否读取到文件末尾。

data:私有数据指针,一般不用。

写函数的原型:

int write_proc(struct file *file, const char __user *buffer,unsigned long count, void *data);

file:内核中一个打开的文件结构,通常忽略。

buffer:用户空间传递过来的数据指针,用户待写入文件的数据就存储在这个值所指向的地址区域中。而这家伙实际上是一个用户空间地址,内核中不能直接拿来用,需要调用诸如copy_from_user()之类的函数来讲用户空间的数据复制到内核空间来。

count:用户待写入文件的字节数。

data:一般不用。

删除proc文件比较简单,调用:

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

即可,参数同上,其中name是要删除的proc文件的名称。看个proc文件的应用示例:

点击(此处)折叠或打开

  1. /* myproctest.c*/
  2. #include <linux/init.h>
  3. #include <linux/module.h>
  4. #include <linux/stat.h>
  5. #include <linux/kernel.h>
  6. #include <linux/proc_fs.h>
  7. #include <asm/uaccess.h>
  8. MODULE_AUTHOR("Koorey Wung");
  9. MODULE_DESCRIPTION("procfs test module.");
  10. MODULE_LICENSE("GPL");
  11. #define PROCNAME "mytest"
  12. static struct proc_dir_entry * myproc_entry = NULL;
  13. static char msg[512]={0};
  14. static int my_read(char *page, char **start, off_t off,int count, int *eof, void *data)
  15. {
  16. int len = strlen(msg);
  17. if(off >= len)
  18. return 0;
  19. if(count > len-off)
  20. count = len-off;
  21. memcpy(page+off,msg+off,count);
  22. return off+count;
  23. }
  24. static int my_write(struct file *file, const char __user *buffer,unsigned long count, void *data)
  25. {
  26. unsigned long len = sizeof(msg);
  27. if(count >= len)
  28. count = len -1;
  29. if(copy_from_user(msg,(void*)buffer,count))
  30. return -EFAULT;
  31. msg[count]='\0';
  32. return count;
  33. }
  34. static int __init procTest_init(void)
  35. {
  36. myproc_entry = create_proc_entry(PROCNAME,0666,NULL);
  37. if(!myproc_entry){
  38. printk(KERN_ERR "can't create /proc/mytest \n");
  39. return -EFAULT;
  40. }
  41. myproc_entry->read_proc = my_read;
  42. myproc_entry->write_proc = my_write;
  43. return 0;
  44. }
  45. static void __exit procTest_exit(void)
  46. {
  47. remove_proc_entry(PROCNAME,NULL);
  48. }
  49. module_init(procTest_init);
  50. module_exit(procTest_exit);

编程生成myproctest.ko模块后,测试结果如下:

在上面的例子中我们看到read_proc的回调函数中我们确实没有调用copy_to_user()函数,结果还是正确误区地返回给用户。procfs既然属于一种特殊的文件系统,那我们我们是否可以像操作普通文件那样,对其进行扩充呢?答案是肯定,我们对上面的例子稍加改造,使用内核提供的文件系统的机制来实现对proc文件的读写操作,修改后的代码如下:

点击(此处)折叠或打开

  1. /* myproctest.c*/
  2. #include <linux/init.h>
  3. #include <linux/module.h>
  4. #include <linux/stat.h>
  5. #include <linux/kernel.h>
  6. #include <linux/proc_fs.h>
  7. #include <asm/uaccess.h>
  8. MODULE_AUTHOR("Koorey Wung");
  9. MODULE_DESCRIPTION("procfs test module.");
  10. MODULE_LICENSE("GPL");
  11. #define PROCNAME "mytest"
  12. static struct proc_dir_entry * myproc_entry = NULL;
  13. static char msg[512]={0};
  14. static int my_file_read(struct file * file,char *data,size_t len,loff_t *off)
  15. {
  16. if(*off > 0)
  17. return 0;
  18. if(copy_to_user(data,msg,strlen(msg)))
  19. return -EFAULT;
  20. *off += strlen(msg);
  21. return strlen(msg);
  22. }
  23. static int my_file_write(struct file *file, const char *data,size_t len,loff_t *off)
  24. {
  25. if(copy_from_user(msg,(void*)data,len))
  26. return -EFAULT;
  27. msg[len]='\0';
  28. return len;
  29. }
  30. static struct file_operations my_file_test_ops = {
  31. .read = my_file_read,
  32. .write = my_file_write,
  33. };
  34. static int __init procTest_init(void)
  35. {
  36. myproc_entry = create_proc_entry(PROCNAME,0666,NULL);
  37. if(!myproc_entry){
  38. printk(KERN_ERR "can't create /proc/mytest \n");
  39. return -EFAULT;
  40. }
  41. myproc_entry->proc_fops = & my_file_test_ops;
  42. return 0;
  43. }
  44. static void __exit procTest_exit(void)
  45. {
  46. remove_proc_entry(PROCNAME,NULL);
  47. }
  48. module_init(procTest_init);
  49. module_exit(procTest_exit);

编译后验证,结果依然正确,这是我们站在文件系统的角度来实现proc目录下的文件的读写操作,关于文件系统这里就不展开了,以后有时间再写个它的专题。

转载于:https://www.cnblogs.com/masterpanda/p/5700474.html

用户空间和内核空间通讯之【proc文件系统】相关推荐

  1. 用户空间和内核空间通讯Netlink

    用户空间和内核空间通讯Netlink http://pan.baidu.com/s/1i386MWX

  2. 用户空间和内核空间通讯之【Netlink 中】

    今天我们来动手演练一下Netlink的用法,看看它到底是如何实现用户-内核空间的数据通信的.我们依旧是在2.6.21的内核环境下进行开发. 在文件里包含了Netlink协议簇已经定义好的一些预定义协议 ...

  3. linux内核空间和用户空间的是怎样区别的,如何交互,如何从用户空间进入内核空间

    linux驱动程序一般工作在内核空间,但也可以工作在用户空间.下面我们将详细解析,什么是内核空间,什么是用户空间,以及如何判断他们. Linux简化了分段机制,使得虚拟地址与线性地址总是一致,因此,L ...

  4. linux 物理内存用完了_Linux用户空间与内核空间(理解高端内存)

    Linux内核地址映射模型 x86 CPU采用了段页式地址映射模型.进程代码中的地址为逻辑地址,经过段页式地址映射后,才真正访问物理内存. 段页式机制如下图. Linux内核地址空间划分 通常32位L ...

  5. 用户空间与内核空间,进程上下文与中断上下文[总结]

    用户空间与内核空间,进程上下文与中断上下文[总结] 最近有研究到zabbix监控,就得清楚cpu各个指标的含义, 1,简单回顾下cpu及计算机组成: 计算机五大部件: 运算器 控制器 存储器 输入/输 ...

  6. 虚拟内存,虚拟地址空间,用户空间,内核空间

    虚拟内存,就是用磁盘作为介质,暂时性存储数据,和主存进行换入换出,使程序能够使用更多的内存.虚拟内存是单位是页,固定大小的数据方便数据的交换.如果一个应用程序要访问某段内存,通过MMU得到相应的页号, ...

  7. 【Binder 机制】进程通信 | 用户空间与内核空间 | MMU 与虚拟内存地址

    文章目录 一.进程通信 二.用户空间与内核空间 三.MMU 与虚拟内存地址 一.进程通信 进程隔离概念 : 系统中的进程存在 " 进程隔离 " , 出于对进程运行的保护 , 两个进 ...

  8. Linux Malloc分析-从用户空间到内核空间

    Linux Malloc分析-从用户空间到内核空间 本文介绍malloc的实现及其malloc在进行堆扩展操作,并分析了虚拟地址到物理地址是如何实现映射关系. ordeder原创,原文链接: http ...

  9. Linux 用户空间和内核空间指的是什么?

    Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间.两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数 ...

  10. (整理)用户空间_内核空间以及内存映射

    内核空间和用户空间   现代操作系统采用虚拟存储器,对于32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方).操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也 ...

最新文章

  1. c语言用链表的方式实现多项式加减,如何实现C语言单链表多项式相加的操作
  2. linux 定时任务
  3. B-Tree和B+tree
  4. 鸿蒙系统sp3什么意思,怎么看电脑系统是哪个版本的?例如SP2 SP3?
  5. ubuntu服务器创建共享文件夹,Ubuntu samba安装创建共享目录及使用
  6. iOS 播放音频的几种方法
  7. Norton AntiVirus 8.0 企业版服务器客户端安装说明
  8. ios 按钮下面加下划线_iOS 给部分文字加下划线四种方法
  9. xbox360 无线手柄 通过cmd_vel控制小乌龟运动
  10. 如何浏览自己的新浪微博图床
  11. Tesla_T4加速卡详细参数
  12. OSI七层网络模型和四层网络模型详解
  13. python做一副54扑克牌发牌技巧_最强大脑!杭四中高一男生邹全50秒轻松记住一副扑克牌...
  14. SNPE教程一:基本概念
  15. 内存碎片---内部碎片外部碎片
  16. Excel2007版的常用功能(7):Excel公式与函数
  17. tensor.repeat()
  18. python shell的无法保存_关于shell:如何保存Python交互式会话?
  19. 程序员现状分析,什么时候是进入IT行业的黄金时期?千万注意了
  20. Thinkphp 源码分析1

热门文章

  1. JS getAttribute和setAttribute(取得和设置属性)的使用介绍
  2. HBase简单代码实例(Java)
  3. JS 提交form表单
  4. OK6410裸机开发之LED灯
  5. android之ListView和adapter配合显示图片和文字列表
  6. Hadoop学习全程记录——在Eclipse中运行第一个MapReduce程序
  7. 查询在一张表不在另外一张表的记录
  8. muduo之Logger
  9. 彻底解决IAR中Go to definition of不可用
  10. 常考数据结构与算法:二叉树的最大深度