linux驱动常用输出和调试手段

简介:


结合scull驱动代码,来观察其实现使用。

1.   创建/proc文件调试

在/proc 下的每个文件都绑到一个内核函数上, 当文件被读的时候即时产生文件内容.

使用 /proc 的模块需要包含 <linux/proc_fs.h>

当一个进程读模块的 /proc 文件, 内核分配了一页内存(就是说, PAGE_SIZE 字节), 驱动可以写入数据来返回给用户空间. 那个缓存区传递给你的函数, 是一个file_operations的函数集方法:

struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,

                                        struct proc_dir_entry *parent,

                                        const struct file_operations *proc_fops,

                                        void *data)

在驱动中通过如下调用:

proc_create_data("scullmem", 0 /* default mode */,

                        NULL /* parent dir */, &scullmem_proc_ops,

                        NULL /* client data */);

在/proc文件夹下创建scullmem文件,其对应的操作函数集合是

scullmem_proc_ops,如下:

static struct file_operations scullmem_proc_ops = {

        .owner   = THIS_MODULE,

        .open    = scullmem_proc_open,

        .read    = seq_read,

        .llseek  = seq_lseek,

        .release = single_release

};

主要是scullmem_proc_open函数,就一条语句,调用scull_read_procmem.

static int scullmem_proc_open(struct inode *inode, struct file *file)

{

        return single_open(file, scull_read_procmem, NULL);

}

其中single_open在内核中定义如下:

int single_open(struct file *file, int (*show)(struct seq_file *, void *), void *data) 

而scull_read_procmem函数实现真正的输出操作。

移除/proc下目录:

void remove_proc_entry(const char *name, struct proc_dir_entry *parent)

/proc处理大文件时候容易出错。内核提供了seq_file接口。

2.   seq_file

需要包含 <linux/seq_file.h> ,必须创建 4 个 iterator 方 法, 称为 start, next, stop, 和 show.

创建的proc下目录如下,并关联proc文件相关的操作:

proc_create("scullseq", 0, NULL, &scullseq_proc_ops);

其中scullseq_proc_ops如下:

static struct file_operations scullseq_proc_ops = {

        .owner   = THIS_MODULE,

        .open    = scullseq_proc_open,

        .read    = seq_read,

        .llseek  = seq_lseek,

        .release = seq_release

};

在scullseq_proc_open中会调用seq_open函数,如下,并关联打开相关的函数集scull_seq_ops。

static int scullseq_proc_open(struct inode *inode, struct file *file)

{

        return seq_open(file, &scull_seq_ops);

}

static struct seq_operations scull_seq_ops = {

        .start = scull_seq_start,

        .next  = scull_seq_next,

        .stop  = scull_seq_stop,

        .show  = scull_seq_show

};

然后seq_open在内核中如下:

/**

 *      seq_open -      initialize sequential file

 *      @file: file we initialize

 *      @op: method table describing the sequence

 *

 *      seq_open() sets @file, associating it with a sequence described

 *      by @op.  @op->start() sets the iterator up and returns the first

 *      element of sequence. @op->stop() shuts it down.  @op->next()

 *      returns the next element of sequence.  @op->show() prints element

 *      into the buffer.  In case of error ->start() and ->next() return

 *      ERR_PTR(error).  In the end of sequence they return %NULL. ->show()

 *      returns 0 in case of success and negative number in case of error.

 *      Returning SEQ_SKIP means "discard this element and move on".

 *      Note: seq_open() will allocate a struct seq_file and store its

 *      pointer in @file->private_data. This pointer should not be modified.

 */

int seq_open(struct file *file, const struct seq_operations *op)

3.   ioctl

ioctl是一个系统调用, 作用于一个文件描述符;接收一个确定要进行的命令的数字和(可选地)另一个参数,常常是一个指针。可以作为使用 /proc 文件系统的替代,可以实现几个用来调试用的 ioctl 命令. 这些命令可以从 驱动拷贝相关的数据结构到用户空间, 这里你可以检查它们.

使用 ioctl 来获取信息比使用/proc 困难,运行比读取/proc快,不要求划分数据为小于一页的片段。

另外,ioctl 命令可能不记录文档并不为人知。如果驱动发生了怪异的事情, 仍将在那里.缺点是模块可能会稍微大些.

4.   strace

strace其中最有用 的是 -t 来显示每个调用执行的时间, -T 来显示调用中花费的时间, -e 来限制被跟踪调 用的类型, 以及-o 来重定向输出到一个文件。

strace 从内核自身获取信息,所以不管它是否带有调试支持编译(对 gcc 是 -g 选项)以及不管它是否 strip 过,都可以跟踪。

5.   gdb调试内核

使用gdb方式,gdb 不能修改内核数据,也不可能设置断点或观察点, 或单步内核函数。

