Kernel的环形Buffer(Ring Buffer)——以Logger Buffer为例

时间:2022-04-11 03:59:18

在Android L之前的日志系统是Kernel层实现了若干个环形Buffer实现的。系统各个日志读写操作都是针对这几个RingBuffer来实现的。那就来一窥Kernel是怎么做的。相关源码是位于driver/staging/android/下面的logger.c和logger.h两个文件

1,在整个Android日志系统的位置

Kernel的环形Buffer(Ring Buffer)——以Logger Buffer为例

2,在logger.c中,入口函数

  
  
 
 
  1. static int __init logger_init(void)
  2. {
  3. ...
  4. ret = create_log(LOGGER_LOG_MAIN, 256*1024);
  5. ret = create_log(LOGGER_LOG_EVENTS, 256*1024);
  6. ret = create_log(LOGGER_LOG_RADIO, 256*1024);
  7. ret = create_log(LOGGER_LOG_SYSTEM, 256*1024);
  8. ...
  9. }

分别来看create_log的实现和LOGGER_LOG_*的相关定义

  
  
 
 
  1. static int __init create_log(char *log_name, int size)
  2. {
  3. ...
  4. log->buffer = buffer;
  5. /* finally, initialize the misc device for this log */
  6. ret = misc_register(&log->misc);
  7. ...
  8. }
  
  
 
 
  1. #define LOGGER_LOG_RADIO "log_radio" /* radio-related messages */
  2. #define LOGGER_LOG_EVENTS "log_events" /* system/hardware events */
  3. #define LOGGER_LOG_SYSTEM "log_system" /* system/framework messages */
  4. #define LOGGER_LOG_MAIN "log_main" /* everything else */
  5. ...

从上可以知道,create_log函数就是把创建4个misc设备,并注册上。在创建的过程中会初始化各个设备的logger_log结构体。也就是说有4个logger_log结构体的实例。这4个MISC设备(即对应4种Logger)的Buffer大小为256Kb,即RING BUFFER的大小为256Kb。

再来看这个十分重要的结构体logger_log的其它成员

  
  
 
 
  1. /**
  2. * struct logger_log - represents a specific log, such as 'main' or 'radio'
  3. * @buffer: The actual ring buffer
  4. * @misc: The "misc" device representing the log
  5. * @wq: The wait queue for @readers
  6. * @readers: This log's readers
  7. * @mutex: The mutex that protects the @buffer
  8. * @w_off: The current write head offset
  9. * @head: The head, or location that readers start reading at.
  10. * @size: The size of the log
  11. * @logs: The list of log channels
  12. *
  13. * This structure lives from module insertion until module removal, so it does
  14. * not need additional reference counting. The structure is protected by the
  15. * mutex 'mutex'.
  16. */
  17. struct logger_log {
  18. unsigned char *buffer;
  19. struct miscdevice misc;
  20. wait_queue_head_t wq;
  21. struct list_head readers;
  22. struct mutex mutex;
  23. size_t w_off;
  24. size_t head;
  25. size_t size;
  26. struct list_head logs;
  27. };

不复杂,注释的解析也十分清楚。

