Logger详解(三)

四、日志记录读取过程分析
继续看 kernel/drivers/staging/Android/logger.c  文件,注册的读取日志设备文件的方法为 logger_read : 
/* 
 * logger_read - our log's read() method 
 * 
 * Behavior: 
 * 
 *  - O_NONBLOCK works 
 *  - If there are no log entries to read, blocks until log is written to 
 *  - Atomically reads exactly one log entry 
 * 
 * Optimal read size is LOGGER_ENTRY_MAX_LEN. Will set errno to EINVAL if read 
 * buffer is insufficient to hold next entry. 
 */  
static ssize_t logger_read(struct file *file, char __user *buf,  
               size_t count, loff_t *pos)  
{  
    struct logger_reader *reader =  file->private_data;  
    struct logger_log *log = reader->log;  
    ssize_t ret;  
    DEFINE_WAIT(wait);  
  
start:  
     while (1) {  
        prepare_to_wait(&log->wq, &wait, TASK_INTERRUPTIBLE);  
  
        mutex_lock(&log->mutex);  
        ret = ( log->w_off == reader->r_off);  
        mutex_unlock(&log->mutex);  
        if (!ret)  
             break;  
  
        if (file->f_flags & O_NONBLOCK) {  
            ret = -EAGAIN;  
             break;  
        }  
  
        if (signal_pending(current)) {  
            ret = -EINTR;  
             break;  
        }  
  
         schedule();  
    }  
  
    finish_wait(&log->wq, &wait);  
    if (ret)  
         return ret;  
  
    mutex_lock(&log->mutex);  
  
     /* is there still something to read or did we race? */  
    if (unlikely(log->w_off == reader->r_off)) {  
        mutex_unlock(&log->mutex);  
         goto start;  
    }  
  
     /* get the size of the next entry */  
    ret = get_entry_len(log,  reader->r_off);  
     if (count < ret) {  
        ret = -EINVAL;  
         goto out;  
    }  
  
     /* get exactly one entry from the log */  
    ret =  do_read_log_to_user(log, reader, buf, ret);  
  
out:  
    mutex_unlock(&log->mutex);  
  
     return ret;  
}  
 注意在函数开始的地方,表示读取日志上下文的结构体 logger_reader 是保存在文件指针的 private_data 成员变量里面的,这是在打开设备文件时设置的,设备文件打开方法为 logger_open
/* 
 * logger_open - the log's open() file operation 
 * 
 * Note how near a no-op this is in the write-only case. Keep it that way! 
 */  
static int logger_open(struct inode *inode, struct file *file)  
{  
    struct logger_log *log;  
     int ret;  
  
     ret = nonseekable_open(inode, file);  
     if (ret)  
         return ret;  
  
    log = get_log_from_minor(MINOR(inode->i_rdev));  
    if (!log)  
         return -ENODEV;  
  
     if (file->f_mode & FMODE_READ) {  
        struct logger_reader *reader;  
  
         reader = kmalloc(sizeof(struct logger_reader), GFP_KERNEL);  
        if (!reader)  
            return -ENOMEM;  
  
        reader->log = log;  
        INIT_LIST_HEAD(&reader->list);  
  
        mutex_lock(&log->mutex);  
         reader->r_off = log->head;  
        list_add_tail(&reader->list, &log->readers);  
        mutex_unlock(&log->mutex);  
  
         file->private_data = reader;  
    }  else  
        file->private_data = log;  
  
     return 0;  
}  
我们可以使用get_log_from_minor()函数通过inode->i_rdev所对应对应的MINOR ID来返回不同的Looger驱动的指针。
static struct  logger_log *get_log_from_minor(int minor)
{
         if (log_main.misc.minor == minor)
                 return &log_main;
         if (log_events.misc.minor == minor)
                return &log_events;
         if (log_radio.misc.minor == minor)
                 return &log_radio;
         if (log_system.misc.minor == minor)
                 return &log_system;
         return NULL;
}
    新打开日志设备文件时,是从 log->head 位置开始读取日志的,保存在 logger_reader 的成员变量 r_off 中。
