<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                當進程調用read函數從日志設備讀取日志記錄時,Logger日志驅動程序中的函數logger_read就會被調用從相應的日志緩沖區中讀取日志記錄。 **kernel/goldfish/drivers/staging/android/logger.c** ~~~ /* * 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; } ~~~ 函數第16行得到日志記錄讀取進程結構體reader,而第17行得到日志緩沖區結構體log。在前面的4.2.3小節中提到,當進程以讀模式打開相應的日志設備時,Logger日志驅動程序就會將上述兩個結構體信息保存在打開文件結構體參數file的成員變量private_data中,因此,函數第16行和第17行就可以安全地將它們獲取回來。 函數第22行到第42行的while循環用來檢查日志緩沖區結構體log中是否有日志記錄可讀取,這主要是通過第26行代碼來判斷的。在進入第22行到第42行的while循環時,第23行首先調用函數prepare_to_wait將等待隊列項wait加入日志記錄讀取進程的等待隊列log->wq中。等到確認日志緩沖區結構體log有日志記錄可讀或者其他原因跳出該while循環時,第44行就會調用函數finish_wait將等待隊列項wait從日志記錄讀取進程的等待隊列log->wq中移除。 日志緩沖區結構體log的成員變量w_off用來描述下一條新寫入的日志記錄在日志緩沖區中的位置,而日志記錄讀取進程結構體reader的成員變量r_off用來描述當前進程下一條要讀取的日志記錄在日志緩沖區中的位置。當這兩者相等時,就說明日志緩沖區結構體log中沒有新的日志記錄可讀,因此,Logger日志驅動程序就會繼續執行第22行到第42行的while循環來等待寫入新的日志記錄。如果日志緩沖區結構體log中有新的日志記錄可讀,那么第26行得到的變量ret的值就為false,因此,第29行就會跳出第22行到第42行的while循環,準備讀取日志緩沖區結構體log中的日志記錄。 如果日志緩沖區結構體log中沒有日志記錄可讀,即第26行得到的變量ret為true,那么當前進程就有可能需要進入睡眠狀態,直到日志緩沖區結構體log中有新的日志記錄可讀為止。當前進程是通過調用第41行的函數schedule來請求內核進行一次進程調度,從而進入睡眠狀態的。但是有一種特殊情況——當前進程是以非阻塞模式來打開日志設備文件,即第31行的if語句為true時,當前進程就不會因為日志緩沖區結構體log中沒有日志記錄可讀而進入睡眠狀態,它直接返回到用戶空間中。一般來說,當驅動程序決定使當前進程進入睡眠狀態之前,要先通過調用函數signal_pending來檢查當前進程是否有信號正在等待處理。如果有的話,即第36行的if語句為true,那么這時候驅動程序就不能使當前進程進入睡眠狀態,而必須要讓它結束當前系統調用,以便它可以立即返回去處理那些待處理信號。 如果日志緩沖區結構體log中有日志記錄可讀,那么Logger日志驅動程序就會跳出第22行到第42行的while循環。但是執行到第51行時,又會再次重新判斷日志緩沖區結構體log的成員變量w_off 和日志讀取進程結構體reader的成員變量r_off是否相等。這是因為在執行第48行代碼來獲取互斥量log->mutex時,可能會失敗。在這種情況下,當前進程就會進入睡眠狀態,直到成功獲取互斥量log->mutex為止。在當前進程的睡眠過程中,日志緩沖區結構體log中的日志記錄可能已經被其他進程訪問過了,即log->w_off的值可能已經發生了變化。因此,第51行需要重新判斷日志緩沖區結構體log的成員變量w_off 和日志讀取進程結構體reader的成員變量r_off是否相等。 第51行再次確認日志緩沖區結構體log中有新的日志記錄可讀之后,接著第57行就調用函數get_entry_len來得到下一條要讀取的日志記錄的長度。 **kernel/goldfish/drivers/staging/android/logger.c** ~~~ /* * 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; } ~~~ 在4.2.1小節中介紹日志記錄結構體logger_entry時提到,每一條日志記錄都是由兩部分內容組成的,其中一部分內容用來描述日志記錄,即結構體logger_entry本身所占據的內容;另一部分內容是日志記錄的有效負載,即真正的日志記錄內容。由于結構體logger_entry的大小是固定的,因此只要知道它的有效負載長度,就可以通過計算得到一條日志記錄的長度。日志記錄結構體logger_entry的有效負載長度記錄在它的成員變量len中。由于成員變量len是日志記錄結構體logger_entry的第一個成員變量,因此,它們的起始地址是相同的。又由于日志記錄結構體logger_entry的成員變量len的類型為__u16,因此,只要讀取該結構體變量地址的前兩個字節的內容就可以知道對應的日志記錄的長度。我們知道,日志緩沖區是循環使用的,因此,一條日志記錄的前兩個字節有可能分別保存在日志緩沖區的首尾字節中。因此,在計算一條日志記錄的長度時,需要分兩種情況來考慮。第一種情況是該日志記錄的前兩個字節是連在一起的;第二種情況就是前兩個字節分別保存在日志緩沖區的首字節和尾字節中。 在函數get_entry_len中,第11行將日志緩沖區結構體log的總長度size減去下一條要讀取的日志記錄的位置off。如果得到的結果等于1,那么就說明該日志記錄的長度值分別保存在日志緩沖區的首尾字節中,因此,第13行和第14行就將它的前后兩個字節從日志緩沖區的尾字節和首字節中讀取出來,并且將它們的內容組合在變量val中。如果得到的結果不等于1,那么就說明該日志記錄的長度值保存在兩個連在一起的字節中,即保存在地址log->buffer + off中,因此,第17行就直接將日志記錄的長度值讀取出來,并且保存在變量val中。最后,第20行將變量val的值加上結構體logger_entry的大小,就得到下一條要讀取的日志記錄的總長度了。 回到logger_read函數中,第64行調用函數do_read_log_to_user執行真正的日志記錄讀取操作。 **kernel/goldfish/drivers/staging/android/logger.c** ~~~ /* * do_read_log_to_user - reads exactly 'count' bytes from 'log' into the * user-space buffer 'buf'. Returns 'count' on success. * * Caller must hold log->mutex. */ 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; } ~~~ 由于一條日志記錄的內容有可能同時位于日志緩沖區的末尾和開頭處,即它在地址空間上是不連續的,因此,函數do_read_log_to_user就有可能需要分兩次來讀取它。第19行計算第一次要讀取的日志記錄的長度,接著第20行就將這一部分的日志記錄拷貝到用戶空間緩沖區buf中。第27行檢查上一次是否已經將日志記錄的內容讀取完畢,如果未讀取完畢,即第27行的if語句為true,那么第28行就會繼續將第二部分的日志記錄拷貝到用戶空間緩沖區buf中,這樣就完成了一條日志記錄的讀取。 > 日志記錄結構體log的成員變量buffer指向的是一個內核緩沖區。在將一個內核緩沖區的內容拷貝到一個用戶空間緩沖區時,必須要調用函數copy_to_user來進行,因為用戶空間緩沖區的地址有可能是無效的,而函數copy_to_user在拷貝內容之前會對它進行檢查,避免訪問非法地址。 當前進程從日志緩沖區結構體log中讀取了一條日志記錄之后,就要修改它的成員變量r_off的值,表示下次要讀取的是日志緩沖區結構體log中的下一條日志記錄。第31行首先將日志讀取進程結構體reader的成員變量r_off的值加上前面已經讀取的日志記錄的長度count,然后再使用宏logger_offset來對計算結果進行調整。 **kernel/goldfish/drivers/staging/android/logger.c** ~~~ /* logger_offset - returns index 'n' into the log via (optimized) modulus */ #define logger_offset(n) ((n) & (log->size - 1)) ~~~ 這是由于日志緩沖區是循環使用的,如果計算得到的下一條日志記錄的位置大于日志緩沖區的長度,那么就需要將它繞回到日志緩沖區的前面。 > 日志緩沖區的長度是2的N次方,因此,只要它的值減1之后,再與參數n執行按位與運算,就可以得到參數n在日志緩沖區中的正確位置。 至此,日志記錄的讀取過程就介紹完了。接下來,我們繼續分析日志記錄的寫入過程。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看