android log机制

1、linux内核的log输出
   在标准的linux内核开发过程中,使用 printk ,这是一个与printf输出打印齐名的函数,同样提供格式化输出功能,只是其有
   打印级别且将信息保存到 /proc/kmsg 日志中,使用cat命令查看其信息[cat  /proc/kmsg]
   
[cpp]  view plain  copy
  1. <span style="font-size:14px;color:#003333;">#define KERN_EMERG  "<0>"     /* system is unusable           */    
  2. #define KERN_ALERT  "<1>"     /* action must be taken immediately */    
  3. #define KERN_CRIT   "<2>"     /* critical conditions          */    
  4. #deinfe KERN_ERR    "<3>"     /* error conditions         */    
  5. #deinfe KERN_WARNING    "<4>"     /* warning conditions           */    
  6. #deinfe KERN_NOTICE "<5>"     /* normal but significant condition */    
  7. #deinfe KERN_INFO   "<6>"     /* informational            */    
  8. #deinfe KERN_DEBUG  "<7>"     /* debug-level messages         */  </span>  
2、android中log输出
Android系统在用户空间中提供了轻量级的logger日志系统,它是在内核中实现的一种设备驱动,与用户空间的logcat工具配合使用能够方便地跟踪调试程序。
Android系统中的C/C++日志接口是通过宏来使用的。在system/core/include/android/log.h定义了日志的级别:
/*
* Android log priority values, in ascending priority order.
*/
typedef enum android_LogPriority {
   ANDROID_LOG_UNKNOWN = 0,
   ANDROID_LOG_DEFAULT,    /* only for SetMinPriority() */
   ANDROID_LOG_VERBOSE,
   ANDROID_LOG_DEBUG,
   ANDROID_LOG_INFO,
   ANDROID_LOG_WARN,
   ANDROID_LOG_ERROR,
   ANDROID_LOG_FATAL,
   ANDROID_LOG_SILENT,     /* only for SetMinPriority(); must be last */
} android_LogPriority;

为了使用方便,在system/core/include/cutils/log.h定义了相对应的宏:
#define LOGV(...) ((void)LOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
#define LOGD(...) ((void)LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
#define LOGI(...) ((void)LOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
#define LOGW(...) ((void)LOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
#define LOGE(...) ((void)LOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))

因为如果需要使用log输出,包含其头文件:#include <cutils/log.h> 并link其动态库:liblog.so 即可
#define LOG_TAG "XX_LOG_TAG" // 这里可以定义其输出的TAG
    #include <cutils/log.h>
   
   JAVA层打印:
   import android.util.Log;

   private static final String TAG = "XX_LOG_TAG";

   Log.e(TAG, "This is the error log printed by Log.i in android user space.");

内核代码路径:
kernel/drivers/staging/android/logger.h
kernel/drivers/staging/android/logger.c
1、Logger驱动程序的相关数据结构
首先来看logger.h头文件的内容:

