linux内核创建proc,Linux内核调试技术之自构proc

1、简介

在上一篇中,在内核中使用printk可以讲调试信息保存在log_buf缓冲区中,可以使用命令 #cat /proc/kmsg  将缓冲区的数区的数数据打印出来,今天我们就来研究一下,自己写kmsg这个文件,我们取名叫做 mymsg。

2、查看内核中 /proc/kmsg怎么写的!

在Proc_misc.c (fs\proc) 文件中:

void __init proc_misc_init(void)

{

.........................struct proc_dir_entry *entry;//这里创建了一个proc入口kmsg

entry = create_proc_entry("kmsg", S_IRUSR, &proc_root);if(entry)

/*构造一个proc_fops结构*/

entry->proc_fops = &proc_kmsg_operations;  .........................

}

在Kmsg.c (fs\proc) 文件中:

const struct file_operations proc_kmsg_operations ={

.read=kmsg_read,

.poll=kmsg_poll,

.open=kmsg_open,

.release=kmsg_release,

};

在用户空间中使用 cat /proc/kmsg的时候,会调用kmsg_open,在调用kmsg_read函数,读取log_buf中的数据,拷贝到用户空间显示。

3、在写之前,我们需要来学习一下循环队列

信息来源:(http://blog.sina.com.cn/s/blog_8b200d440100xsug.html)

环形队列是在实际编程极为有用的数据结构,它有如下特点。

它是一个首尾相连的FIFO的数据结构,采用数组的线性空间,数据组织简单,能很快知道队列是否满为空。能以很快速度的来存取数据。

因为有简单高效的原因,甚至在硬件都实现了环形队列。

环形队列广泛用于网络数据收发,和不同程序间数据交换(比如内核与应用程序大量交换数据,从硬件接收大量数据)均使用了环形队列。

3.1.环形队列实现原理

内存上没有环形的结构,因此环形队列实上是数组的线性空间来实现。那当数据到了尾部如何处理呢?它将转回到0位置来处理。这个的转回是通过取模操作来执行的。

因此环列队列的是逻辑上将数组元素q[0]与q[MAXN-1]连接,形成一个存放队列的环形空间。

为了方便读写,还要用数组下标来指明队列的读写位置。head/tail.其中head指向可以读的位置,tail指向可以写的位置。

84f48fc08a5ef08874871c12e16f4567.gif

环形队列的关键是判断队列为空,还是为满。当tail追上head时,队列为满时,当head追上tail时,队列为空。但如何知道谁追上谁。还需要一些辅助的手段来判断.

如何判断环形队列为空,为满有两种判断方法。

一.是附加一个标志位tag

当head赶上tail,队列空,则令tag=0,

当tail赶上head,队列满,则令tag=1,

二.限制tail赶上head,即队尾结点与队首结点之间至少留有一个元素的空间。

队列空:   head==tail

队列满:   (tail+1)% MAXN ==head

6ac4f6a3425f2336c257bf948f47b047.gif

4、程序编写

7065189b863144fd293fa4d195257033.gif

#include #include#include#include#include#include#include#include#include#include#include

#define MYLOG_BUF_LEN 1024

static charmylog_buf[MYLOG_BUF_LEN];static chartmp_buf[MYLOG_BUF_LEN];static int mylog_r = 0;static int mylog_w = 0;static int mylog_r_tmp = 0;/*休眠队列初始化*/

staticDECLARE_WAIT_QUEUE_HEAD(mymsg_waitq);/**判断环形队列是否为空

*返回0:表示不空  返回1:表示空*/

static int is_mylog_empty(void)

{return (mylog_r ==mylog_w);

}/**判断环形队列是否满

*返回0:表示不满  返回1:表示满*/

static int is_mylog_full(void)

{return((mylog_w + 1)% MYLOG_BUF_LEN ==mylog_r);

}/**在读取的时候,判断环形队列中数据是否为空

*返回0:表示不空  返回1:表示空*/

static int is_mylog_empty_for_read(void)

{return (mylog_r_tmp ==mylog_w);

}/**往循环队列中存字符

*输入:c字符 单位:1byte

*输出:无*/

static void mylog_putc(charc)

{if(is_mylog_full())

{/*如果检测到队列已经满了,则丢弃该数据*/mylog_r= (mylog_r + 1) %MYLOG_BUF_LEN;/*mylog_r_tmp不能大于mylog_r*/

if((mylog_r_tmp + 1)% MYLOG_BUF_LEN ==mylog_r)

mylog_r_tmp=mylog_r;

}

mylog_buf[mylog_w]=c;/*当mylog_w=1023的时候 (mylog_w+1) % MYLOG_BUF_LEN =0,回到队列头,实现循环*/mylog_w= (mylog_w + 1) %MYLOG_BUF_LEN;/*唤醒等待数据的进程*/wake_up_interruptible(&mymsg_waitq);

}/**从循环队列中读字符

*输入:*p 单位:1byte

*输出:1表示成功*/

static int mylog_getc(char *p)

{/*判断数据是否为空*/

if(is_mylog_empty_for_read())

{return 0;

}*p =mylog_buf[mylog_r_tmp ];

mylog_r_tmp= (mylog_r_tmp  + 1) %MYLOG_BUF_LEN;return 1;

}/**调用myprintk,和printf用法相同*/

int myprintk(const char *fmt, ...)

{

va_list args;inti;intj;

va_start(args, fmt);

i=vsnprintf(tmp_buf, INT_MAX, fmt, args);

va_end(args);for (j = 0; j < i; j++)

mylog_putc(tmp_buf[j]);returni;

}static ssize_t mymsg_read(struct file *file, char __user *buf,

size_t count, loff_t*ppos)

{int error=0;

size_t i=0;charc;/*把mylog_buf的数据copy_to_user, return*/

/*非阻塞 和 缓冲区为空的时候返回*/

if ((file->f_flags & O_NONBLOCK) &&is_mylog_empty())return -EAGAIN;/*休眠队列wait_event_interruptible(xxx,0)-->休眠*/error= wait_event_interruptible(mymsg_waitq, !is_mylog_empty_for_read());/*copy_to_user*/

while (!error && (mylog_getc(&c)) && i

error=__put_user(c, buf);

buf++;

i++;

}if (!error)

error=i;/*返回实际读到的个数*/

returnerror;

}static int mymsg_open(struct inode * inode, struct file *file)

{

mylog_r_tmp=mylog_r;return 0;

}const struct file_operations proc_mymsg_operations ={

.read=mymsg_read,

.open=mymsg_open,

};static int mymsg_init(void)

{struct proc_dir_entry *myentry; kmsg

myentry= create_proc_entry("mymsg", S_IRUSR, &proc_root);if(myentry)

myentry->proc_fops = &proc_mymsg_operations;return 0;

}static void mymsg_exit(void)

{

remove_proc_entry("mymsg", &proc_root);

}

module_init(mymsg_init);

module_exit(mymsg_exit);/*声名到内核空间*/EXPORT_SYMBOL(myprintk);

MODULE_LICENSE("GPL");

5、测试程序

注意:在上面程序中 使用了 EXPORT_SYMBOL(myprintk);意思是把myprintk可以在整个内核空间使用。

使用方法:①extern int myprintk(const char *fmt, ...);声明

②myprintk("first_drv_open : %d\n", ++cnt);使用

7065189b863144fd293fa4d195257033.gif

#include #include#include#include#include#include#include#include#include#include

static struct class *firstdrv_class;static struct class_device    *firstdrv_class_dev;volatile unsigned long *gpfcon =NULL;volatile unsigned long *gpfdat =NULL;extern int myprintk(const char *fmt, ...);static int first_drv_open(struct inode *inode, struct file *file)

{static int cnt = 0;

myprintk("first_drv_open : %d\n", ++cnt);/*配置GPF4,5,6为输出*/

*gpfcon &= ~((0x3<

}static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)

{intval;static int cnt = 0;

myprintk("first_drv_write : %d\n", ++cnt);

copy_from_user(&val, buf, count); //copy_to_user();

if (val == 1)

{//点灯

*gpfdat &= ~((1<<4) | (1<<5) | (1<<6));

}else{//灭灯

*gpfdat |= (1<<4) | (1<<5) | (1<<6);

}return 0;

}static struct file_operations first_drv_fops ={

.owner=  THIS_MODULE,    /*这是一个宏,推向编译模块时自动创建的__this_module变量*/.open=first_drv_open,

.write=first_drv_write,

};intmajor;static int first_drv_init(void)

{

myprintk("first_drv_init\n");

major= register_chrdev(0, "first_drv", &first_drv_fops); //注册, 告诉内核

firstdrv_class= class_create(THIS_MODULE, "firstdrv");

firstdrv_class_dev= class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /*/dev/xyz*/gpfcon= (volatile unsigned long *)ioremap(0x56000050, 16);

gpfdat= gpfcon + 1;return 0;

}static void first_drv_exit(void)

{

unregister_chrdev(major,"first_drv"); //卸载

class_device_unregister(firstdrv_class_dev);

class_destroy(firstdrv_class);

iounmap(gpfcon);

}

module_init(first_drv_init);

module_exit(first_drv_exit);

MODULE_LICENSE("GPL");

View Code

6、在tty中测试效果

成功!!!

感谢韦东山老师!!!!

0b1331709591d260c1c78e86d0c51c18.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值