start 标号处的 while 循环是在等待日志可读,如果已经没有新的日志可读了,那么就要读进程就要进入休眠状态,等待新的日志写入后再唤醒,这是通过 prepare_wait schedule 两个调用来实现的。如果没有新的日志可读,并且设备文件不是以非阻塞 O_NONBLOCK 的方式打开或者这时有信号要处理(signal_pending(current)),那么就直接返回,不再等待新的日志写入。判断当前是否有新的日志可读的方法是
        ret = (log->w_off == reader->r_off);
        即判断当前缓冲区的写入位置和当前读进程的读取位置是否相等,如果不相等,则说明有新的日志可读。
        继续向下看,如果有新的日志可读,那么就,首先通过 get_entry_len 来获取下一条可读的日志记录的长度,从这里可以看出,日志读取进程是以 日志记录为单位 进行读取的,一次只读取一条记录。 get_entry_len 的函数实现如下
/* 
 * get_entry_len - Grabs the length of the payload of the next entry starting 
 * from 'off'. 
 * 
 * Caller needs to hold log->mutex. 
 */  
static __u32 get_entry_len(struct logger_log *log, size_t off)  
{  
    __u16 val;  
  
     switch (log->size - off) {  
     case 1:  
        memcpy(&val, log->buffer + off, 1);  
        memcpy(((char *) &val) + 1, log->buffer, 1);  
         break;  
     default:  
        memcpy(&val, log->buffer + off, 2);  
    }  
  
     return sizeof(struct logger_entry) + val;  
}  
 如前文所述,每一条日志记录是由两大部分组成的,一个用于描述这条日志记录的结构体 logger_entry ,另一个是 记录内容本身即有效负载。 结构体 logger_entry 的长度是固定的,只要知道有效负载的长度,就可以知道整条日志记录的长度了而有效负载的长度是记录在结构体l ogger_entry 的成员变量 len 中,而len成员变量的地址与 logger_entry 的地址相同,因此,只需要读取记录的开始位置的两个字节就可以了。又由于日志记录缓冲区是循环使用的,这两个节字有可能是第一个字节存放在缓冲区最后一个字节,而第二个字节存放在缓冲区的第一个节,除此之外,这两个字节都是连在一起的。因此,分两种情况来考虑,对于前者, 分别通过读取缓冲区最后一个字节和第一个字节来得到日志记录的有效负载长度到本地变量val中,对于后者,直接读取连续两个字节的值到本地变量val中。这两种情况是通过判断日志缓冲区的大小和要读取的日志记录在缓冲区中的位置的差值来区别的,如果相差1,就说明是前一种情况了。 最后,把有效负载的长度 val 加上 logger_entry 的长度就得到了要读取的日志记录的总长度了
接着往下看,得到了要读取的记录的长度,就调用 do_read_log_to_user() 函数来执行真正的读取动作
static ssize_t do_read_log_to_user(struct logger_log *log,  
                   struct logger_reader *reader,  
                   char __user *buf,  
                   size_t count)  
{  
    size_t len;  
  
    /* 
     * We read from the log in two disjoint operations. First, we read from 
     * the current read head offset up to 'count' bytes or to the end of 
     * the log, whichever comes first. 
     */  
    len = min(count, log->size - reader->r_off);  
     if (copy_to_user(buf, log->buffer + reader->r_off, len))  
         return -EFAULT;  
  
    /* 
     * Second, we read any remaining bytes, starting back at the head of 
     * the log. 
     */  
     if (count != len)  
        if (copy_to_user(buf + len, log->buffer, count - len))  
            return -EFAULT;  
  
    reader->r_off = logger_offset(reader->r_off + count);  
  
     return count;  
这个函数简单地调用 copy_to_user函数来把位于内核空间的日志缓冲区指定的内容拷贝到用户空间的内存缓冲区就可以了,同时,把当前读取日志进程的上下文信息中的读偏移 r_off前进到下一条日志记录的开始的位置上。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值