Linux内核调试技术之自构proc

简介:

1、简介

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

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

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


  
  
  1. void __init proc_misc_init(void){ 
  2.     ......................... 
  3.         struct proc_dir_entry *entry; 
  4.         //这里创建了一个proc入口kmsg 
  5.         entry = create_proc_entry("kmsg", S_IRUSR, &proc_root); 
  6.         if (entry) /*构造一个proc_fops结构*/ 
  7.  entry->proc_fops = &proc_kmsg_operations;......................... }  

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


  
  
  1. const struct file_operations proc_kmsg_operations = { 
  2.     .read        = kmsg_read, 
  3.     .poll        = kmsg_poll, 
  4.     .open        = kmsg_open, 
  5.     .release    = kmsg_release,};  

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

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

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

  • 它是一个首尾相连的FIFO的数据结构,采用数组的线性空间,数据组织简单,能很快知道队列是否满为空。能以很快速度的来存取数据。
  • 因为有简单高效的原因,甚至在硬件都实现了环形队列。
  • 环形队列广泛用于网络数据收发,和不同程序间数据交换(比如内核与应用程序大量交换数据,从硬件接收大量数据)均使用了环形队列。

3.1.环形队列实现原理

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

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

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

环形队列

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

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

(1)是附加一个标志位tag

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

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

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

队列空: head==tail

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

