glibc fread函式原始碼剖析

2021-07-15 02:38:02 字數 3802 閱讀 6821

最近看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...