本章主要分为四个部分:通过日志(打印)调试,通过proc(查询)调试,通过strace(监视)调试以及通过kgdb调试。kgdb简单看了下,没动手,感觉作用不是很大,如果以后有需要再回头看。先按章节来总结。
通过日志调试
p83页上开户/关闭调试信息的宏的技巧不错,加至了实验代码。 这节可以总结的东西很少,不过,还是遗留下了一个问题——klogd无法安装。由于起初想把debug级的信息也能记录在messages文件中(也只有debug这一级不能记录),但似乎必须在syslogd和klogd同时运行时才可以,关于书中说的echo 8>/proc/sys/kernel/printk或者rsyslog -c 8都无法解决问题,而在安装klogd的RPM包却提示:failed dependencies: rsyslogd conflicts with klogd,怀疑可能还是版本问题,不过花了不少时间,不想再深究,就把这个问题搁浅了。
通过proc调试
对proc文件系统的理解
/proc文件系统是内核用来向进程发送信息的接口,是个虚拟在内存中的文件系统,“存储的是当前内核运行状态的一系列特殊文件,用户可以通过这些文件查看有关系统硬件及当前正在运行进程的信息,甚至可以通过更改其中某些文件来改变内核的运行状态”。其中比较重要的有:在/proc目录下有一系列数字编号的文件。每个实际上都是一个目录,表示系统中的一个进程。而在这些进程目录下,又有fd目录,status文件等重要的用于查询进程状态信息。具体的可以参考:深入理解linux系统下proc文件系统内容。
创建proc文件
通过create_proc_read_entry创建proc文件,其中有个参数为与该文件绑写的callback函数,这个callback函数负责完成读该proc文件需显示的内容。该callback的原型为:
int read_callback(char *buf, char **start, off_t offset, int count, int *eof, void *data);
虽然在实验中已经按书中的代码实现proc功能,但对此接口的几个接口还是很不理解,尤其对start和offset究竟有什么作用以及87页最后一段,作者谈到的连续read调用会出现ASCII变化的例子,搞不懂,为什么会出现这种问题?为什么把*start设为一个小整数值就可以了?想过一段时间,想不明白,网上关于Proc的文章大多关于proc文件系统的理解,没有找到关于这方面的文章,当然这个问题可能也不是很重要,就pass了。
seq_file接口
感觉seq_file与proc的区别只是在实现上,在面对用户时,基本是相同的,当然因为seq_file使用file_operations结构,它能提供的功能肯定会比proc更多。在实现过程中,seq_file按书所说的,有个重要的迭代器对象——void *v, 即由start创建,为next, stop, show所使用的一个指针,但可惜的是书中的例程压根没用它,对于这个代替器究竟是何种用处,我没有深究。总之,seq_file作为proc的升级版,其原理与seq_file是相同的,只是实现上有点小差异。
通过监视调试
这里主要谈到两个重要的工具吧,strace和oops,其实在内核出现错误时,都会调用这两者打印信息到messages中,只需要看懂他们的调试信息即可。需要注意的是strace给出的系统调用信息是最近的系统调用,而不是函数的调用关系。
内存模型
书中在此处谈到了内存模型,提到0xc000 0000以上的线性空间是内核栈空间,以下的是用户栈空间,起初不理解,虽然知道linux32位的线性空间分配1g给内核,3g给用户,但一直以为是开头的1g空间是内核区,之后的3g是用户区,因为开头的地址中存放了很多重要的内核数据,但实际上,这个是地址是物理地址而不是线性地址,这些存放在物理地址开头的内核数据也都被映射到了线性地址空间中高1g地址空间中(0xc000 0000 ~ 0xffff ffff)。另外,线性地址空间中从0x0000 0000 至0x0804 8000被称之为保留区域,这段保留区域有何作用还不清楚。至0x0804 8000到0xc000 0000这段近3g的空间才是用户空间,其中包括从0x0804 8000至0x4000 0000近1g的空间主要存放程序的数据段和bss段,之后为用户堆(小堆,大堆可能分配至文件映射区)的空间。0x4000 0000至0xc000 0000文件映射区。0xc000 0000即为内核空间,内核栈和内核堆均分配于此。内存模型大致情况就是如此,这里也主要是复述大内高手—内存模型这篇文章。PS:原文写的非常好。
目前的实验代码:
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/semaphore.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/major.h>
#include <asm/uaccess.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#undef PDEBUG
#ifdef SCULL_DEBUG
# ifdef __KERNEL__
# define PDEBUG(fmt, args...) printk( KERN_INFO "scull: "fmt, ##args)
# else
# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ##args)
# endif
#else
# define PDEBUG(fmt, args...)
#endif
#define SCULL_DEV_CNT 4
#define SCULL_QUANTUM 256
#define SCULL_QSET 16
struct scull_qset{
void **data;
struct scull_qset *next;
};
struct scull_dev{
struct scull_qset *data;
int quantum;
int qset;
unsigned long size;
unsigned int access_key;
struct semaphore sem;
struct cdev cdev;
};
static int scull_major, scull_minor;
static struct scull_dev my_scull_dev[SCULL_DEV_CNT];
module_param( scull_major, int, S_IRUGO );
module_param( scull_minor, int, S_IRUGO );
static void
scull_trim( struct scull_dev *dev )
{
struct scull_qset *next, *dptr;
int i, qset = dev->qset;
PDEBUG("prepare scull_trim!\n");
for (dptr=dev->data; dptr; dptr=next){
if (dptr->data) {
for (i=0; i<qset; ++i)
kfree(dptr->data[i]);
kfree(dptr->data);
dptr->data = NULL;
}
next = dptr->next;
kfree(dptr);
}
dev->size = 0;
dev->quantum = SCULL_QUANTUM;
dev->qset = SCULL_QSET;
dev->data = NULL;
}
static int
scull_open(struct inode *inode, struct file *filp)
{
struct scull_dev *dev;
dev = container_of(inode->i_cdev, struct scull_dev, cdev);
filp->private_data = dev;
if ((filp->f_flags & O_ACCMODE) == O_WRONLY)
scull_trim(dev);
PDEBUG("get out of open\n");
return 0;
}
static int
scull_release(struct inode *inode, struct file *filp)
{
return 0;
}
static struct scull_qset *
scull_follow(struct scull_dev *dev, long index )
{
struct scull_qset *p;
for(p=dev->data; index; p=p->next, --index);
return p;
}
static void
scull_insertnode( struct scull_dev *dev, long index, struct scull_qset *new )
{
struct scull_qset *p;
if (dev->data == NULL){
dev->data = new;
return;
}
for(p=dev->data; index; p=p->next, --index);
p->next = new;
}
static ssize_t
scull_read( struct file *filp, char __user *buf, size_t count, loff_t *f_pos )
{
struct scull_dev *dev = filp->private_data;
struct scull_qset *dptr;
int quantum = dev->quantum, qset = dev->qset;
int itemsize = quantum * qset;
int item, s_pos, q_pos, rest;
ssize_t retval = 0;
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
if (*f_pos >= dev->size)
goto scull_read_out;
if (*f_pos + count > dev->size)
count = dev->size - *f_pos;
item = (long)*f_pos / itemsize;
rest = (long)*f_pos % itemsize;
s_pos = rest / quantum;
q_pos = rest % quantum;
dptr = scull_follow(dev, item);
if (dptr == NULL || !dptr->data || !dptr->data[s_pos])
goto scull_read_out;
if (count > quantum - q_pos)
count = quantum - q_pos;
if (copy_to_user(buf, dptr->data[s_pos] + q_pos, count)) {
retval = -EFAULT;
goto scull_read_out;
}
*f_pos += count;
retval = count;
scull_read_out:
up(&dev->sem);
return retval;
}
static ssize_t
scull_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
struct scull_dev *dev = filp->private_data;
struct scull_qset *dptr;
int quantum = dev->quantum, qset = dev->qset;
int itemsize = quantum *qset;
int item, s_pos, q_pos, rest;
ssize_t retval = -ENOMEM;
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
item = (long)*f_pos / itemsize;
rest = (long)*f_pos % itemsize;
s_pos = rest / quantum;
q_pos = rest % quantum;
dptr = scull_follow(dev, item);
if (dptr == NULL){
if ( (dptr = kmalloc(sizeof(struct scull_qset), GFP_KERNEL)) == NULL)
goto scull_write_out;
memset(dptr, 0, sizeof(struct scull_qset));
scull_insertnode(dev, item, dptr);
}
if (!dptr->data){
dptr->data = kmalloc(qset * sizeof(char *), GFP_KERNEL);
if (!dptr->data)
goto scull_write_out;
memset(dptr->data, 0, qset * sizeof(char *));
}
if (!dptr->data[s_pos]) {
dptr->data[s_pos] = kmalloc(SCULL_QUANTUM, GFP_KERNEL);
if (!dptr->data[s_pos])
goto scull_write_out;
memset(dptr->data[s_pos], 0, SCULL_QUANTUM);
}
if (count > quantum - q_pos)
count = quantum - q_pos;
if (copy_from_user(dptr->data[s_pos]+q_pos, buf, count)){
retval = -EFAULT;
goto scull_write_out;
}
PDEBUG("write though\n");
*f_pos += count;
retval = count;
if (dev->size < *f_pos)
dev->size = *f_pos;
scull_write_out:
up(&dev->sem);
return retval;
}
static struct file_operations scull_fops = {
.owner = THIS_MODULE,
/*
.llseek = scull_llseek,
.ioctl = scull_ioctl,
*/
.read = scull_read,
.write = scull_write,
.open = scull_open,
.release = scull_release,
};
static void
scull_setup_cdev( struct scull_dev *dev, int index )
{
int ret;
dev_t devno = MKDEV(scull_major, scull_minor + index);
cdev_init(&dev->cdev, &scull_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &scull_fops;
dev->quantum = SCULL_QUANTUM;
dev->qset = SCULL_QSET;
init_MUTEX(&dev->sem);
ret = cdev_add(&dev->cdev, devno, 1);
if (ret)
printk(KERN_ALERT "Error %d adding scull%d\n", ret, index);
else
PDEBUG("Success added scull%d!\n", index);
}
static void *
scull_seq_start(struct seq_file *s, loff_t *pos)
{
return *pos >= SCULL_DEV_CNT ? NULL: my_scull_dev+*pos;
}
static void *
scull_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
(*pos)++;
return *pos >= SCULL_DEV_CNT ? NULL: my_scull_dev+*pos;
}
static int
scull_seq_show(struct seq_file *s, void *v)
{
struct scull_dev *dev = (struct scull_dev *)v;
struct scull_qset *d;
int i;
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
seq_printf(s, "\nDevice%d: qset %d, q %d, sz %ld\n", \
(int) (dev - my_scull_dev) / sizeof(struct scull_dev), \
dev->qset, dev->quantum, dev->size);
for (d=dev->data; d; d=d->next) {
seq_printf(s, "item at %p, qset at %p\n", d, d->data);
if (d->data && !d->next)
for (i=0; i<dev->qset; i++) {
if (d->data[i])
seq_printf(s, "%4d:%8p\n", i, d->data[i]);
}
}
up(&dev->sem);
return 0;
}
static void scull_seq_stop(struct seq_file *s, void *v)
{
return;
}
static struct seq_operations scull_seq_ops = {
.start = scull_seq_start,
.next = scull_seq_next,
.show = scull_seq_show,
.stop = scull_seq_stop
};
static int
scull_proc_open(struct inode *inode, struct file *file)
{
return seq_open(file, &scull_seq_ops);
}
static struct file_operations scull_proc_ops = {
.owner = THIS_MODULE,
.open = scull_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release
};
static int
scull_read_procmem(char *buf, char **start, off_t offset, int count, int *eof, void *data )
{
int i, j, len = 0;
int limit = count - 80;
for (i=0; i<SCULL_DEV_CNT && len<=limit; i++) {
struct scull_dev *d = &my_scull_dev[i];
struct scull_qset *qs = d->data;
if (down_interruptible(&d->sem))
return -ERESTARTSYS;
len += sprintf(buf+len, "\nDevice%d: qset %d, q %d, sz %ld\n", \
i, d->qset, d->quantum, d->size);
for (; qs && len<=limit; qs=qs->next) {
len += sprintf(buf+len, "item at %p, qset at %p\n", qs, qs->data);
if (qs->data && !qs->next)
for (j=0; j<d->qset; j++) {
if (qs->data[j])
len += sprintf(buf+len, "%4d:%8p\n", j, qs->data[j]);
}
}
up(&d->sem);
}
*eof = 1;
return len;
}
static int
scull_init( void )
{
int err, i;
dev_t devno;
struct proc_dir_entry *entry;
if (scull_major) {
devno = MKDEV(scull_major, scull_minor);
err = register_chrdev_region(devno, SCULL_DEV_CNT, "scull");
} else {
err = alloc_chrdev_region( &devno, 0, SCULL_DEV_CNT, "scull");
scull_major = MAJOR(devno);
scull_minor = MINOR(devno);
}
if (err < 0) {
printk(KERN_WARNING "scull:can't get major %d\n", scull_major);
return err;
}
if (create_proc_read_entry("scullmem", 0 ,NULL, scull_read_procmem, NULL) == NULL)
printk(KERN_ERR "create proc scullmem fail!\n");
if ((entry = create_proc_entry("scullseq", 0, NULL)))
entry->proc_fops = &scull_proc_ops;
for(i=0; i<SCULL_DEV_CNT; ++i)
scull_setup_cdev(&my_scull_dev[i], i);
return 0;
}
static void
scull_exit( void )
{
int i;
dev_t devno = MKDEV(scull_major, scull_minor);
remove_proc_entry("scullmem", NULL);
remove_proc_entry("scullseq", NULL);
for(i=0; i<SCULL_DEV_CNT; ++i){
cdev_del(&(my_scull_dev[i].cdev));
scull_trim(&my_scull_dev[i]);
}
unregister_chrdev_region(devno, SCULL_DEV_CNT);
printk(KERN_INFO "scull exit!\n" );
}
MODULE_LICENSE("GPL");
module_init(scull_init);
module_exit(scull_exit);