用户层对于这4个MISC设备的操作,是通过/dev/log/*下面的几个设备实现的,它同样也有file_operations结构体中的读写函数指针,如下:

  
  
 
 
  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. };

3,MISC设备节点是怎么生成?

在运行的系统的/dev/log目录下有如下节点,

  
  
 
 
  1. root@U:/dev/log # ls
  2. events
  3. main
  4. radio
  5. system

这是因为在上面提到的misc_register(&log->misc)函数,在前面的Kernel设备驱动相关文章可以知道,最终是调用device_create() -> device_add()->kobject_uevent(&dev->kobj, KOBJ_ADD);就会发送一个KOBJ_ADD的UEVENT给到上层,由《Android的根文件系统》可以知道,这是由init进程来处理的。在init项目中devices.cpp会来处理这个ADD事件

  
  
 
 
  1. ... 
  2. } else if(!strncmp(uevent->subsystem, "misc", 4) &&
  3. !strncmp(name, "log_", 4)) {
  4. INFO("kernel logger is deprecated\n");
  5. base = "/dev/log/";
  6. make_dir(base, 0755);
  7. name += 4;
  8. ...

如果设备的名字前缀是log_的话,就会创建/dev/log/目录。所以就出现dev/log/下面的4个文件。

4,写操作分析

写操作是写一条消息,封装在logger_entry为头的消息。一次写操作大概构成如下:

struct logger_entry | priority | tag | msg

比如logcat打印出来如下:

12-06 16:12:23.849  3700  3788 | D | WifiStateMachine | : handleMessage: X

logger_aio_write()函数如下:

  
  
 
 
  1. static ssize_t logger_aio_write(struct kiocb *iocb, const struct iovec *iov,
  2. unsigned long nr_segs, loff_t ppos)
  3. {
  4. //1,填充header头的结构体
  5. header.pid = current->tgid;
  6. header.tid = current->pid;
  7. header.sec = now.tv_sec;
  8. header.nsec = now.tv_nsec;
  9. header.euid = current_euid();
  10. header.len = min_t(size_t, iocb->ki_nbytes, LOGGER_ENTRY_MAX_PAYLOAD);
  11. header.hdr_size = sizeof(struct logger_entry);
  12. /* null writes succeed, return zero */
  13. if (unlikely(!header.len))
  14. return 0;
  15. //如果写的一条信息,会覆盖正在进程读的信息,那么就丢弃这条读,把读的偏移指到下一条安全的信息。
  16. fix_up_readers(log, sizeof(struct logger_entry) + header.len);
  17. //2,把header信息定写入
  18. do_write_log(log, &header, sizeof(struct logger_entry));
  19. //3,把用户传过来的Message信息写入
  20. while (nr_segs-- > 0) {
  21. size_t len;
  22. ssize_t nr;
  23. /* figure out how much of this vector we can keep */
  24.     //这里计算剩余的信息是否超过了MAX_PAYLOAD的长度,即4K,超过部分将会丢弃。
  25.     //一条信息基本上也不会超过4K,基本是取实际信息长度
  26. len = min_t(size_t, iov->iov_len, header.len - ret);
  27. /* write out this segment's payload */
  28. nr = do_write_log_from_user(log, iov->iov_base, len);
  29. if (unlikely(nr < 0)) {
  30. log->w_off = orig;
  31. mutex_unlock(&log->mutex);
  32. return nr;
  33. }
  34. iov++;
  35. ret += nr;
  36. }
  37. //4,唤醒读的队列
  38. /* wake up any blocked readers */
  39. wake_up_interruptible(&log->wq);
  40. return ret;
  41. }

一条log日志的头部信息(12-06 16:12:23.849  3700  3788)是在kernel中写入的。do_write_log()

  
  
 
 
  1. static void do_write_log(struct logger_log *log, const void *buf, size_t count)
  2. {
  3. size_t len;
  4. //Buffer剩余的长度和消息长度取较小的值
  5. //如果buffer剩余长度不够写完一条信息,就进入下面的if,把剩余的部分,从头部开始写进去。
  6. len = min(count, log->size - log->w_off);
  7. memcpy(log->buffer + log->w_off, buf, len);
  8. //如果一条Log,不够剩余的buffer的话,上面memcpy就是写了前半部分,
  9. //下面的memcpy就把剩下的后半部分(cout-len),写到log->buffer的头。
  10. //这里体现了循环Buffer。
  11. if (count != len)
  12. memcpy(log->buffer, buf + len, count - len);
  13. //移动w_off
  14. log->w_off = logger_offset(log, log->w_off + count);
  15. }

