日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

Android日志系統(tǒng)驅(qū)動(dòng)程序Logger源代碼分析

 lifei_szdz 2013-05-05

 我們知道,在Android系統(tǒng)中,提供了一個(gè)輕量級(jí)的日志系統(tǒng),這個(gè)日志系統(tǒng)是以驅(qū)動(dòng)程序的形式實(shí)現(xiàn)在內(nèi)核空間的,而在用戶(hù)空間分別提供了Java接口和C/C++接口來(lái)使用這個(gè)日志系統(tǒng),取決于你編寫(xiě)的是Android應(yīng)用程序還是系統(tǒng)組件。在前面的文章淺談Android系統(tǒng)開(kāi)發(fā)中LOG的使用中,已經(jīng)簡(jiǎn)要地介紹了在Android應(yīng)用程序開(kāi)發(fā)中Log的使用方法,在這一篇文章中,我們將更進(jìn)一步地分析Logger驅(qū)動(dòng)程序的源代碼,使得我們對(duì)Android日志系統(tǒng)有一個(gè)深刻的認(rèn)識(shí)。

        既然Android 日志系統(tǒng)是以驅(qū)動(dòng)程序的形式實(shí)現(xiàn)在內(nèi)核空間的,我們就需要獲取Android內(nèi)核源代碼來(lái)分析了,請(qǐng)參照前面在Ubuntu上下載、編譯和安裝Android最新源代碼在Ubuntu上下載、編譯和安裝Android最新內(nèi)核源代碼(Linux Kernel)兩篇文章,下載好Android源代碼工程。Logger驅(qū)動(dòng)程序主要由兩個(gè)文件構(gòu)成,分別是:

       kernel/common/drivers/staging/android/logger.h

       kernel/common/drivers/staging/android/logger.c

       接下來(lái),我們將分別介紹Logger驅(qū)動(dòng)程序的相關(guān)數(shù)據(jù)結(jié)構(gòu),然后對(duì)Logger驅(qū)動(dòng)程序源代碼進(jìn)行情景分析,分別日志系統(tǒng)初始化情景、日志讀取情景和日志寫(xiě)入情景。

       一. Logger驅(qū)動(dòng)程序的相關(guān)數(shù)據(jù)結(jié)構(gòu)。

      我們首先來(lái)看logger.h頭文件的內(nèi)容:

  1. #ifndef _LINUX_LOGGER_H  
  2. #define _LINUX_LOGGER_H  
  3.   
  4. #include <linux/types.h>  
  5. #include <linux/ioctl.h>  
  6.   
  7. struct logger_entry {  
  8.     __u16       len;    /* length of the payload */  
  9.     __u16       __pad;  /* no matter what, we get 2 bytes of padding */  
  10.     __s32       pid;    /* generating process's pid */  
  11.     __s32       tid;    /* generating process's tid */  
  12.     __s32       sec;    /* seconds since Epoch */  
  13.     __s32       nsec;   /* nanoseconds */  
  14.     char        msg[0]; /* the entry's payload */  
  15. };  
  16.   
  17. #define LOGGER_LOG_RADIO    "log_radio" /* radio-related messages */  
  18. #define LOGGER_LOG_EVENTS   "log_events"    /* system/hardware events */  
  19. #define LOGGER_LOG_MAIN     "log_main"  /* everything else */  
  20.   
  21. #define LOGGER_ENTRY_MAX_LEN        (4*1024)  
  22. #define LOGGER_ENTRY_MAX_PAYLOAD    \  
  23.     (LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry))  
  24.   
  25. #define __LOGGERIO  0xAE  
  26.   
  27. #define LOGGER_GET_LOG_BUF_SIZE     _IO(__LOGGERIO, 1) /* size of log */  
  28. #define LOGGER_GET_LOG_LEN      _IO(__LOGGERIO, 2) /* used log len */  
  29. #define LOGGER_GET_NEXT_ENTRY_LEN   _IO(__LOGGERIO, 3) /* next entry len */  
  30. #define LOGGER_FLUSH_LOG        _IO(__LOGGERIO, 4) /* flush log */  
  31.   
  32. #endif /* _LINUX_LOGGER_H */  
        struct logger_entry是一個(gè)用于描述一條Log記錄的結(jié)構(gòu)體。len成員變量記錄了這條記錄的有效負(fù)載的長(zhǎng)度,有效負(fù)載指定的日志記錄本身的長(zhǎng)度,但是不包括用于描述這個(gè)記錄的struct logger_entry結(jié)構(gòu)體?;貞浺幌挛覀冋{(diào)用android.util.Log接口來(lái)使用日志系統(tǒng)時(shí),會(huì)指定日志的優(yōu)先級(jí)別Priority、Tag字符串以及Msg字符串,Priority + Tag + Msg三者內(nèi)容的長(zhǎng)度加起來(lái)就是記錄的有效負(fù)載長(zhǎng)度了。__pad成員變量是用來(lái)對(duì)齊結(jié)構(gòu)體的。pid和tid成員變量分別用來(lái)記錄是哪條進(jìn)程寫(xiě)入了這條記錄。sec和nsec成員變量記錄日志寫(xiě)的時(shí)間。msg成員變量記錄的就有效負(fù)載的內(nèi)容了,它的大小由len成員變量來(lái)確定。

       接著定義兩個(gè)宏:

       #define LOGGER_ENTRY_MAX_LEN             (4*1024)

       #define LOGGER_ENTRY_MAX_PAYLOAD   \

                         (LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry))

      從這兩個(gè)宏可以看出,每條日志記錄的有效負(fù)載長(zhǎng)度加上結(jié)構(gòu)體logger_entry的長(zhǎng)度不能超過(guò)4K個(gè)字節(jié)。

      logger.h文件中還定義了其它宏,讀者可以自己分析,在下面的分析中,碰到時(shí),我們也會(huì)詳細(xì)解釋。

      再來(lái)看logger.c文件中,其它相關(guān)數(shù)據(jù)結(jié)構(gòu)的定義:

  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. };  
  18.   
  19. /* 
  20.  * struct logger_reader - a logging device open for reading 
  21.  * 
  22.  * This object lives from open to release, so we don't need additional 
  23.  * reference counting. The structure is protected by log->mutex. 
  24.  */  
  25. struct logger_reader {  
  26.     struct logger_log * log;    /* associated log */  
  27.     struct list_head    list;   /* entry in logger_log's list */  
  28.     size_t          r_off;  /* current read head offset */  
  29. };  
  30.   
  31. /* logger_offset - returns index 'n' into the log via (optimized) modulus */  
  32. #define logger_offset(n)    ((n) & (log->size - 1))  
        結(jié)構(gòu)體struct logger_log就是真正用來(lái)保存日志的地方了。buffer成員變量變是用保存日志信息的內(nèi)存緩沖區(qū),它的大小由size成員變量確定。從misc成員變量可以看出,logger驅(qū)動(dòng)程序使用的設(shè)備屬于misc類(lèi)型的設(shè)備,通過(guò)在Android模擬器上執(zhí)行cat /proc/devices命令(可參考在Ubuntu上下載、編譯和安裝Android最新內(nèi)核源代碼(Linux Kernel)一文),可以看出,misc類(lèi)型設(shè)備的主設(shè)備號(hào)是10。關(guān)于主設(shè)備號(hào)的相關(guān)知識(shí),可以參考Android學(xué)習(xí)啟動(dòng)篇一文中提到的Linux Driver Development一書(shū)。wq成員變量是一個(gè)等待隊(duì)列,用于保存正在等待讀取日志的進(jìn)程。readers成員變量用來(lái)保存當(dāng)前正在讀取日志的進(jìn)程,正在讀取日志的進(jìn)程由結(jié)構(gòu)體logger_reader來(lái)描述。mutex成員變量是一個(gè)互斥量,用來(lái)保護(hù)log的并發(fā)訪問(wèn)。可以看出,這里的日志系統(tǒng)的讀寫(xiě)問(wèn)題,其實(shí)是一個(gè)生產(chǎn)者-消費(fèi)者的問(wèn)題,因此,需要互斥量來(lái)保護(hù)log的并發(fā)訪問(wèn)。 w_off成員變量用來(lái)記錄下一條日志應(yīng)該從哪里開(kāi)始寫(xiě)。head成員變量用來(lái)表示打開(kāi)日志文件中,應(yīng)該從哪一個(gè)位置開(kāi)始讀取日志。

       結(jié)構(gòu)體struct logger_reader用來(lái)表示一個(gè)讀取日志的進(jìn)程,log成員變量指向要讀取的日志緩沖區(qū)。list成員變量用來(lái)連接其它讀者進(jìn)程。r_off成員變量表示當(dāng)前要讀取的日志在緩沖區(qū)中的位置。

       struct logger_log結(jié)構(gòu)體中用于保存日志信息的內(nèi)存緩沖區(qū)buffer是一個(gè)循環(huán)使用的環(huán)形緩沖區(qū),緩沖區(qū)中保存的內(nèi)容是以struct logger_entry為單位的,每個(gè)單位的組成為:

       struct logger_entry | priority | tag | msg

       由于是內(nèi)存緩沖區(qū)buffer是一個(gè)循環(huán)使用的環(huán)形緩沖區(qū),給定一個(gè)偏移值,它在buffer中的位置由下logger_offset來(lái)確定:

       #define logger_offset(n)          ((n) & (log->size - 1))

       二. Logger驅(qū)動(dòng)程序模塊的初始化過(guò)程分析。

       繼續(xù)看logger.c文件,定義了三個(gè)日志設(shè)備:

  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)  
       分別是log_main、log_events和log_radio,名稱(chēng)分別LOGGER_LOG_MAIN、LOGGER_LOG_EVENTS和LOGGER_LOG_RADIO,它們的次設(shè)備號(hào)為MISC_DYNAMIC_MINOR,即為在注冊(cè)時(shí)動(dòng)態(tài)分配。在logger.h文件中,有這三個(gè)宏的定義:

       #define LOGGER_LOG_RADIO "log_radio" /* radio-related messages */
       #define LOGGER_LOG_EVENTS "log_events" /* system/hardware events */
       #define LOGGER_LOG_MAIN "log_main" /* everything else */

       注釋說(shuō)明了這三個(gè)日志設(shè)備的用途。注冊(cè)的日志設(shè)備文件操作方法為logger_fops:

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

       日志驅(qū)動(dòng)程序模塊的初始化函數(shù)為logger_init:

  1. static int __init logger_init(void)  
  2. {  
  3.     int ret;  
  4.   
  5.     ret = init_log(&log_main);  
  6.     if (unlikely(ret))  
  7.         goto out;  
  8.   
  9.     ret = init_log(&log_events);  
  10.     if (unlikely(ret))  
  11.         goto out;  
  12.   
  13.     ret = init_log(&log_radio);  
  14.     if (unlikely(ret))  
  15.         goto out;  
  16.   
  17. out:  
  18.     return ret;  
  19. }  
  20. device_initcall(logger_init);  
        logger_init函數(shù)通過(guò)調(diào)用init_log函數(shù)來(lái)初始化了上述提到的三個(gè)日志設(shè)備:

  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. }  
        init_log函數(shù)主要調(diào)用了misc_register函數(shù)來(lái)注冊(cè)misc設(shè)備,misc_register函數(shù)定義在kernel/common/drivers/char/misc.c文件中:

  1. /** 
  2.  *      misc_register   -       register a miscellaneous device 
  3.  *      @misc: device structure 
  4.  * 
  5.  *      Register a miscellaneous device with the kernel. If the minor 
  6.  *      number is set to %MISC_DYNAMIC_MINOR a minor number is assigned 
  7.  *      and placed in the minor field of the structure. For other cases 
  8.  *      the minor number requested is used. 
  9.  * 
  10.  *      The structure passed is linked into the kernel and may not be 
  11.  *      destroyed until it has been unregistered. 
  12.  * 
  13.  *      A zero is returned on success and a negative errno code for 
  14.  *      failure. 
  15.  */  
  16.   
  17. int misc_register(struct miscdevice * misc)  
  18. {  
  19.         struct miscdevice *c;  
  20.         dev_t dev;  
  21.         int err = 0;  
  22.   
  23.         INIT_LIST_HEAD(&misc->list);  
  24.   
  25.         mutex_lock(&misc_mtx);  
  26.         list_for_each_entry(c, &misc_list, list) {  
  27.                 if (c->minor == misc->minor) {  
  28.                         mutex_unlock(&misc_mtx);  
  29.                         return -EBUSY;  
  30.                 }  
  31.         }  
  32.   
  33.         if (misc->minor == MISC_DYNAMIC_MINOR) {  
  34.                 int i = DYNAMIC_MINORS;  
  35.                 while (--i >= 0)  
  36.                         if ( (misc_minors[i>>3] & (1 << (i&7))) == 0)  
  37.                                 break;  
  38.                 if (i<0) {  
  39.                         mutex_unlock(&misc_mtx);  
  40.                         return -EBUSY;  
  41.                 }  
  42.                 misc->minor = i;  
  43.         }  
  44.   
  45.         if (misc->minor < DYNAMIC_MINORS)  
  46.                 misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7);  
  47.         dev = MKDEV(MISC_MAJOR, misc->minor);  
  48.   
  49.         misc->this_device = device_create(misc_class, misc->parent, dev, NULL,  
  50.                                           "%s", misc->name);  
  51.         if (IS_ERR(misc->this_device)) {  
  52.                 err = PTR_ERR(misc->this_device);  
  53.                 goto out;  
  54.         }  
  55.   
  56.         /* 
  57.          * Add it to the front, so that later devices can "override" 
  58.          * earlier defaults 
  59.          */  
  60.         list_add(&misc->list, &misc_list);  
  61.  out:  
  62.         mutex_unlock(&misc_mtx);  
  63.         return err;  
  64. }  
        注冊(cè)完成后,通過(guò)device_create創(chuàng)建設(shè)備文件節(jié)點(diǎn)。這里,將創(chuàng)建/dev/log/main、/dev/log/events和/dev/log/radio三個(gè)設(shè)備文件,這樣,用戶(hù)空間就可以通過(guò)讀寫(xiě)這三個(gè)文件和驅(qū)動(dòng)程序進(jìn)行交互。

        三. Logger驅(qū)動(dòng)程序的日志記錄讀取過(guò)程分析。

        繼續(xù)看logger.c 文件,注冊(cè)的讀取日志設(shè)備文件的方法為logger_read:

  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. }  
       注意,在函數(shù)開(kāi)始的地方,表示讀取日志上下文的struct logger_reader是保存在文件指針的private_data成員變量里面的,這是在打開(kāi)設(shè)備文件時(shí)設(shè)置的,設(shè)備文件打開(kāi)方法為logger_open:

  1. /* 
  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;  
  31.         list_add_tail(&reader->list, &log->readers);  
  32.         mutex_unlock(&log->mutex);  
  33.   
  34.         file->private_data = reader;  
  35.     } else  
  36.         file->private_data = log;  
  37.   
  38.     return 0;  
  39. }  
       新打開(kāi)日志設(shè)備文件時(shí),是從log->head位置開(kāi)始讀取日志的,保存在struct logger_reader的成員變量r_off中。

       start標(biāo)號(hào)處的while循環(huán)是在等待日志可讀,如果已經(jīng)沒(méi)有新的日志可讀了,那么就要讀進(jìn)程就要進(jìn)入休眠狀態(tài),等待新的日志寫(xiě)入后再喚醒,這是通過(guò)prepare_wait和schedule兩個(gè)調(diào)用來(lái)實(shí)現(xiàn)的。如果沒(méi)有新的日志可讀,并且設(shè)備文件不是以非阻塞O_NONBLOCK的方式打開(kāi)或者這時(shí)有信號(hào)要處理(signal_pending(current)),那么就直接返回,不再等待新的日志寫(xiě)入。判斷當(dāng)前是否有新的日志可讀的方法是:

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

       即判斷當(dāng)前緩沖區(qū)的寫(xiě)入位置和當(dāng)前讀進(jìn)程的讀取位置是否相等,如果不相等,則說(shuō)明有新的日志可讀。

       繼續(xù)向下看,如果有新的日志可讀,那么就,首先通過(guò)get_entry_len來(lái)獲取下一條可讀的日志記錄的長(zhǎng)度,從這里可以看出,日志讀取進(jìn)程是以日志記錄為單位進(jìn)行讀取的,一次只讀取一條記錄。get_entry_len的函數(shù)實(shí)現(xiàn)如下:

  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. }  
        上面我們提到,每一條日志記錄是由兩大部分組成的,一個(gè)用于描述這條日志記錄的結(jié)構(gòu)體struct logger_entry,另一個(gè)是記錄體本身,即有效負(fù)載。結(jié)構(gòu)體struct logger_entry的長(zhǎng)度是固定的,只要知道有效負(fù)載的長(zhǎng)度,就可以知道整條日志記錄的長(zhǎng)度了。而有效負(fù)載的長(zhǎng)度是記錄在結(jié)構(gòu)體struct logger_entry的成員變量len中,而len成員變量的地址與struct logger_entry的地址相同,因此,只需要讀取記錄的開(kāi)始位置的兩個(gè)字節(jié)就可以了。又由于日志記錄緩沖區(qū)是循環(huán)使用的,這兩個(gè)節(jié)字有可能是第一個(gè)字節(jié)存放在緩沖區(qū)最后一個(gè)字節(jié),而第二個(gè)字節(jié)存放在緩沖區(qū)的第一個(gè)節(jié),除此之外,這兩個(gè)字節(jié)都是連在一起的。因此,分兩種情況來(lái)考慮,對(duì)于前者,分別通過(guò)讀取緩沖區(qū)最后一個(gè)字節(jié)和第一個(gè)字節(jié)來(lái)得到日志記錄的有效負(fù)載長(zhǎng)度到本地變量val中,對(duì)于后者,直接讀取連續(xù)兩個(gè)字節(jié)的值到本地變量val中。這兩種情況是通過(guò)判斷日志緩沖區(qū)的大小和要讀取的日志記錄在緩沖區(qū)中的位置的差值來(lái)區(qū)別的,如果相差1,就說(shuō)明是前一種情況了。最后,把有效負(fù)載的長(zhǎng)度val加上struct logger_entry的長(zhǎng)度就得到了要讀取的日志記錄的總長(zhǎng)度了。

       接著往下看,得到了要讀取的記錄的長(zhǎng)度,就調(diào)用do_read_log_to_user函數(shù)來(lái)執(zhí)行真正的讀取動(dòng)作:

  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.     size_t len;  
  7.   
  8.     /* 
  9.      * We read from the log in two disjoint operations. First, we read from 
  10.      * the current read head offset up to 'count' bytes or to the end of 
  11.      * the log, whichever comes first. 
  12.      */  
  13.     len = min(count, log->size - reader->r_off);  
  14.     if (copy_to_user(buf, log->buffer + reader->r_off, len))  
  15.         return -EFAULT;  
  16.   
  17.     /* 
  18.      * Second, we read any remaining bytes, starting back at the head of 
  19.      * the log. 
  20.      */  
  21.     if (count != len)  
  22.         if (copy_to_user(buf + len, log->buffer, count - len))  
  23.             return -EFAULT;  
  24.   
  25.     reader->r_off = logger_offset(reader->r_off + count);  
  26.   
  27.     return count;  
  28. }  
        這個(gè)函數(shù)簡(jiǎn)單地調(diào)用copy_to_user函數(shù)來(lái)把位于內(nèi)核空間的日志緩沖區(qū)指定的內(nèi)容拷貝到用戶(hù)空間的內(nèi)存緩沖區(qū)就可以了,同時(shí),把當(dāng)前讀取日志進(jìn)程的上下文信息中的讀偏移r_off前進(jìn)到下一條日志記錄的開(kāi)始的位置上。

        四.  Logger驅(qū)動(dòng)程序的日志記錄寫(xiě)入過(guò)程分析。

        繼續(xù)看logger.c 文件,注冊(cè)的寫(xiě)入日志設(shè)備文件的方法為logger_aio_write:

  1. /* 
  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.     struct logger_entry header;  
  12.     struct timespec now;  
  13.     ssize_t ret = 0;  
  14.   
  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. }  
        輸入的參數(shù)iocb表示io上下文,iov表示要寫(xiě)入的內(nèi)容,長(zhǎng)度為nr_segs,表示有nr_segs個(gè)段的內(nèi)容要寫(xiě)入。我們知道,每個(gè)要寫(xiě)入的日志的結(jié)構(gòu)形式為:

        struct logger_entry | priority | tag | msg

        其中, priority、tag和msg這三個(gè)段的內(nèi)容是由iov參數(shù)從用戶(hù)空間傳遞下來(lái)的,分別對(duì)應(yīng)iov里面的三個(gè)元素。而logger_entry是由內(nèi)核空間來(lái)構(gòu)造的:

        struct logger_entry header;
struct timespec now;

now = current_kernel_time();

header.pid = current->tgid;
header.tid = current->pid;
header.sec = now.tv_sec;
header.nsec = now.tv_nsec;
header.len = min_t(size_t, iocb->ki_left, LOGGER_ENTRY_MAX_PAYLOAD);

        然后調(diào)用do_write_log首先把logger_entry結(jié)構(gòu)體寫(xiě)入到日志緩沖區(qū)中:

  1. /* 
  2.  * do_write_log - writes 'len' bytes from 'buf' to 'log' 
  3.  * 
  4.  * The caller needs to hold log->mutex. 
  5.  */  
  6. static void do_write_log(struct logger_log *log, const void *buf, size_t count)  
  7. {  
  8.     size_t len;  
  9.   
  10.     len = min(count, log->size - log->w_off);  
  11.     memcpy(log->buffer + log->w_off, buf, len);  
  12.   
  13.     if (count != len)  
  14.         memcpy(log->buffer, buf + len, count - len);  
  15.   
  16.     log->w_off = logger_offset(log->w_off + count);  
  17.   
  18. }  
       由于logger_entry是內(nèi)核堆??臻g分配的,直接用memcpy拷貝就可以了。

       接著,通過(guò)一個(gè)while循環(huán)把iov的內(nèi)容寫(xiě)入到日志緩沖區(qū)中,也就是日志的優(yōu)先級(jí)別priority、日志Tag和日志主體Msg:

  1. while (nr_segs-- > 0) {  
  2.         size_t len;  
  3.         ssize_t nr;  
  4.   
  5.         /* figure out how much of this vector we can keep */  
  6.         len = min_t(size_t, iov->iov_len, header.len - ret);  
  7.   
  8.         /* write out this segment's payload */  
  9.         nr = do_write_log_from_user(log, iov->iov_base, len);  
  10.         if (unlikely(nr < 0)) {  
  11.             log->w_off = orig;  
  12.             mutex_unlock(&log->mutex);  
  13.             return nr;  
  14.         }  
  15.   
  16.         iov++;  
  17.         ret += nr;  
  18. }  
         由于iov的內(nèi)容是由用戶(hù)空間傳下來(lái)的,需要調(diào)用do_write_log_from_user來(lái)寫(xiě)入:

  1. static ssize_t do_write_log_from_user(struct logger_log *log,  
  2.                       const void __user *buf, size_t count)  
  3. {  
  4.     size_t len;  
  5.   
  6.     len = min(count, log->size - log->w_off);  
  7.     if (len && copy_from_user(log->buffer + log->w_off, buf, len))  
  8.         return -EFAULT;  
  9.   
  10.     if (count != len)  
  11.         if (copy_from_user(log->buffer, buf + len, count - len))  
  12.             return -EFAULT;  
  13.   
  14.     log->w_off = logger_offset(log->w_off + count);  
  15.   
  16.     return count;  
  17. }  
        這里,我們還漏了一個(gè)重要的步驟:

  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);  
        為什么要調(diào)用fix_up_reader這個(gè)函數(shù)呢?這個(gè)函數(shù)又是作什么用的呢?是這樣的,由于日志緩沖區(qū)是循環(huán)使用的,即舊的日志記錄如果沒(méi)有及時(shí)讀取,而緩沖區(qū)的內(nèi)容又已經(jīng)用完時(shí),就需要覆蓋舊的記錄來(lái)容納新的記錄。而這部分將要被覆蓋的內(nèi)容,有可能是某些reader的下一次要讀取的日志所在的位置,以及為新的reader準(zhǔn)備的日志開(kāi)始讀取位置head所在的位置。因此,需要調(diào)整這些位置,使它們能夠指向一個(gè)新的有效的位置。我們來(lái)看一下fix_up_reader函數(shù)的實(shí)現(xiàn):

  1. /* 
  2.  * fix_up_readers - walk the list of all readers and "fix up" any who were 
  3.  * lapped by the writer; also do the same for the default "start head". 
  4.  * We do this by "pulling forward" the readers and start head to the first 
  5.  * entry after the new write head. 
  6.  * 
  7.  * The caller needs to hold log->mutex. 
  8.  */  
  9. static void fix_up_readers(struct logger_log *log, size_t len)  
  10. {  
  11.     size_t old = log->w_off;  
  12.     size_t new = logger_offset(old + len);  
  13.     struct logger_reader *reader;  
  14.   
  15.     if (clock_interval(old, new, log->head))  
  16.         log->head = get_next_entry(log, log->head, len);  
  17.   
  18.     list_for_each_entry(reader, &log->readers, list)  
  19.         if (clock_interval(old, new, reader->r_off))  
  20.             reader->r_off = get_next_entry(log, reader->r_off, len);  
  21. }  
        判斷l(xiāng)og->head和所有讀者reader的當(dāng)前讀偏移reader->r_off是否在被覆蓋的區(qū)域內(nèi),如果是,就需要調(diào)用get_next_entry來(lái)取得下一個(gè)有效的記錄的起始位置來(lái)調(diào)整當(dāng)前位置:

  1. /* 
  2.  * get_next_entry - return the offset of the first valid entry at least 'len' 
  3.  * bytes after 'off'. 
  4.  * 
  5.  * Caller must hold log->mutex. 
  6.  */  
  7. static size_t get_next_entry(struct logger_log *log, size_t off, size_t len)  
  8. {  
  9.     size_t count = 0;  
  10.   
  11.     do {  
  12.         size_t nr = get_entry_len(log, off);  
  13.         off = logger_offset(off + nr);  
  14.         count += nr;  
  15.     } while (count < len);  
  16.   
  17.     return off;  
  18. }  
        而判斷l(xiāng)og->head和所有讀者reader的當(dāng)前讀偏移reader->r_off是否在被覆蓋的區(qū)域內(nèi),是通過(guò)clock_interval函數(shù)來(lái)實(shí)現(xiàn)的:

  1. /* 
  2.  * clock_interval - is a < c < b in mod-space? Put another way, does the line 
  3.  * from a to b cross c? 
  4.  */  
  5. static inline int clock_interval(size_t a, size_t b, size_t c)  
  6. {  
  7.     if (b < a) {  
  8.         if (a < c || b >= c)  
  9.             return 1;  
  10.     } else {  
  11.         if (a < c && b >= c)  
  12.             return 1;  
  13.     }  
  14.   
  15.     return 0;  
  16. }  
        最后,日志寫(xiě)入完畢,還需要喚醒正在等待新日志的reader進(jìn)程:

        /* wake up any blocked readers */
wake_up_interruptible(&log->wq);

        至此, Logger驅(qū)動(dòng)程序的主要邏輯就分析完成了,還有其它的一些接口,如logger_poll、 logger_ioctl和logger_release函數(shù),比較簡(jiǎn)單,讀取可以自行分析。這里還需要提到的一點(diǎn)是,由于Logger驅(qū)動(dòng)程序模塊在退出系統(tǒng)時(shí),是不會(huì)卸載的,所以這個(gè)模塊沒(méi)有module_exit函數(shù),而對(duì)于模塊里面定義的對(duì)象,也沒(méi)有用對(duì)引用計(jì)數(shù)技術(shù)。

      這篇文章著重介紹了Android日志系統(tǒng)在內(nèi)核空間的實(shí)現(xiàn),在下一篇文章中,我們將接著介紹在用戶(hù)空間中,提供給Android應(yīng)用程序使用的Java和C/C++ LOG調(diào)用接口的實(shí)現(xiàn)過(guò)程,敬請(qǐng)關(guān)注。

老羅的新浪微博:http://weibo.com/shengyangluo,歡迎關(guān)注!

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多