4、程序编写


  
  
  1. #include <linux/module.h> 
  2. #include<linux/kernel.h> 
  3. #include<linux/fs.h> 
  4. #include<linux/init.h> 
  5. #include<linux/delay.h> 
  6. #include<asm/uaccess.h> 
  7. #include<asm/irq.h> 
  8. #include<asm/io.h> 
  9. #include<asm/arch/regs-gpio.h> 
  10. #include<asm/hardware.h> 
  11. #include<linux/proc_fs.h> 
  12.  
  13. #define MYLOG_BUF_LEN 1024 
  14. static char mylog_buf[MYLOG_BUF_LEN]; 
  15. static char tmp_buf[MYLOG_BUF_LEN]; 
  16. static int mylog_r = 0; 
  17. static int mylog_w = 0; 
  18. static int mylog_r_tmp = 0; 
  19.  
  20. /*休眠队列初始化*/ 
  21. static DECLARE_WAIT_QUEUE_HEAD(mymsg_waitq); 
  22.  
  23. /* 
  24. *判断环形队列是否为空 
  25. *返回0:表示不空  返回1:表示空 
  26. */ 
  27. static int is_mylog_empty(void) 
  28.    return (mylog_r == mylog_w); 
  29.  
  30. /* 
  31. *判断环形队列是否满 
  32. *返回0:表示不满  返回1:表示满 
  33. */ 
  34. static int is_mylog_full(void) 
  35.    return((mylog_w + 1)% MYLOG_BUF_LEN == mylog_r); 
  36.  
  37. /* 
  38. *在读取的时候,判断环形队列中数据是否为空 
  39. *返回0:表示不空  返回1:表示空 
  40. */ 
  41. static int is_mylog_empty_for_read(void) 
  42.    return (mylog_r_tmp == mylog_w); 
  43.  
  44. /* 
  45. *往循环队列中存字符 
  46. *输入:c字符 单位:1byte 
  47. *输出:无 
  48. */ 
  49. static void mylog_putc(char c) 
  50.  
  51.    if(is_mylog_full()) 
  52.     { 
  53.        /*如果检测到队列已经满了,则丢弃该数据*/ 
  54.         mylog_r= (mylog_r + 1) % MYLOG_BUF_LEN; 
  55.          
  56.        /*mylog_r_tmp不能大于mylog_r*/ 
  57.         if((mylog_r_tmp + 1)% MYLOG_BUF_LEN == mylog_r) 
  58.             mylog_r_tmp= mylog_r; 
  59.          
  60.     } 
  61.     mylog_buf[mylog_w]= c; 
  62.    /*当mylog_w=1023的时候 (mylog_w+1) % MYLOG_BUF_LEN =0,回到队列头,实现循环*/ 
  63.     mylog_w= (mylog_w + 1) % MYLOG_BUF_LEN; 
  64.    /* 唤醒等待数据的进程*/     
  65.     wake_up_interruptible(&mymsg_waitq);   
  66.  
  67. /* 
  68. *从循环队列中读字符 
  69. *输入:*p 单位:1byte 
  70. *输出:1表示成功 
  71. */ 
  72. static int mylog_getc(char *p) 
  73.    /*判断数据是否为空*/ 
  74.     if (is_mylog_empty_for_read()) 
  75.     { 
  76.        return 0; 
  77.     } 
  78.    *p = mylog_buf[mylog_r_tmp ]; 
  79.     mylog_r_tmp = (mylog_r_tmp  + 1) % MYLOG_BUF_LEN; 
  80.    return 1; 
  81.  
  82. /* 
  83. *调用myprintk,和printf用法相同 
  84. */ 
  85. int myprintk(const char *fmt, ...) 
  86.     va_list args; 
  87.    int i; 
  88.    int j; 
  89.  
  90.     va_start(args, fmt); 
  91.     i= vsnprintf(tmp_buf, INT_MAX, fmt, args); 
  92.     va_end(args); 
  93.      
  94.    for (j = 0; j < i; j++) 
  95.         mylog_putc(tmp_buf[j]); 
  96.          
  97.    return i; 
  98.  
  99.  
  100. static ssize_t mymsg_read(struct file *file, char __user *buf, 
  101.             size_t count, loff_t*ppos) 
  102.    int error=0; 
  103.     size_t i=0; 
  104.    char c; 
  105.    /* 把mylog_buf的数据copy_to_user, return*/ 
  106.  
  107.     /*非阻塞 和 缓冲区为空的时候返回*/ 
  108.     if ((file->f_flags & O_NONBLOCK) && is_mylog_empty()) 
  109.        return -EAGAIN; 
  110.      
  111.    /*休眠队列wait_event_interruptible(xxx,0)-->休眠*/ 
  112.     error= wait_event_interruptible(mymsg_waitq, !is_mylog_empty_for_read()); 
  113.      
  114.    /* copy_to_user*/ 
  115.     while (!error && (mylog_getc(&c)) && i < count) { 
  116.         error= __put_user(c, buf); 
  117.         buf++; 
  118.         i++; 
  119.     } 
  120.    if (!error) 
  121.         error= i; 
  122.    /*返回实际读到的个数*/ 
  123.     return error; 
  124.  
  125. static int mymsg_open(struct inode * inode, struct file * file) 
  126.     mylog_r_tmp= mylog_r; 
  127.    return 0; 
  128.  
  129.  
  130. const struct file_operations proc_mymsg_operations = { 
  131.     .read= mymsg_read, 
  132.     .open= mymsg_open, 
  133.     }; 
  134. static int mymsg_init(void) 
  135.    struct proc_dir_entry *myentry; kmsg 
  136.     myentry= create_proc_entry("mymsg", S_IRUSR, &proc_root); 
  137.    if (myentry) 
  138.         myentry->proc_fops = &proc_mymsg_operations; 
  139.    return 0; 
  140.  
  141. static void mymsg_exit(void) 
  142.     remove_proc_entry("mymsg", &proc_root); 
  143.  
  144. module_init(mymsg_init); 
  145. module_exit(mymsg_exit); 
  146.  
  147. /*声名到内核空间*/ 
  148. EXPORT_SYMBOL(myprintk); 
  149.  
  150. MODULE_LICENSE("GPL");  

5、测试程序

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

使用方法:

①extern int myprintk(const char *fmt, ...);声明

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


  
  
  1. #include <linux/module.h> 
  2. #include<linux/kernel.h> 
  3. #include<linux/fs.h> 
  4. #include<linux/init.h> 
  5. #include<linux/delay.h> 
  6. #include<asm/uaccess.h> 
  7. #include<asm/irq.h> 
  8. #include<asm/io.h> 
  9. #include<asm/arch/regs-gpio.h> 
  10. #include<asm/hardware.h> 
  11.  
  12. static struct class *firstdrv_class; 
  13. static struct class_device    *firstdrv_class_dev; 
  14.  
  15. volatile unsigned long *gpfcon = NULL
  16. volatile unsigned long *gpfdat = NULL
  17.  
  18. extern int myprintk(const char *fmt, ...); 
  19.  
  20. static int first_drv_open(struct inode *inode, struct file *file) 
  21.    static int cnt = 0; 
  22.     myprintk("first_drv_open : %d\n", ++cnt); 
  23.    /* 配置GPF4,5,6为输出*/ 
  24.     *gpfcon &= ~((0x3<<(4*2)) | (0x3<<(5*2)) | (0x3<<(6*2))); 
  25.    *gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2))); 
  26.    return 0; 
  27.  
  28. static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) 
  29.    int val; 
  30.    static int cnt = 0; 
  31.  
  32.     myprintk("first_drv_write : %d\n", ++cnt); 
  33.  
  34.     copy_from_user(&val, buf, count); //    copy_to_user(); 
  35.  
  36.     if (val == 1) 
  37.     { 
  38.        // 点灯 
  39.         *gpfdat &= ~((1<<4) | (1<<5) | (1<<6)); 
  40.     } 
  41.    else 
  42.     { 
  43.        // 灭灯 
  44.         *gpfdat |= (1<<4) | (1<<5) | (1<<6); 
  45.     } 
  46.      
  47.    return 0; 
  48.  
  49. static struct file_operations first_drv_fops = { 
  50.     .owner =  THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量*/ 
  51.     .open  =  first_drv_open,     
  52.     .write   =    first_drv_write,       
  53. }; 
  54.  
  55.  
  56. int major; 
  57. static int first_drv_init(void) 
  58.     myprintk("first_drv_init\n"); 
  59.     major= register_chrdev(0, "first_drv", &first_drv_fops); // 注册, 告诉内核 
  60.  
  61.     firstdrv_class= class_create(THIS_MODULE, "firstdrv"); 
  62.  
  63.     firstdrv_class_dev= class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL"xyz"); /* /dev/xyz*/ 
  64.  
  65.     gpfcon= (volatile unsigned long *)ioremap(0x56000050, 16); 
  66.     gpfdat= gpfcon + 1; 
  67.  
  68.    return 0; 
  69.  
  70. static void first_drv_exit(void) 
  71.     unregister_chrdev(major,"first_drv"); // 卸载 
  72.  
  73.     class_device_unregister(firstdrv_class_dev); 
  74.     class_destroy(firstdrv_class); 
  75.     iounmap(gpfcon); 
  76.  
  77. module_init(first_drv_init); 
  78. module_exit(first_drv_exit); 
  79.  
  80.  
  81. MODULE_LICENSE("GPL");  

6、在tty中测试效果


  
  
  1. # insmod my_msg.ko 
  2. # insmod first_drv.ko 
  3. # cat /proc/mymsg 
  4. mymsg_open mylog_r_tmp=0 
  5. first_drv_init  