[cpp]  view plain  copy
  1. /* include/linux/logger.h 
  2.  * 
  3.  * Copyright (C) 2007-2008 Google, Inc. 
  4.  * Author: Robert Love <rlove@android.com> 
  5.  * 
  6.  * This software is licensed under the terms of the GNU General Public 
  7.  * License version 2, as published by the Free Software Foundation, and 
  8.  * may be copied, distributed, and modified under those terms. 
  9.  * 
  10.  * This program is distributed in the hope that it will be useful, 
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of 
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  13.  * GNU General Public License for more details. 
  14.  * 
  15.  */  
  16.   
  17. #ifndef _LINUX_LOGGER_H  
  18. #define _LINUX_LOGGER_H  
  19.   
  20. #include <linux/types.h>  
  21. #include <linux/ioctl.h>  
  22.   
  23. struct logger_entry {  
  24.     __u16       len;    /* length of the payload */  
  25.     __u16       __pad;  /* no matter what, we get 2 bytes of padding */  
  26.     __s32       pid;    /* generating process's pid */  
  27.     __s32       tid;    /* generating process's tid */  
  28.     __s32       sec;    /* seconds since Epoch */  
  29.     __s32       nsec;   /* nanoseconds */  
  30.     char        msg[0]; /* the entry's payload */  
  31. };  
  32.   
  33. #define LOGGER_LOG_RADIO    "log_radio" /* radio-related messages */  
  34. #define LOGGER_LOG_EVENTS   "log_events"    /* system/hardware events */  
  35. #define LOGGER_LOG_SYSTEM   "log_system"    /* system/framework messages */  
  36. #define LOGGER_LOG_MAIN     "log_main"  /* everything else */  
  37.   
  38. #define LOGGER_ENTRY_MAX_LEN        (4*1024)  
  39. #define LOGGER_ENTRY_MAX_PAYLOAD    \  
  40.     (LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry))  
  41.   
  42. #define __LOGGERIO  0xAE  
  43.   
  44. #define LOGGER_GET_LOG_BUF_SIZE     _IO(__LOGGERIO, 1) /* size of log */  
  45. #define LOGGER_GET_LOG_LEN      _IO(__LOGGERIO, 2) /* used log len */  
  46. #define LOGGER_GET_NEXT_ENTRY_LEN   _IO(__LOGGERIO, 3) /* next entry len */  
  47. #define LOGGER_FLUSH_LOG        _IO(__LOGGERIO, 4) /* flush log */  
  48.   
  49. #endif /* _LINUX_LOGGER_H */  
struct logger_entry是一个用于描述一条Log记录的结构体。
其中len成员变量记录了这条记录的有效负载的长度,有效负载指定的日志记录本身的长度,但是不包括用于描述这个记录的struct logger_entry结构体。
从struct logger_entry中也可以看出:优先级别Priority、Tag字符串以及Msg字符串,pid和tid成员变量分别用来记录是哪条进程写入了这条记录。sec和nsec成员变量记录日志写的时间。msg成员变量记录的就有效负载的内容了,它的大小由len成员变量来确定

#define LOGGER_ENTRY_MAX_LEN(4*1024)
#define LOGGER_ENTRY_MAX_PAYLOAD \
(LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry))

这两个宏定义记录了 最大有效负载长度。

再分析下logger.c实现文件:

[cpp]  view plain  copy
  1. /* 
  2.  * struct logger_log - represents a specific log, such as 'main' or 'radio' 
  3.  * 
  4.  * This structure lives from module insertion until module removal, so it does 
  5.  * not need additional reference counting. The structure is protected by the 
  6.  * mutex 'mutex'. 
  7.  */  
  8. struct logger_log {  
  9.     unsigned char       *buffer;/* the ring buffer itself */  
  10.     struct miscdevice   misc;   /* misc device representing the log */  
  11.     wait_queue_head_t   wq; /* wait queue for readers */  
  12.     struct list_head    readers; /* this log's readers */  
  13.     struct mutex        mutex;  /* mutex protecting buffer */  
  14.     size_t          w_off;  /* current write head offset */  
  15.     size_t          head;   /* new readers start here */  
  16.     size_t          size;   /* size of the log */  
  17. };  
  结构体struct logger_log就是真正用来保存日志的地方了。buffer成员变量变是用保存日志信息的内存缓冲区,它的大小由size成员变量确定。

 buffer是一个循环使用的环形缓冲区,缓冲区中保存的内容是以struct logger_entry为单位的,其组成方式是:

struct logger_entry | priority | tag | msg

