最近看apue,看到了fread函式,就把之前想分析的乙個函式借這個機會研究一下。
先寫個程式,呼叫一下fread函式。
#include int main()
好了,還是咱們的老套路,通過gdb可以直接定位到fread的原始碼,原始碼如下,位於./libio/iofread.c。
_io_size_t
_io_fread (void *buf, _io_size_t size, _io_size_t count, _io_file *fp)
libc_hidden_def (_io_fread)
#ifdef weak_alias
weak_alias (_io_fread, fread)
# ifndef _io_mtsafe_io
strong_alias (_io_fread, __fread_unlocked)
libc_hidden_def (__fread_unlocked)
weak_alias (_io_fread, fread_unlocked)
# endif
#endif
還是一樣的套路,弱符號機制,不管該函式最後的匯出名是什麼,我們的注意力都聚焦到了_io_fread,這個函式就是fread的函式體。一行一行的分析。
先看頭兩個變數:
_io_size_t bytes_requested = size * count;
_io_size_t bytes_read;
通過變數名基本可以確定,「bytes_requested」應該就是要讀入的位元組數,而「bytes_read」我估計是已經讀出的位元組數,這一點可通過我們接下來的原始碼分析進行驗證。
check_file (fp, 0);
這個看起來像函式呼叫的語句,我估計又是通過巨集實現的,先在標頭檔案裡找一下。
果然沒錯:
# define check_file(file, ret) coerce_file (file)
# define coerce_file(file) \
(((file)->_io_file_flags & _io_magic_mask) == _old_magic_mask \
&& (file) = *(file**)&((int*)fp)[1])
在分析這麼乙個簡單的巨集函式過程中,我就遇到了非常多的問題,首先是第一點:
(file)->_io_file_flags
#define _io_file_flags _flags
不過這也不對頭,這個define的作用也是把「_io_file_flags」換成「_flags」。
關於這個問題暫且先存下吧,也許是咱們之前的某些分析不對,導致這一步理解的有問題。
((file)->_io_file_flags & _io_magic_mask)
這一句倒是沒什麼問題了,就是當前的檔案標誌與_io_magic_mask做與運算。_io_magic_mask的定義位於./libio/libio.h,值為0xffff0000,這一步的作用就是把所有檔案標誌位關閉,然後保留io的魔數,這個io魔數的值為0xfbad0000,定義同樣位於./libio/libio.h。但_old_magic_mask的值我就沒有找到了。不過這一步的結果也比較明確不是0就是1。
第二句的功能我根本就沒有分析出來。
&((int*)fp)[1])
首先將fp轉化為int指標(此處的fp應該是某個全域性變數),然後再取fp指標所在位址的第一項的位址。此時fp指標是乙個int**型別的指標。
*(file**)&((int*)fp)[1])
再轉化為file**型別,再取其第乙個元素,對於這一系列操作的功能,我實在不能理解其深意,不過file指標結果還是兩個不是指向乙個位址,就是為null,與上一步的結果進行與運算,得到的結果不是1就是0,但我們的程式中並沒有利用這一結果。
所以這一語句的功能我就不得而知了。
if (bytes_requested == 0)
return 0;
沒什麼說的,如果需要讀入的位元組為0,則直接返回。
_io_acquire_lock (fp);
bytes_read = _io_sgetn (fp, (char *) buf, bytes_requested);
_io_release_lock (fp);
這才是fread函式的主要功能,獲取與釋放所就不說了,直奔主題:_io_sgetn函式。
_io_size_t
_io_sgetn (_io_file *fp, void *data, _io_size_t n)
libc_hidden_def (_io_sgetn) //定義位於./libio/genops.c
#define _io_xsgetn(fp, data, n) jump2 (__xsgetn, fp, data, n) //定義位於./libio/libiop.h
#define jump2(func, this, x1, x2) (_io_jumps_func(this)->func) (this, x1, x2) //定義位於./libio/libiop.h
相關的定義查詢至此已經可以進行分析了,關於「_io_jumps_func」,定義太複雜了,就不展開來看了,通過這一巨集定義的基本形式可以推斷func就是實際執行功能的函式。
所以又回到了「__xsgetn」這個函式上,在libiop.h有乙個結構體的定義如下:
struct _io_jump_t
;
其中有關於i/o操作的定義,jump_field的功能比較簡單:
#define jump_field(type, name) type name
現在就需要看看_io_xsgetn_t的型別了。定義如下:
typedef _io_size_t (*_io_xsgetn_t) (_io_file *fp, void *data, _io_size_t n);
原來是乙個函式指標,至此我們的線索已經全部用完了,直接搜尋「_io_xsgetn_t」與「__xsgetn」已無法得到什麼有效結果了。
不過可通過「_io_xsgetn_t」的注釋略知一二。
/* the 'xsgetn' hook reads upto n characters into buffer data.
returns the number of character actually read.
it matches the streambuf::xsgetn virtual function. */
xsgetn函式的功能就是讀n個位元組到data中,並返回實際讀的位元組數。
到此就算是我們的fread函式的功能分析完了吧,其實肯定沒有分析完,fread在底層上一定是通過read實現功能的,而read是通過檔案描述符對檔案進行標記,因此這之間肯定還涉及引數的轉換問題,這一功能我們已知可以通過fileno實現。
好了fread功能的實現就先分析到這裡。
waitpid函式原始碼
int sys waitpid pid t pid,unsigned long stat addr int options 掛起當前程序,直到pid指定的子程序退出終止或者收到要求終止該程序 的訊號 或者需要呼叫 乙個訊號處理函式 elseif pid elseif pid 1 以下的語名定為要找到...
strtok函式原始碼
今天用到strtok時,總感覺怪怪的,為啥第二次呼叫第乙個引數要用null,難道是函式內部儲存了當前的狀態,假如這樣的話,那就不能對多個串交叉呼叫strtok了,而且儲存這個狀態的不是全域性變數就是static變數。於是看了他的原始碼,有幾個不同的版本,思想是一樣的。感覺寫的挺巧妙的,深深的折服這些...
PHP explode 函式原始碼分析
在php 開發中,我們經常需要把乙個字串分割成陣列,這時候我們經常會使用 php explode 函式來幫我們實現。這篇文章我們主要來學學 explode 函式是怎麼工作的。檔案1 ext standard string.c explode的源 php function explode if del...