本文作者:佚名
来源:51CTO
目录
相关文章
|
5天前
|
缓存 算法 Linux
深入理解Linux内核调度器:公平性与性能的平衡####
真知灼见 本文将带你深入了解Linux操作系统的核心组件之一——完全公平调度器(CFS),通过剖析其设计原理、工作机制以及在实际系统中的应用效果,揭示它是如何在众多进程间实现资源分配的公平性与高效性的。不同于传统的摘要概述,本文旨在通过直观且富有洞察力的视角,让读者仿佛亲身体验到CFS在复杂系统环境中游刃有余地进行任务调度的过程。 ####
26 6
|
6天前
|
缓存 NoSQL Linux
Linux调试
本文介绍了Linux调试、性能分析和追踪的培训资料,涵盖调试、性能分析和追踪的基础知识及常用工具。
34 6
Linux调试
|
4天前
|
缓存 资源调度 安全
深入探索Linux操作系统的心脏——内核配置与优化####
本文作为一篇技术性深度解析文章,旨在引领读者踏上一场揭秘Linux内核配置与优化的奇妙之旅。不同于传统的摘要概述,本文将以实战为导向,直接跳入核心内容,探讨如何通过精细调整内核参数来提升系统性能、增强安全性及实现资源高效利用。从基础概念到高级技巧,逐步揭示那些隐藏在命令行背后的强大功能,为系统管理员和高级用户打开一扇通往极致性能与定制化体验的大门。 --- ###
19 9
|
3天前
|
缓存 负载均衡 Linux
深入理解Linux内核调度器
本文探讨了Linux操作系统核心组件之一——内核调度器的工作原理和设计哲学。不同于常规的技术文章,本摘要旨在提供一种全新的视角来审视Linux内核的调度机制,通过分析其对系统性能的影响以及在多核处理器环境下的表现,揭示调度器如何平衡公平性和效率。文章进一步讨论了完全公平调度器(CFS)的设计细节,包括它如何处理不同优先级的任务、如何进行负载均衡以及它是如何适应现代多核架构的挑战。此外,本文还简要概述了Linux调度器的未来发展方向,包括对实时任务支持的改进和对异构计算环境的适应性。
18 6
|
4天前
|
缓存 Linux 开发者
Linux内核中的并发控制机制:深入理解与应用####
【10月更文挑战第21天】 本文旨在为读者提供一个全面的指南,探讨Linux操作系统中用于实现多线程和进程间同步的关键技术——并发控制机制。通过剖析互斥锁、自旋锁、读写锁等核心概念及其在实际场景中的应用,本文将帮助开发者更好地理解和运用这些工具来构建高效且稳定的应用程序。 ####
18 5
|
1天前
|
算法 Linux 调度
深入理解Linux内核调度器:从基础到优化####
本文旨在通过剖析Linux操作系统的心脏——内核调度器,为读者揭开其高效管理CPU资源的神秘面纱。不同于传统的摘要概述,本文将直接以一段精简代码片段作为引子,展示一个简化版的任务调度逻辑,随后逐步深入,详细探讨Linux内核调度器的工作原理、关键数据结构、调度算法演变以及性能调优策略,旨在为开发者与系统管理员提供一份实用的技术指南。 ####
14 4
|
4天前
|
算法 Unix Linux
深入理解Linux内核调度器:原理与优化
本文探讨了Linux操作系统的心脏——内核调度器(Scheduler)的工作原理,以及如何通过参数调整和代码优化来提高系统性能。不同于常规摘要仅概述内容,本摘要旨在激发读者对Linux内核调度机制深层次运作的兴趣,并简要介绍文章将覆盖的关键话题,如调度算法、实时性增强及节能策略等。
|
5天前
|
存储 监控 安全
Linux内核调优的艺术:从基础到高级###
本文深入探讨了Linux操作系统的心脏——内核的调优方法。文章首先概述了Linux内核的基本结构与工作原理,随后详细阐述了内核调优的重要性及基本原则。通过具体的参数调整示例(如sysctl、/proc/sys目录中的设置),文章展示了如何根据实际应用场景优化系统性能,包括提升CPU利用率、内存管理效率以及I/O性能等关键方面。最后,介绍了一些高级工具和技术,如perf、eBPF和SystemTap,用于更深层次的性能分析和问题定位。本文旨在为系统管理员和高级用户提供实用的内核调优策略,以最大化Linux系统的效率和稳定性。 ###
|
4天前
|
Java Linux Android开发
深入探索Android系统架构:从Linux内核到应用层
本文将带领读者深入了解Android操作系统的复杂架构,从其基于Linux的内核到丰富多彩的应用层。我们将探讨Android的各个关键组件,包括硬件抽象层(HAL)、运行时环境、以及核心库等,揭示它们如何协同工作以支持广泛的设备和应用。通过本文,您将对Android系统的工作原理有一个全面的认识,理解其如何平衡开放性与安全性,以及如何在多样化的设备上提供一致的用户体验。
|
3天前
|
缓存 运维 网络协议
深入Linux内核架构:操作系统的核心奥秘
深入Linux内核架构:操作系统的核心奥秘
18 2