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

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

 
   如何判断环形队列为空,为满有两种判断方法。
  一.是附加一个标志位tag
      当head赶上tail,队列空,则令tag=0,
      当tail赶上head,队列满,则令tag=1,
 
  二.限制tail赶上head,即队尾结点与队首结点之间至少留有一个元素的空间。
      队列空:   head==tail
      队列满:   (tail+1)% MAXN ==head

4、程序编写

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <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 = ;
static int mylog_w = ;
static int mylog_r_tmp = ; /*休眠队列初始化*/
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 + )% 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 = (mylog_r + ) % MYLOG_BUF_LEN; /*mylog_r_tmp不能大于mylog_r*/
if((mylog_r_tmp + )% 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 + ) % MYLOG_BUF_LEN;
/* 唤醒等待数据的进程 */
wake_up_interruptible(&mymsg_waitq);
} /*
*从循环队列中读字符
*输入:*p 单位:1byte
*输出:1表示成功
*/
static int mylog_getc(char *p)
{
/*判断数据是否为空*/
if (is_mylog_empty_for_read())
{
return ;
}
*p = mylog_buf[mylog_r_tmp ];
mylog_r_tmp = (mylog_r_tmp + ) % MYLOG_BUF_LEN;
return ;
} /*
*调用myprintk,和printf用法相同
*/
int myprintk(const char *fmt, ...)
{
va_list args;
int i;
int j; va_start(args, fmt);
i = vsnprintf(tmp_buf, INT_MAX, fmt, args);
va_end(args); for (j = ; j < i; j++)
mylog_putc(tmp_buf[j]); return i;
} static ssize_t mymsg_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
int error=;
size_t i=;
char c;
/* 把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 < count) {
error = __put_user(c, buf);
buf++;
i++;
}
if (!error)
error = i;
/*返回实际读到的个数*/
return error;
} static int mymsg_open(struct inode * inode, struct file * file)
{
mylog_r_tmp = mylog_r;
return ;
} 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 ;
} 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);使用

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h> 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 = ;
myprintk("first_drv_open : %d\n", ++cnt);
/* 配置GPF4,5,6为输出 */
*gpfcon &= ~((0x3<<(*)) | (0x3<<(*)) | (0x3<<(*)));
*gpfcon |= ((0x1<<(*)) | (0x1<<(*)) | (0x1<<(*)));
return ;
} static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
int val;
static int cnt = ; myprintk("first_drv_write : %d\n", ++cnt); copy_from_user(&val, buf, count); // copy_to_user(); if (val == )
{
// 点灯
*gpfdat &= ~((<<) | (<<) | (<<));
}
else
{
// 灭灯
*gpfdat |= (<<) | (<<) | (<<);
} return ;
} static struct file_operations first_drv_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = first_drv_open,
.write = first_drv_write,
}; int major;
static int first_drv_init(void)
{
myprintk("first_drv_init\n");
major = register_chrdev(, "first_drv", &first_drv_fops); // 注册, 告诉内核 firstdrv_class = class_create(THIS_MODULE, "firstdrv"); firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, ), NULL, "xyz"); /* /dev/xyz */ gpfcon = (volatile unsigned long *)ioremap(0x56000050, );
gpfdat = gpfcon + ; return ;
} 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");

6、在tty中测试效果

# insmod my_msg.ko
# insmod first_drv.ko
# cat /proc/mymsg
mymsg_open mylog_r_tmp=0
first_drv_init

成功!!!

感谢韦东山老师!!!!