/*

[cpp]  view plain  copy
  1. struct logger_reader - a logging device open for reading  
  2. *  
  3. * This object lives from open to release, so we don't need additional  
  4. * reference counting. The structure is protected by log->mutex.  
  5. */  
  6. truct logger_reader {  
  7. struct logger_log   *log;   /* associated log */  
  8. struct list_head    list;   /* entry in logger_log's list */  
  9. size_t          r_off;  /* current read head offset */  
  10. ;  
 结构体struct logger_reader用来表示一个读取日志的进程,log成员变量指向要读取的日志缓冲区。list成员变量用来连接其它读者进程。r_off成员变量表示当前要读取的日志在缓冲区中的位置。


2、模块初始化过程:

logger是一个misc设备,那么misc设备是个什么东东呢?网上有很多资料,这里简要说明一下:

杂设备——misc

简单的说,杂设备就是内核自动帮你分配设备号并且自动创建设备文件。

1、自动分配设备号,是指所有注册为杂设备的设备的主设备号为10,而次设备号内核自动分配。

2、自动创建设备文件是指,内核会使用udev(前提是你已经移植udev),动态创建设备节点。


利用:

int misc_register(struct miscdevice * misc); //注册

int misc_deregister(struct miscdevice *misc); //注销


执行cat /proc/devices命令可以查看此类设备

# cat /proc/devices

Character devices:

1 mem

4 /dev/vc/0

4 tty

5 /dev/tty

5 /dev/console

5 /dev/ptmx

10 misc //创建的设备在misc


在logger这里定义了三个日志设备:

[cpp]  view plain  copy
  1. /* 
  2.  * Defines a log structure with name 'NAME' and a size of 'SIZE' bytes, which 
  3.  * must be a power of two, greater than LOGGER_ENTRY_MAX_LEN, and less than 
  4.  * LONG_MAX minus LOGGER_ENTRY_MAX_LEN. 
  5.  */  
  6. #define DEFINE_LOGGER_DEVICE(VAR, NAME, SIZE) \  
  7. static unsigned char _buf_ ## VAR[SIZE]; \  
  8. static struct logger_log VAR = { \  
  9.     .buffer = _buf_ ## VAR, \  
  10.     .misc = { \  
  11.         .minor = MISC_DYNAMIC_MINOR, \  
  12.         .name = NAME, \  
  13.         .fops = &logger_fops, \  
  14.         .parent = NULL, \  
  15.     }, \  
  16.     .wq = __WAIT_QUEUE_HEAD_INITIALIZER(VAR .wq), \  
  17.     .readers = LIST_HEAD_INIT(VAR .readers), \  
  18.     .mutex = __MUTEX_INITIALIZER(VAR .mutex), \  
  19.     .w_off = 0, \  
  20.     .head = 0, \  
  21.     .size = SIZE, \  
  22. };  
  23.   
  24. DEFINE_LOGGER_DEVICE(log_main, LOGGER_LOG_MAIN, 64*1024)  
  25. DEFINE_LOGGER_DEVICE(log_events, LOGGER_LOG_EVENTS, 256*1024)  
  26. DEFINE_LOGGER_DEVICE(log_radio, LOGGER_LOG_RADIO, 64*1024)  
  27. DEFINE_LOGGER_DEVICE(log_system, LOGGER_LOG_SYSTEM, 64*1024)  
分别是log_main、log_events和log_radio,名称分别LOGGER_LOG_MAIN、LOGGER_LOG_EVENTS和LOGGER_LOG_RADIO

这三个不同名称的设备文件操作方法如下:

[cpp]  view plain  copy
  1. static const struct file_operations logger_fops = {  
  2.     .owner = THIS_MODULE,  
  3.     .read = logger_read,  
  4.     .aio_write = logger_aio_write,  
  5.     .poll = logger_poll,  
  6.     .unlocked_ioctl = logger_ioctl,  
  7.     .compat_ioctl = logger_ioctl,  
  8.     .open = logger_open,  
  9.     .release = logger_release,  
  10. };  

日志驱动程序模块的初始化函数为logger_init:
[cpp]  view plain  copy
  1. static int __init init_log(struct logger_log *log)  
  2. {  
  3.     int ret;  
  4.   
  5.     ret = misc_register(&log->misc);  
  6.     if (unlikely(ret)) {  
  7.         printk(KERN_ERR "logger: failed to register misc "  
  8.                "device for log '%s'!\n", log->misc.name);  
  9.         return ret;  
  10.     }  
  11.   
  12.     printk(KERN_INFO "logger: created %luK log '%s'\n",  
  13.            (unsigned long) log->size >> 10, log->misc.name);  
  14.   
  15.     return 0;  
  16. }  
  17.   
  18. static int __init logger_init(void)  
  19. {  
  20.     int ret;  
  21.   
  22.     ret = init_log(&log_main);  
  23.     if (unlikely(ret))  
  24.         goto out;  
  25.   
  26.     ret = init_log(&log_events);  
  27.     if (unlikely(ret))  
  28.         goto out;  
  29.   
  30.     ret = init_log(&log_radio);  
  31.     if (unlikely(ret))  
  32.         goto out;  
  33.   
  34.     ret = init_log(&log_system);  
  35.     if (unlikely(ret))  
  36.         goto out;  
  37.   
  38. out:  
  39.     return ret;  
  40. }  
  41. device_initcall(logger_init);  
logger_init函数通过调用init_log函数来初始化了上述提到的三个日志设备,而init_log函数主要调用了misc_register函数来注册misc设备。

3、日志写入重要过程分析:

注册的写入日志设备文件的方法为logger_aio_write

[cpp]  view plain  copy
  1. <span style="color:#333333;">/* 
  2.  * logger_aio_write - our write method, implementing support for write(), 
  3.  * writev(), and aio_write(). Writes are our fast path, and we try to optimize 
  4.  * them above all else. 
  5.  */  
  6. ssize_t logger_aio_write(struct kiocb *iocb, const struct iovec *iov,  
  7.              unsigned long nr_segs, loff_t ppos)  
  8. {  
  9.     struct logger_log *log = file_get_log(iocb->ki_filp);  
  10.     size_t orig = log->w_off;  
  11.     </span><span style="color:#cc0000;">struct logger_entry header</span><span style="color:#333333;">;  
  12.     struct timespec now;  
  13.     ssize_t ret = 0;  
  14. </span><span style="color:#ff6600;"><span style="white-space: pre;">    </span>// 下面重点构造 <span style="background-color: rgb(230, 230, 230); font-family: Arial;">struct logger_entry header 结构体</span></span><span style="color:#333333;">  
  15.     now = current_kernel_time();  
  16.   
  17.     header.pid = current->tgid;  
  18.     header.tid = current->pid;  
  19.     header.sec = now.tv_sec;  
  20.     header.nsec = now.tv_nsec;  
  21.     header.len = min_t(size_t, iocb->ki_left, LOGGER_ENTRY_MAX_PAYLOAD);  
  22.   
  23.     /* null writes succeed, return zero */  
  24.     if (unlikely(!header.len))  
  25.         return 0;  
  26.   
  27.     mutex_lock(&log->mutex);  
  28.   
  29.     /* 
  30.      * Fix up any readers, pulling them forward to the first readable 
  31.      * entry after (what will be) the new write offset. We do this now 
  32.      * because if we partially fail, we can end up with clobbered log 
  33.      * entries that encroach on readable buffer. 
  34.      */  
  35.     fix_up_readers(log, sizeof(struct logger_entry) + header.len);  
  36.   
  37.     do_write_log(log, &header, sizeof(struct logger_entry));  
  38.   
  39.     while (nr_segs-- > 0) {  
  40.         size_t len;  
  41.         ssize_t nr;  
  42.   
  43.         /* figure out how much of this vector we can keep */  
  44.         len = min_t(size_t, iov->iov_len, header.len - ret);  
  45.   
  46.         /* write out this segment's payload */  
  47.         nr = do_write_log_from_user(log, iov->iov_base, len);  
  48.         if (unlikely(nr < 0)) {  
  49.             log->w_off = orig;  
  50.             mutex_unlock(&log->mutex);  
  51.             return nr;  
  52.         }  
  53.   
  54.         iov++;  
  55.         ret += nr;  
  56.     }  
  57.   
  58.     mutex_unlock(&log->mutex);  
  59.   
  60.     /* wake up any blocked readers */  
  61.     wake_up_interruptible(&log->wq);  
  62.   
  63.     return ret;  
  64. }</span>  
首先利用内核信息构造header信息,然后写入:do_write_log(log, &header, sizeof(struct logger_entry));

然后根据nr_segs数目,通过一个while循环把iov的内容写入到日志缓冲区中,也就是日志的优先级别priority、日志Tag和日志主体Msg。

最后一个重要函数说明:

[cpp]  view plain  copy
  1.     /* 
  2.      * Fix up any readers, pulling them forward to the first readable 
  3.      * entry after (what will be) the new write offset. We do this now 
  4.      * because if we partially fail, we can end up with clobbered log 
  5.      * entries that encroach on readable buffer. 
  6.      */  
  7.     fix_up_readers(log, sizeof(struct logger_entry) + header.len);  
  8.   
  9. /* 
  10.  * fix_up_readers - walk the list of all readers and "fix up" any who were 
  11.  * lapped by the writer; also do the same for the default "start head". 
  12.  * We do this by "pulling forward" the readers and start head to the first 
  13.  * entry after the new write head. 
  14.  * 
  15.  * The caller needs to hold log->mutex. 
  16.  */  
  17. static void fix_up_readers(struct logger_log *log, size_t len)  
  18. {  
  19.     size_t old = log->w_off;  
  20.     size_t new = logger_offset(old + len);  
  21.     struct logger_reader *reader;  
  22.   
  23.     if (clock_interval(old, new, log->head))  
  24.         log->head = get_next_entry(log, log->head, len);  
  25.   
  26.     list_for_each_entry(reader, &log->readers, list)  
  27.         if (clock_interval(old, new, reader->r_off))  
  28.             reader->r_off = get_next_entry(log, reader->r_off, len);  
  29. }  
为何需要这么一个函数呢?

由于日志缓冲区是循环使用的,即旧的日志记录如果没有及时读取,而缓冲区的内容又已经用完时,就需要覆盖旧的记录来容纳新的记录。而这部分将要被覆盖的内容,有可能是某些reader的下一次要读取的日志所在的位置,以及为新的reader准备的日志开始读取位置head所在的位置。因此,需要调整这些位置,使它们能够指向一个新的有效的位置。

4、日志读取重要过程分析:

注册的读取日志设备文件的方法为logger_read

[cpp]  view plain  copy
  1. /* 
  2.  * logger_read - our log's read() method 
  3.  * 
  4.  * Behavior: 
  5.  * 
  6.  *  - O_NONBLOCK works 
  7.  *  - If there are no log entries to read, blocks until log is written to 
  8.  *  - Atomically reads exactly one log entry 
  9.  * 
  10.  * Optimal read size is LOGGER_ENTRY_MAX_LEN. Will set errno to EINVAL if read 
  11.  * buffer is insufficient to hold next entry. 
  12.  */  
  13. static ssize_t logger_read(struct file *file, char __user *buf,  
  14.                size_t count, loff_t *pos)  
  15. {  
  16.     struct logger_reader *reader = file->private_data;  
  17.     struct logger_log *log = reader->log;  
  18.     ssize_t ret;  
  19.     DEFINE_WAIT(wait);  
  20.   
  21. start:  
  22.     while (1) {  
  23.         prepare_to_wait(&log->wq, &wait, TASK_INTERRUPTIBLE);  
  24.   
  25.         mutex_lock(&log->mutex);  
  26.         ret = (log->w_off == reader->r_off);  
  27.         mutex_unlock(&log->mutex);  
  28.         if (!ret)  
  29.             break;  
  30.   
  31.         if (file->f_flags & O_NONBLOCK) {  
  32.             ret = -EAGAIN;  
  33.             break;  
  34.         }  
  35.   
  36.         if (signal_pending(current)) {  
  37.             ret = -EINTR;  
  38.             break;  
  39.         }  
  40.   
  41.         schedule();  
  42.     }  
  43.   
  44.     finish_wait(&log->wq, &wait);  
  45.     if (ret)  
  46.         return ret;  
  47.   
  48.     mutex_lock(&log->mutex);  
  49.   
  50.     /* is there still something to read or did we race? */  
  51.     if (unlikely(log->w_off == reader->r_off)) {  
  52.         mutex_unlock(&log->mutex);  
  53.         goto start;  
  54.     }  
  55.   
  56.     /* get the size of the next entry */  
  57.     ret = get_entry_len(log, reader->r_off);  
  58.     if (count < ret) {  
  59.         ret = -EINVAL;  
  60.         goto out;  
  61.     }  
  62.   
  63.     /* get exactly one entry from the log */  
  64.     ret = do_read_log_to_user(log, reader, buf, ret);  
  65.   
  66. out:  
  67.     mutex_unlock(&log->mutex);  
  68.   
  69.     return ret;  
  70. }  
struct logger_reader *reader = file->private_data; 在这里直接使用  file->private_data是因为在device open时将private_data赋值为reader,在文件操作方法 logger_open 中:
[cpp]  view plain  copy
  1. <span style="color:#333333;">/* 
  2.  * logger_open - the log's open() file operation 
  3.  * 
  4.  * Note how near a no-op this is in the write-only case. Keep it that way! 
  5.  */  
  6. static int logger_open(struct inode *inode, struct file *file)  
  7. {  
  8.     struct logger_log *log;  
  9.     int ret;  
  10.   
  11.     ret = nonseekable_open(inode, file);  
  12.     if (ret)  
  13.         return ret;  
  14.   
  15.     log = get_log_from_minor(MINOR(inode->i_rdev));  
  16.     if (!log)  
  17.         return -ENODEV;  
  18.   
  19.     if (file->f_mode & FMODE_READ) {  
  20.         struct logger_reader *reader;  
  21.   
  22.         reader = kmalloc(sizeof(struct logger_reader), GFP_KERNEL);  
  23.         if (!reader)  
  24.             return -ENOMEM;  
  25.   
  26.         reader->log = log;  
  27.         INIT_LIST_HEAD(&reader->list);  
  28.   
  29.         mutex_lock(&log->mutex);  
  30.         reader->r_off = log->head; </span><span style="color:#ff0000;">// 从<span style="font-family: Arial; line-height: 26px;">log->head位置开始读取日志的,保存在struct logger_reader的成员变量r_off中</span></span><span style="color:#333333;">  
  31.         list_add_tail(&reader->list, &log->readers);  
  32.         mutex_unlock(&log->mutex);  
  33.   
  34.         </span><span style="color:#ff0000;">file->private_data = reader;  // 这里对</span><span style="color: rgb(255, 0, 0); background-color: rgb(230, 230, 230); font-family: Arial;">private_data进行了赋值</span><span style="color:#ff0000;">  
  35. </span><span style="color:#333333;">  
  36.     } else  
  37.         file->private_data = log;  
  38.   
  39.     return 0;  
  40. }</span>  
首先在 while (1)循环中判定是否有日志可读,判定语句如下:

ret = (log->w_off == reader->r_off);

即判断当前缓冲区的写入位置和当前读进程的读取位置是否相等,如果不相等,则说明有新的日志可读。

首先通过get_entry_len获取下一条可读的日志记录的长度(日志读取进程是以日志记录为单位进行读取的,一次只读取一条记录

/* get the size of the next entry */
ret = get_entry_len(log, reader->r_off);

如果其中有数据时则利用do_read_log_to_user执行真正的读取动作

/* get exactly one entry from the log */
ret = do_read_log_to_user(log, reader, buf, ret);

下面我们仔细看下get_entry_len函数:

[cpp]  view plain  copy
  1. /* 
  2.  * get_entry_len - Grabs the length of the payload of the next entry starting 
  3.  * from 'off'. 
  4.  * 
  5.  * Caller needs to hold log->mutex. 
  6.  */  
  7. static __u32 get_entry_len(struct logger_log *log, size_t off)  
  8. {  
  9.     __u16 val;  
  10.   
  11.     switch (log->size - off) {  
  12.     case 1:  
  13.         memcpy(&val, log->buffer + off, 1);  
  14.         memcpy(((char *) &val) + 1, log->buffer, 1);  
  15.         break;  
  16.     default:  
  17.         memcpy(&val, log->buffer + off, 2);  
  18.     }  
  19.   
  20.     return sizeof(struct logger_entry) + val;  
  21. }  
上面这段代码第一次也看不很久,后来想到buffer是一个循环缓冲区终于明白啦!!

我们知道每一条日志记录由两大部分组成,一部分是结构体:struct logger_entry,另外一部是payload有效负载即打印主体数据。
有效负载长度记录在struct logger_entry中的len字段中,占用两个字节,与结构的struct logger_entry的首地址相同。因此只要读取记录
最前面两个字节就可以了。
1、两个字节连在一起,直接读取即可,所以直接使用 memcpy(&val, log->buffer + off, 2); 
2、两个字节不连在一起,则需要分别读取,这种情况就是读取缓冲区最后一个字节和第一个字节来获取其长度,而此时r_off与size的长度相差1

ok,继续分析真正的数据读取函数:

[cpp]  view plain  copy
  1. /* 
  2.  * do_read_log_to_user - reads exactly 'count' bytes from 'log' into the 
  3.  * user-space buffer 'buf'. Returns 'count' on success. 
  4.  * 
  5.  * Caller must hold log->mutex. 
  6.  */  
  7. static ssize_t do_read_log_to_user(struct logger_log *log,  
  8.                    struct logger_reader *reader,  
  9.                    char __user *buf,  
  10.                    size_t count)  
  11. {  
  12.     size_t len;  
  13.   
  14.     /* 
  15.      * We read from the log in two disjoint operations. First, we read from 
  16.      * the current read head offset up to 'count' bytes or to the end of 
  17.      * the log, whichever comes first. 
  18.      */  
  19.     len = min(count, log->size - reader->r_off);  
  20.     if (copy_to_user(buf, log->buffer + reader->r_off, len))  
  21.         return -EFAULT;  
  22.   
  23.     /* 
  24.      * Second, we read any remaining bytes, starting back at the head of 
  25.      * the log. 
  26.      */  
  27.     if (count != len)  
  28.         if (copy_to_user(buf + len, log->buffer, count - len))  
  29.             return -EFAULT;  
  30.   
  31.     reader->r_off = logger_offset(reader->r_off + count);  
  32.   
  33.     return count;  
  34. }  
根据缓冲区中数据分两段的情况, 调用copy_to_user函数来把位于内核空间的日志缓冲区指定的内容拷贝到用户空间的内存缓冲区就可以了,同时,把当前读取日志进程的上下文信息中的读偏移r_off前进到下一条日志记录的开始的位置上。

5、其它函数:

logger_poll  用于log用户态调用select函数进行查询,利用

if (log->w_off != reader->r_off)
ret |= POLLIN | POLLRDNORM;

通知用户是否有有日记需要读取

logger_ioctl 用于一些常用的信息查询,

#define LOGGER_GET_LOG_BUF_SIZE_IO(__LOGGERIO, 1) /* size of log */
#define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */
#define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */
#define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */

获取缓冲区中数据长度,下一个日志的记录,比较有意义的是 LOGGER_FLUSH_LOG:

list_for_each_entry(reader, &log->readers, list)
reader->r_off = log->w_off;
log->head = log->w_off;

清除缓冲区中的所有数据


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值