写完头部,再写用户层传过来的信息

  
  
 
 
  1. static ssize_t do_write_log_from_user(struct logger_log *log,
  2. const void __user *buf, size_t count)
  3. {
  4. //同理会作判断,是否剩余buffer能够写完,否则就把剩余的部分从头开始写
  5. len = min(count, log->size - log->w_off);
  6. if (len && copy_from_user(log->buffer + log->w_off, buf, len))
  7. return -EFAULT;
  8. if (count != len)
  9. if (copy_from_user(log->buffer, buf + len, count - len))
  10. log->w_off = logger_offset(log, log->w_off + count);
  11. return count;
  12. }

5,写操作基本上完成了,接下来看读操作。

  
  
 
 
  1. static ssize_t logger_read(struct file *file, char __user *buf,
  2. size_t count, loff_t *pos)
  3. {
  4. //1,如果读的偏移r_off与写的偏移w_off相等,则循环阻塞,直到write最后唤醒
  5. while (1) {
  6. prepare_to_wait(&log->wq, &wait, TASK_INTERRUPTIBLE);//write函数的wake_up_interruptible(&log->wq);会唤醒
  7. //Write的off存在Logger_log即内存buffer中,而r_off存在reder,即读的进程中。所以执行两次不同的logcat,都是从头开始读的。
  8.         ret = (log->w_off == reader->r_off);
  9. mutex_unlock(&log->mutex);
  10. if (!ret)
  11. break;//如果不相等就跳出去处理。
  12. schedule(); //把cpu调试出去,等待队列的唤醒
  13. }
  14. if (!reader->r_all)
  15. reader->r_off = get_next_entry_by_uid(log,
  16. reader->r_off, current_euid());
  17. /* get the size of the next entry */
  18. //2,获取要读的整个一条日志的长度,同样由两部分组成,一个head长度+信息的长度。
  19.     ret = get_user_hdr_len(reader->r_ver) +
  20. get_entry_msg_len(log, reader->r_off);
  21. /* get exactly one entry from the log */
  22.     //3,把log读ret长度的信息到buf中。
  23. ret = do_read_log_to_user(log, reader, buf, ret);
  24. out:
  25. mutex_unlock(&log->mutex);
  26. return ret;
  27. }

来看看第3步do_read_log_to_user的实现

  
  
 
 
  1. static ssize_t do_read_log_to_user(struct logger_log *log,
  2. struct logger_reader *reader,
  3. char __user *buf,
  4. size_t count)
  5. {
  6.     //先把header copy到用户空间
  7. entry = get_entry_header(log, reader->r_off, &scratch);
  8. if (copy_header_to_user(reader->r_ver, entry, buf))
  9. return -EFAULT;
  10. count -= get_user_hdr_len(reader->r_ver);
  11. buf += get_user_hdr_len(reader->r_ver);
  12. msg_start = logger_offset(log,
  13. reader->r_off + sizeof(struct logger_entry));
  14. /*
  15. * We read from the msg in two disjoint operations. First, we read from
  16. * the current msg head offset up to 'count' bytes or to the end of
  17. * the log, whichever comes first.
  18. */
  19. //同样做是否到log buffer尾部的判断,否则就要读两次
  20. len = min(count, log->size - msg_start);
  21. if (copy_to_user(buf, log->buffer + msg_start, len))
  22. return -EFAULT;
  23. ...
  24. if (count != len)
  25. if (copy_to_user(buf + len, log->buffer, count - len))
  26. return -EFAULT;
  27. //移动reader的off偏移
  28. reader->r_off = logger_offset(log, reader->r_off +
  29. sizeof(struct logger_entry) + count);
  30. return count + get_user_hdr_len(reader->r_ver);
  31. }

这样Logger的实现基本就完成了,循环Buffer的实现,也就是在读与写的操作过程,是否已经到了buffer尾的判断。

  
  
 
 
  1. len = min(count, log->size - msg_start);
  2. if (copy_to_user(buf, log->buffer + msg_start, len))
  3. if (count != len)
  4. if (copy_to_user(buf + len, log->buffer, count - len))
  5. return -EFAULT;

RingBuffer是Kernel一个常用的数据结构,从Logger系统中看它的实现,看到其它模块使用,心里也有就数了。