1. 前言
Linux系统上的/proc目录是一种文件系统,即proc文件系统。
与其它常见的文件系统不同的是,/proc是一种伪文件系统(也即虚拟文件系统),存储的是当前内核运行状态的一系列特殊文件,用户可以通过这些文件查看有关系统硬件及当前正在运行进程的信息,甚至可以通过更改其中某些文件来改变内核的运行状态。
当前的实验平台是嵌入式Linux开发板,根文件系统挂载成功后,进入命令就能看到proc
目录,这个目录里正常情况下已经生成了很多文件。通过cat
命令读取这些文件,可以得到很多内核的信息。
比如:查看中断有哪些注册了,中断从上电到现在响应了多少次,杂项设备注册了哪些,帧缓冲节点有哪些,RTC时间查看,等等。
下面是proc目录下文件的功能的详细介绍(资源来源与网络):
2. 获取CPU使用率
下面这份代码是利用/proc/stat
文件获取当前CPU的占用率详细信息,通过C语言代码读取数据后,进行分析,处理。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
typedef struct cpu_occupy_ //定义一个cpu occupy的结构体
{
char name[20]; //定义一个char类型的数组名name有20个元素
unsigned int user; //定义一个无符号的int类型的user
unsigned int nice; //定义一个无符号的int类型的nice
unsigned int system; //定义一个无符号的int类型的system
unsigned int idle; //定义一个无符号的int类型的idle
unsigned int iowait;
unsigned int irq;
unsigned int softirq;
}cpu_occupy_t;
double cal_cpuoccupy (cpu_occupy_t *o, cpu_occupy_t *n)
{
double od, nd;
double id, sd;
double cpu_use ;
od = (double) (o->user + o->nice + o->system +o->idle+o->softirq+o->iowait+o->irq);//第一次(用户+优先级+系统+空闲)的时间再赋给od
nd = (double) (n->user + n->nice + n->system +n->idle+n->softirq+n->iowait+n->irq);//第二次(用户+优先级+系统+空闲)的时间再赋给od
id = (double) (n->idle); //用户第一次和第二次的时间之差再赋给id
sd = (double) (o->idle) ; //系统第一次和第二次的时间之差再赋给sd
if((nd-od) != 0)
cpu_use =100.0 - ((id-sd))/(nd-od)*100.00; //((用户+系统)乖100)除(第一次和第二次的时间差)再赋给g_cpu_used
else
cpu_use = 0;
return cpu_use;
}
void get_cpuoccupy (cpu_occupy_t *cpust)
{
FILE *fd;
int n;
char buff[256];
cpu_occupy_t *cpu_occupy;
cpu_occupy=cpust;
fd = fopen ("/proc/stat", "r");
if(fd == NULL)
{
perror("fopen:");
exit (0);
}
fgets (buff, sizeof(buff), fd);
sscanf (buff, "%s %u %u %u %u %u %u %u", cpu_occupy->name, &cpu_occupy->user, &cpu_occupy->nice,&cpu_occupy->system, &cpu_occupy->idle ,&cpu_occupy->iowait,&cpu_occupy->irq,&cpu_occupy->softirq);
fclose(fd);
}
double get_sysCpuUsage()
{
cpu_occupy_t cpu_stat1;
cpu_occupy_t cpu_stat2;
double cpu;
get_cpuoccupy((cpu_occupy_t *)&cpu_stat1);
sleep(1);
//第二次获取cpu使用情况
get_cpuoccupy((cpu_occupy_t *)&cpu_stat2);
//计算cpu使用率
cpu = cal_cpuoccupy ((cpu_occupy_t *)&cpu_stat1, (cpu_occupy_t *)&cpu_stat2);
return cpu;
}
int main(int argc,char **argv)
{
while(1)
{
printf("CPU占用率:%f\n",get_sysCpuUsage());
}
return 0;
}
3. proc驱动相关接口
Proc文件接口,主要用于驱动代码调试,获取内核信息,可以直接使用cat命令访问proc目录下的对应文件接口即可。
需要使用的头文件:
#include <linux/proc_fs.h>
#include <linux/fs.h>
下面介绍内核里proc接口实现的相关函数接口:
1. 在proc目录下创建子目录函数
static inline struct proc_dir_entry *proc_mkdir(const char *name,struct proc_dir_entry *parent)
示例: //注意只能创建单层目录
//在proc目录下创建aaa文件夹
proc_mkdir("aaa",NULL);
2. 在proc目录下创建文件
static inline struct proc_dir_entry *proc_create(const char *name, //文件名称
umode_t mode, //模式,默认为0
struct proc_dir_entry *parent, //父目录结构
const struct file_operations *proc_fops) //文件集合
示例:
//在proc目录下创建一个文件
proc_create("aaa/tiny4412_proc_test", 0, NULL, &fops_proc);
3. 删除proc目录下之前创建的文件或者目录
void remove_proc_entry(const char *name, //文件的路径
struct proc_dir_entry *parent //父目录结构
)
示例:
remove_proc_entry("aaa/tiny4412_proc_test", NULL);
注意: 如果是删除目录,需要先把目录下的文件删除掉,每次删除必须保证目录是空的。
4. 编写proc接口测试驱动
4.1 案例1
下面驱动代码注册之后,会在proc目录下创建一个tiny4412_proc
文件,通过cat读取这个文件,可以打印驱动代码里设置好的信息。驱动卸载时会删除这个tiny4412_proc
文件。
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/proc_fs.h>
static int tiny4412_open(struct inode *inode, struct file *file)
{
printk("tiny4412_open ok\n");
return 0;
}
static ssize_t tiny4412_read(struct file *file, char __user *buf, size_t cnt, loff_t *loff)
{
copy_to_user(buf,"123456",6);
printk("tiny4412_read调用成功.\n");
return 0;
}
static int tiny4412_release(struct inode *inode, struct file *file)
{
return 0;
}
static struct file_operations tiny4412_fops=
{
.open=tiny4412_open,
.read=tiny4412_read,
.release=tiny4412_release,
};
static int __init tiny4412_init(void)
{
proc_mkdir("wbyq",0);
/*创建内核接口: proc 存放内核信息*/
proc_create("wbyq/tiny4412_proc",0, NULL, &tiny4412_fops);
printk("驱动安装成功.\n");
return 0;
}
static void __exit tiny4412_exit(void)
{
remove_proc_entry("wbyq/tiny4412_proc", NULL);
remove_proc_entry("wbyq", NULL);
printk("驱动卸载成功.\n");
}
/*驱动的入口:insmod xxx.ko*/
module_init(tiny4412_init);
/*驱动的出口: rmmod xxx.ko*/
module_exit(tiny4412_exit);
/*模块的许可证*/
MODULE_LICENSE("GPL");
/*模块的作者*/
MODULE_AUTHOR("wbyq");
4.2 案例2
下面这份代码是在字符设备框架代码里增加了proc接口,驱动安装之后,会在proc目录下创建tiny4412_proc
文件,通过cat命令读取tiny4412_proc
文件,可以打印出当前主设备号下所有的子设备信息。
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <asm/uaccess.h>
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/list.h>
#include <linux/proc_fs.h>
static struct class *tiny4412_beep_class;
static unsigned int major=0; //主设备号
static LIST_HEAD(tiny4412_beep_list); //链表头
static DEFINE_MUTEX(tiny4412_beep_mtx); //互斥锁
#define DYNAMIC_MINORS 64 /* like dynamic majors */
static DECLARE_BITMAP(beep_minors, DYNAMIC_MINORS);
struct tiny4412_beep_device
{
int minor; /*次设备号*/
const char *name; /*设备节点的名称*/
const struct file_operations *fops; /*文件操作集合*/
struct list_head list; //链表
};
int tiny4412_beep_register(struct tiny4412_beep_device *beep_dev)
{
struct tiny4412_beep_device *c;
dev_t dev;
INIT_LIST_HEAD(&beep_dev->list);
mutex_lock(&tiny4412_beep_mtx);
//查找传入的次设备号是否冲突
list_for_each_entry(c, &tiny4412_beep_list, list)
{
if(c->minor == beep_dev->minor)
{
mutex_unlock(&tiny4412_beep_mtx);
return -EBUSY;
}
}
//自动分配
if(beep_dev->minor == MISC_DYNAMIC_MINOR)
{
int i = find_first_zero_bit(beep_minors,DYNAMIC_MINORS);
if (i >= DYNAMIC_MINORS)
{
mutex_unlock(&tiny4412_beep_mtx);
return -EBUSY;
}
beep_dev->minor = DYNAMIC_MINORS - i - 1;
set_bit(i,beep_minors);
}
//合成设备号
dev = MKDEV(major, beep_dev->minor);
//创建设备节点
device_create(tiny4412_beep_class,NULL,dev,NULL,"%s", beep_dev->name);
list_add(&beep_dev->list,&tiny4412_beep_list);
//解锁
mutex_unlock(&tiny4412_beep_mtx);
return 0;
}
int tiny4412_beep_deregister(struct tiny4412_beep_device *beep_dev)
{
int i = DYNAMIC_MINORS - beep_dev->minor - 1;
mutex_lock(&tiny4412_beep_mtx);
list_del(&beep_dev->list);
//将dev目录下的文件删除掉
device_destroy(tiny4412_beep_class, MKDEV(major, beep_dev->minor));
if (i < DYNAMIC_MINORS && i >= 0)
clear_bit(i, beep_minors);
mutex_unlock(&tiny4412_beep_mtx);
return 0;
}
EXPORT_SYMBOL_GPL(tiny4412_beep_register);
EXPORT_SYMBOL_GPL(tiny4412_beep_deregister);
//底层open函数
static int tiny4412_beep_open(struct inode * inode, struct file * file)
{
//得到次设备号
int minor = iminor(inode);
struct tiny4412_beep_device *c;
struct file_operations *new_fops,*old_fops;
mutex_lock(&tiny4412_beep_mtx);
//遍历链表--找到链表里相同的次设备号
list_for_each_entry(c,&tiny4412_beep_list, list)
{
if (c->minor == minor)
{
new_fops = fops_get(c->fops); //得到47次设备号对应的结构体地址
break;
}
}
file->f_op = new_fops; //改变指向--文件操作集合的指向
if(file->f_op->open)
{
file->f_op->open(inode,file);
}
fops_put(old_fops);
mutex_unlock(&tiny4412_beep_mtx);
return 0;
}
static const struct file_operations tiny4412_beep_fops =
{
.owner = THIS_MODULE,
.open = tiny4412_beep_open,
};
static ssize_t tiny4412_read(struct file *file, char __user *buf, size_t cnt, loff_t *loff)
{
struct tiny4412_beep_device *c;
//遍历链表--找到链表里相同的次设备号
list_for_each_entry(c,&tiny4412_beep_list, list)
{
printk("%d %s\n",c->minor,c->name);
}
return 0;
}
static struct file_operations tiny4412_fops=
{
.read=tiny4412_read,
};
static int __init tiny4412_beep_class_init(void)
{
/*1. 创建设备类*/
tiny4412_beep_class=class_create(THIS_MODULE,"tiny4412_beep");
/*2. 注册字符设备*/
major=register_chrdev(0,"tiny4412_beep",&tiny4412_beep_fops);
proc_mkdir("wbyq",0);
/*创建内核接口: proc 存放内核信息*/
proc_create("wbyq/tiny4412_proc",0, NULL, &tiny4412_fops);
return 0;
}
static void __exit tiny4412_beep_class_cleanup(void)
{
remove_proc_entry("wbyq/tiny4412_proc", NULL);
remove_proc_entry("wbyq", NULL);
//注销设备类
class_destroy(tiny4412_beep_class);
//注销字符设备
unregister_chrdev(major,"tiny4412_beep");
}
module_init(tiny4412_beep_class_init);
module_exit(tiny4412_beep_class_cleanup);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wbyq");