#gdb /usr/src/linux/vmlinux /proc/kcore

(gdb)p jiffies

也可添加模块的符号:

(gdb)add-symbol-file scull.ko 0xffffffffc0184000 -s .bss 0xffffffffc0188d40 -s .data 0xffffffffc0188000

然后可以打印驱动中相关参数:

(gdb) p scull_devices[0]

$1 = {data = 0x0 <irq_stack_union>, quantum = 4000, qset = 1000, size = 0, access_key = 0, sem = {lock = {raw_lock = {val = {

          counter = 0}}}, count = 1, wait_list = {next = 0xffff89b335861028, prev = 0xffff89b335861028}}, cdev = {kobj = {

      name = 0x0 <irq_stack_union>, entry = {next = 0xffff89b335861040, prev = 0xffff89b335861040}, parent = 0x0 <irq_stack_union>,

      kset = 0x0 <irq_stack_union>, ktype = 0xffffffffba661120, sd = 0x0 <irq_stack_union>, kref = {refcount = {refs = {counter = 1}}},

      state_initialized = 1, state_in_sysfs = 0, state_add_uevent_sent = 0, state_remove_uevent_sent = 0, uevent_suppress = 0},

    owner = 0xffffffffc0188a00, ops = 0xffffffffc0188000 <scull_fops>, list = {next = 0xffff89b335861088, prev = 0xffff89b335861088},

    dev = 260046848, count = 1}}

其中地址符号来自/sys/module/scull/sections/.data

/sys/module/scull/sections/.text

/sys/module/scull/sections/.bss

要设置断点可以使用kdb,不过是汇编级别的。要代码级别的可以使用kgdb,只是需要两台机器通过串口来进行调试。

 

相关实践学习
阿里云图数据库GDB入门与应用
图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。 GDB由阿里云自主研发,具备如下优势: 标准图查询语言:支持属性图,高度兼容Gremlin图查询语言。 高度优化的自研引擎:高度优化的自研图计算层和存储层,云盘多副本保障数据超高可靠,支持ACID事务。 服务高可用:支持高可用实例,节点故障迅速转移,保障业务连续性。 易运维:提供备份恢复、自动升级、监控告警、故障切换等丰富的运维功能,大幅降低运维成本。 产品主页:https://www.aliyun.com/product/gdb
目录
相关文章
|
3月前
|
NoSQL Linux C语言
Linux GDB 调试
Linux GDB 调试
59 10
|
3月前
|
Java Linux API
Linux设备驱动开发详解2
Linux设备驱动开发详解
43 6
|
3月前
|
消息中间件 算法 Unix
Linux设备驱动开发详解1
Linux设备驱动开发详解
47 5
|
3月前
|
NoSQL Linux C语言
嵌入式GDB调试Linux C程序或交叉编译(开发板)
【8月更文挑战第24天】本文档介绍了如何在嵌入式环境下使用GDB调试Linux C程序及进行交叉编译。调试步骤包括:编译程序时加入`-g`选项以生成调试信息;启动GDB并加载程序;设置断点;运行程序至断点;单步执行代码;查看变量值;继续执行或退出GDB。对于交叉编译,需安装对应架构的交叉编译工具链,配置编译环境,使用工具链编译程序,并将程序传输到开发板进行调试。过程中可能遇到工具链不匹配等问题,需针对性解决。
|
3月前
|
Ubuntu Linux
内核实验(四):Qemu调试Linux内核,实现NFS挂载
本文介绍了在Qemu虚拟机中配置NFS挂载的过程,包括服务端的NFS服务器安装、配置和启动,客户端的DHCP脚本添加和开机脚本修改,以及在Qemu中挂载NFS、测试连通性和解决挂载失败的方法。
160 0
内核实验(四):Qemu调试Linux内核,实现NFS挂载
|
3月前
|
Ubuntu NoSQL Linux
Linux内核和驱动
Linux内核和驱动
26 2
|
3月前
|
数据采集 Linux
Linux源码阅读笔记20-PCI设备驱动详解
Linux源码阅读笔记20-PCI设备驱动详解
|
4月前
|
存储 JSON Linux
|
2月前
|
Linux API
Linux里的高精度时间计时器(HPET)驱动 【ChatGPT】
Linux里的高精度时间计时器(HPET)驱动 【ChatGPT】
|
4月前
|
运维 Java Linux
(九)JVM成神路之性能调优、GC调试、各内存区、Linux参数大全及实用小技巧
本章节主要用于补齐之前GC篇章以及JVM运行时数据区的一些JVM参数,更多的作用也可以看作是JVM的参数列表大全。对于开发者而言,能够控制JVM的部分也就只有启动参数了,同时,对于JVM的性能调优而言,JVM的参数也是基础。
101 8