Quantcast
Channel: CodeSection,代码区,Linux操作系统:Ubuntu_Centos_Debian - CodeSec
Viewing all articles
Browse latest Browse all 11063

Linux内核调试技术之自构proc

$
0
0
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指向可以写的位置。


Linux内核调试技术之自构proc

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

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

一.是附加一个标志位tag

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

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

队列空: head==tail

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


Linux内核调试技术之自构proc
4、程序编写 #include <linux/module.h>
<linux/kernel.h>
<linux/fs.h>
<linux/init.h>
<linux/delay.h>
<asm/uaccess.h>
<asm/irq.h>
<asm/io.h>
<asm/arch/regs-gpio.h>
<asm/hardware.h>
<linux/proc_fs.h>

#define MYLOG_BUF_LEN 1024

static char mylog_buf[MYLOG_BUF_LEN]; static char tmp_buf[MYLOG_BUF_LEN]; static int mylog_r = 0

;

static int mylog_w = 0

;

static int mylog_r_tmp = 0

;

/* 休眠队列初始化 */ static

DECLARE_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( char

c)

{

if

(is_mylog_full())

{

/* 如果检测到队列已经满了,则丢弃该数据 */
= (mylog_r + 1 ) %

MYLOG_BUF_LEN;

/* mylog_r_tmp不能大于mylog_r */ if ((mylog_r_tmp + 1 )% MYLOG_BUF_LEN ==

mylog_r)

=

mylog_r;

}

=

c;

/* 当mylog_w=1023的时候 (mylog_w+1) % MYLOG_BUF_LEN =0,回到队列头,实现循环 */
= (mylog_w + 1 ) %

MYLOG_BUF_LEN;

/* 唤醒等待数据的进程 */ &

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 + 1 ) %

MYLOG_BUF_LEN;

return 1

;

}

/*

*调用myprintk,和printf用法相同

*/ int myprintk( const char *

fmt, ...)

{

va_list args;

int

i;

int

j;

va_start(args, fmt);

=

vsnprintf(tmp_buf, INT_MAX, fmt, args);

va_end(args);

for (j = 0 ; j < i; j++

)

mylog_putc(tmp_buf[j]); return

i;

}

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

buf,

*

ppos)

{

int error= 0

;

= 0

;

char

c;

/* 把mylog_buf的数据copy_to_user, return */

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

if ((file->f_flags & O_NONBLOCK) &&

is_mylog_empty())

return -

EAGAIN;

/* 休眠队列wait_event_interruptible(xxx,0)-->休眠 */
= wait_event_interruptible(mymsg_waitq, !

is_mylog_empty_for_read());

/* copy_to_user */ while (!error && (mylog_getc(&c)) && i <

count) {

=

__put_user(c, buf);

++

;

++

;

}

i

Viewing all articles
Browse latest Browse all 11063

Trending Articles