linux内核调试技术之自构proc的更多相关文章

  1. Linux内核调试技术——jprobe使用与实现

    前一篇博文介绍了kprobes的原理与kprobe的使用与实现方式,本文介绍kprobes中的另外一种探測技术jprobe.它基于kprobe实现,不能在函数的任何位置插入探測点,仅仅能在函数的入口处 ...

  2. linux内核调试技术之printk

    原创博客:欢迎转载,转载请注明出处https://i.cnblogs.com/EditPosts.aspx?postid=6218383 1.简介(基于s3c2440 linux) 在内核调试技术之中 ...

  3. linux内核调试技术之修改内核定时器来定位系统僵死问题

    1.简介 在内核调试中,会经常出现内核僵死的问题,也就是发生死循环,内核不能产生调度.导致内核失去响应.这种情况下我们可以采用修改系统内核中的系统时钟的中断来定位发生僵死的进程和函数名称.因为内核系统 ...

  4. Linux kprobe调试技术使用

    kprobe调试技术是为了便于跟踪内核函数执行状态所设计的一种轻量级内核调试技术. 利用kprobe技术,可以在内核绝大多数函数中动态插入探测点,收集调试状态所需信息而基本不影响原有执行流程. kpr ...

  5. Linux内核调试的方式以及工具集锦【转】

    转自:https://blog.csdn.net/gatieme/article/details/68948080 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原 ...

  6. Linux内核调试的方式以及工具集锦

    原文:https://blog.csdn.net/gatieme/article/details/68948080 CSDN GitHubLinux内核调试的方式以及工具集锦 LDD-LinuxDev ...

  7. Linux内核调试方法总结

    Linux内核调试方法总结 一  调试前的准备 二  内核中的bug 三  内核调试配置选项 1  内核配置 2  调试原子操作 四  引发bug并打印信息 1  BUG()和BUG_ON() 2   ...

  8. Linux内核调试方法【转】

    转自:http://www.cnblogs.com/shineshqw/articles/2359114.html kdb:只能在汇编代码级进行调试: 优点是不需要两台机器进行调试. gdb:在调试模 ...

  9. 嵌入式Linux的调试技术

    本节我们研究嵌入式Linux的调试技术,对于复杂的Linux驱动及HAL等程序库,需要使用各种方法对其进行调试.刚开始讲了打印内核调试信息:printk,这个函数的用法与printf函数类似,只不过p ...

随机推荐

  1. 自定义基于 VLC 的视频播放器

    前言(蛋疼的背景故事) 前段时间,接了一个小项目,有个需求是要在系统待机一段时间以后,循环播放 MV(类似于 Windows 系统的屏幕保护). 听到这个需求,我首先想到的是 MediaPlayer ...

  2. 简单入门canvas - 通过刮奖效果来学习

    一 .前言 一直在做PC端的前端开发,从互联网到行业软件.最近发现移动端已经成为前端必备技能了,真是不能停止学习.HTML5新增的一些东西,canvas是用的比较多也比较复杂的一个,简单的入门了一下, ...

  3. [WCF]缺少一行代码引发的血案

    这是今天作项目支持的发现的一个关于WCF的问题,虽然最终我只是添加了一行代码就解决了这个问题,但是整个纠错过程是痛苦的,甚至最终发现这个问题都具有偶然性.具体来说,这是一个关于如何自动为服务接口(契约 ...

  4. .NET同步与异步之相关背景知识(六)

    在之前的五篇随笔中,已经介绍了.NET 类库中实现并行的常见方式及其基本用法,当然.这些基本用法远远不能覆盖所有,也只能作为一个引子出现在这里.以下是前五篇随笔的目录: .NET 同步与异步之封装成T ...

  5. 基于Composer Player 模型加载和相关属性设置

    主要是基于达索软件Composer Player.的基础上做些二次开发. public class ComposerToolBarSetting { public bool AntiAliasingO ...

  6. bzoj3208--记忆化搜索

    题目大意: 花花山峰峦起伏,峰顶常年被雪,Memphis打算帮花花山风景区的人员开发一个滑雪项目.    我们可以把风景区看作一个n*n的地图,每个点有它的初始高度,滑雪只能从高处往低处滑[严格大于] ...

  7. dedecms 后台栏目添加图片

    前台调用栏目时需要显示图标,整理一下: 第一步:“系统->SQL命令工具” , 插入sql语句 alter table dede_arctype add typeimg varchar() 第二 ...

  8. Node.js入门(一)

    一.Node.js本质上是js的运行环境. 二.可以解析js代码(没有浏览器安全级的限制): 提供系统级的API:1.文件的读写 2.进程的管理 3.网络通信 三.可以关注的四个网站: 1.https ...

  9. Android Retrofit 2.0 使用-补充篇

    推荐阅读,猛戳: 1.Android MVP 实例 2.Android Retrofit 2.0使用 3.RxJava 4.RxBus 5.Android MVP+Retrofit+RxJava实践小 ...

  10. JAVA装饰者模式(从现实生活角度理解代码原理)

    装饰者模式可以动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator模式相比生成子类更为灵活. 该模式的适用环境为: (1)在不影响其他对象的情况下,以动态.透明的方式给单个对象添加职 ...