linux內核追蹤——find_next_bit函數詳詳詳解

寫在前面算法

宗旨:把話說清楚,把道理講透徹。數組

約定:全部代碼均來自Linux內核2.6.24版。數據結構

建議:本文介紹得十分詳細,但也略顯繁瑣,讀者能夠先看「Ⅴ.總結」部分帶註釋的源碼,若是哪裏不清楚,再回頭看詳細解釋。函數

正文spa

預備知識指針

位圖:code

在Linux下,從數據結構上看,位圖本質上是一個數組,數組的每一個元素都是long型的(即32bit或64bit)。blog

假設在32位系統下,某long型數組有128個元素,那麼,從邏輯上看,這個數組就是一個128行×32列的bit陣列,就是所謂的位圖,見下面的示意圖。索引

               

上圖中的數字就是各個bit位的標號,即索引。進程

對於位圖的操做,也就是對位圖中bit位的操做。

從做用上說,位圖一般與其它數據相關聯,用位圖中的bit位對該數據進行統計或管理。

例如,在文件系統中,每一個進程都有一個元素爲file指針的數組(爲表述方便,後面稱之爲數組A),同時,也有一個位圖,位圖中有效bit位(何爲有效bit位,後面會詳述)的個數與數組A中元素的個數相同。當進程打開一個文件時,要先在位圖中找一個爲0的bit位,而後將該位置1,返回該bit位的索引fd。當內核建立了與要打開文件對應的file實例後,會使數組A中索引爲fd的元素(注意,前邊說過,該元素是一個file類型的指針)指向該file實例。這裏的索引fd,就是咱們日常所說的文件描述符。

正題

Ⅰ.源碼

先給出find_next_bit的源碼(源碼來自內核2.6.24,位置:/lib/find_next_bit.c)

 1 unsigned long find_next_bit(const unsigned long *addr, unsigned long size,
 2         unsigned long offset)
 3 {
 4     const unsigned long *p = addr + BITOP_WORD(offset);
 5     unsigned long result = offset & ~(BITS_PER_LONG-1);
 6     unsigned long tmp;
 7 
 8     if (offset >= size)
 9         return size;
10     size -= result;
11     offset %= BITS_PER_LONG;
12     if (offset) {
13         tmp = *(p++);
14         tmp &= (~0UL << offset);
15         if (size < BITS_PER_LONG)
16             goto found_first;
17         if (tmp)
18             goto found_middle;
19         size -= BITS_PER_LONG;
20         result += BITS_PER_LONG;
21     }
22     while (size & ~(BITS_PER_LONG-1)) {
23         if ((tmp = *(p++)))
24             goto found_middle;
25         result += BITS_PER_LONG;
26         size -= BITS_PER_LONG;
27     }
28     if (!size)
29         return result;
30     tmp = *p;
31 
32 found_first:
33     tmp &= (~0UL >> (BITS_PER_LONG - size));
34     if (tmp == 0UL)
35         return result + size;
36 found_middle:
37     return result + __ffs(tmp);
38 }

Ⅱ.功能——參數——返回值

功能:在addr指向的位圖中,從索引爲offset的bit位(包括該位)開始,找到第一個爲1的bit位,返回該位的索引。

參數:

@addr:位圖(數組)的起始地址。
@size:位圖的大小,即位圖中有效bit位的個數。注意,Linux內核實際調用該函數時,該參數的值不必定是32的整數倍(32位系統下)。假設構成位圖的數組大小爲3,即一共有96個bit,但函數調用時,參數size可           能是90,那麼,從邏輯上說,數組最後一個元素的最後6位是不參與構成位圖的,即它們不是位圖的組成部分,是「無效」的;而前邊的90個bit共同構成了位圖,它們是「有效」的。注意,後面解釋中常常會用             到「有效位」和「無效位」的概念,對此,讀者必定要理解清楚。

@offset:查找起點。即從位圖中索引爲offset的位(包括該位)開始,查找第一個爲1的bit位,offset以前的bit位不在搜索範圍以內。「查找起點」這個概念在後面的敘述中常常會用到,但願讀者能理解清楚。

返回值:找到的bit位的索引。

Ⅲ.BITS_PER_LONG和BITOP_WORD

3.1 BITS_PER_LONG

顧名思義,BITS_PER_LONG是指一個long型數據中bit位的個數,看源碼

/*include/asm-x86/types.h*/

#ifdef CONFIG_X86_32
# define BITS_PER_LONG 32
#else
# define BITS_PER_LONG 64
#endif

可見,32位系統下,它是32;64位系統下,它是64。

3.2 BITOP_WORD

很少說,直接看源碼

/*lib/find_next_bit.c*/

#define BITOP_WORD(nr)        ((nr) / BITS_PER_LONG)

就是參數nr除以32或64。

Ⅳ.一句一句解釋

注:在後面的全部解釋中,咱們按32位系統講解,即BITS_PER_LONG取32

 a.

const unsigned long *p = addr + BITOP_WORD(offset);

該句的做用是使p指向數組中索引爲offset的bit位(即查找起點)所在的元素。

咱們知道,位圖的0~31位在數組第0個元素中, 32~63位在第1個元素中,等等(如圖1)。假設offset=33,則BITOP_WORD(offset)就是33/32,結果爲1,那addr + BITOP_WORD(offset)就是addr+1,即指向數組第1個元素,就是索引爲33的bit位所在的元素。

b.

unsigned long result = offset & ~(BITS_PER_LONG-1);

BITS_PER_LONG-1就是31,二進制形式就是0001 1111(這裏爲了表述方便,採用8位二進制),再取反就是1110 0000,即低5位全0,剩下的都是1。假設offset=70D=0100 0110B,那麼,

offset & ~(BITS_PER_LONG-1)就是0100 0110 & 1110 0000 =0100 0000=64。其實,就是將offset的低5位置0,高位保持不變。這個結果有什麼意義呢?其實就是:索引爲offset的bit位所在的元素前的元素中bit位的個數,以下圖所示。很顯然,這些bit位都位於查找起點的前面,即它們都不在搜索範圍以內,咱們能夠將它們理解爲「已處理」的bit位。請讀者牢記,在find_next_bit函數中,result老是表示「已處理」的bit位總數,而且,很顯然,它必定是32的整數倍。

                               

c.

if (offset >= size)
    return size;

若是查找的起始位置offset大於等於位圖的總大小,直接返回位圖大小。這裏解釋一下問什麼要有等於,假設offset=size=64,但實際上,這種狀況下,合法的索引是0~63,因此,值爲64的offset不合法。問題的根源在於offset是從0算起的,而size是從1算起的。另外,我的認爲這兩句應該放在函數體的最前面,這樣一來,只要if條件成立,就直接返回,後面的工做就都不用作了。而按照源碼的寫法,是不管如何都要執行前兩句的,但若是此時if條件成立,前邊的工做就白作了,這不是一個高效的安排。

d.

size -= result;
offset %= BITS_PER_LONG;

注意,從這兩句開始,size和offset的含義和傳參時的含義就不一樣了

size-=result就是size=size-result,前邊說過,此時的result表示查找起點所在數組元素前的元素中bit位的個數,就是「已處理」的bit位個數,咱們要找的bit位必定在這後邊;而size表示位圖總位數。二者相減的結果,就是「未處理的、待查找的」bit位個數。請讀者牢記,此後的代碼中,result表示「已處理」的bit位總數,而size表示「待處理」的bit位總數,二者的加和,必定等於傳參時size的值。

offset %= BITS_PER_LONG執行以後的offset也再也不表示查找起點的索引,而表示查找起點在它所在的元素中是第幾個。假設原來offset=32,則執行該句以後,offset的值變成了0,而從前面的示意圖中咱們能夠看到,索引爲32的bit位正是在數組的第1個元素(從0算起)的第0位(從0算起)。因此,咱們能夠這樣說,代碼執行前,offset是查找起點在整個位圖中的索引;代碼執行後,offset是查找起點在它所在的數組元素中的索引。

e.

if (offset) {
    tmp = *(p++);
    tmp &= (~0UL << offset);
    if (size < BITS_PER_LONG)
        goto found_first;
    if (tmp)
        goto found_middle;
     size -= BITS_PER_LONG;
     result += BITS_PER_LONG;
    }

進入這個if的條件是offset不爲0,經過前面的分析,就是查找起點不在所在數組元素的首位(咱們前邊舉的例子就是在首位的狀況)。好了,開始分析裏面的代碼,看看這個if作了什麼。

e_1

tmp = *(p++);

這句獲得的是查找起點所在的數組元素的值,也就是那32個bit。tmp是unsigned long型的,在文章開頭給出的源碼的第6行定義。注意:

1.表達式(p++)獲得的是p的值(後置++),以後指針p再自增1,指向下一個數組元素。
2.tmp只是構成位圖的數組元素的拷貝而不是數組元素自己,即,對tmp所進行的任何修改,都不會影響到原位圖

e_2

tmp &= (~0UL << offset);

首先,0UL就是unsigned long型的數字0,從二進制的角度看,就是32位全0;而後,對它取反,就是32位全1;接着,再左移offset位,假設offset=3,移位後就是1111 1000(爲表述方便,這裏只寫8位);最後,再和tmp(即查找起點所在的數組元素的值)相與,結果就是tmp的低3位置0,其他位保持不變。爲何要這麼作呢?由於offset=3,它前面的第2位、第1位、第0位其實都不在搜索範圍以內,因此,咱們將它們置爲0(固然,e_1中就說過,這只是在拷貝上操做,不會影響到原位圖),這主要是爲後面的工做作準備(讀者看到e_4小節就會天然明瞭)。因此,這句代碼的用意是:在查找起點所在的元素中,將查找起點前的bit位置0。代碼之因此要將查找起點是否在數組元素的第0位區別對待,就是由於若查找起點不在數組元素首位,須要將查找起點前的bit位置0。

e_3

if (size < BITS_PER_LONG)
        goto found_first;

經過前面的分析,咱們知道,此時的size表示的是「待處理」的bit位總數,而如今,這個數字小於32,這說明:

1.函數調用時傳入的參數不是32的整數倍,構成位圖的數組的最後一個元素中含有「無效位」,這一點,在講解函數參數時就詳細解釋過;
2.查找起點就在數組的最後一個元素中!

上面的圖2就展現了這種狀況,假設函數調用時傳入的參數size是86,經過d中的分析,咱們知道,此時size的值是86-64=22,知足if中的條件,而顯然,這種狀況下,查找起點(索引爲70的bit位)正是在最後一個數組元素中。
在這種狀況下,代碼要轉到found_first處。咱們繼續跟蹤,看看found_first幹了些什麼。

e_4

found_first:
    tmp &= (~0UL >> (BITS_PER_LONG - size));
    if (tmp == 0UL)    
        return result + size;    

首先,咱們要知道,代碼能走到這裏,存在兩個前提:

1.查找起點在數組的最後一個元素中
2.最後一個元素中存在「無效位」

經過e_3中的分析,咱們知道,此時的size表示的是「待處理」的bit位的個數,同時,它也表示最後一個元素中「有效位」的個數。那麼,很顯然,BITS_PER_LONG-size就是「無效位」的個數,爲了表述方便,咱們假設該值位3,那麼~0UL >> (BITS_PER_LONG - size)獲得的就是這樣的32個bit位:最左3位爲0,其他位爲1。而後,這32個bit位再與tmp(即最後一個數組元素的拷貝)相與,結果就是:將該元素中的「無效位」都置爲0了(注意,對32位的long型數據來講,低位、索引小的位在右,高位、索引大的位在左,因此,「無效位「必定是在最左邊的)!

回顧一下e_2,在e_2中,將元素中查找起點以前的位都置成了0,而如今,又將「無效位」都置成了0,以下圖

                 

那麼,剩下的是什麼呢?剩下的就是查找範圍,即咱們要在上圖中的白色部分找第一個位1的bit位。

如今,讓咱們思考這樣一個問題:若是此時最後一個數組元素,即tmp的值爲0(即if (tmp == 0UL)),說明了什麼呢?細思,極恐,這說明查找範圍內全是0!由於紅色部分和黃色部分早就置爲0了,而如今整個元素的值爲0,那結論只有一個——白色部分全是0!

而咱們可能會立刻想到這樣一個不幸事實:這已是位圖中的最後一個數組元素了!

因而,咱們就會獲得這樣一個使人絕望的結論:以函數參數offset爲查找起點,在當前位圖中找到一個值爲1的bit位,已經不可能了!

在這種狀況下,函數只好無奈地return result + size了,d中說過,result + size的值,必定等於傳參時size的值,即數組的總大小。

這,就是e_4中代碼的含義。

那麼,若是咱們的運氣沒那麼差,if (tmp == 0UL)沒有被執行,也就是說,白色部分有1存在!進而咱們就會欣喜地想到:這樣,就必定能找到知足條件的bit位!那麼,具體怎麼找呢?看了最初的源碼咱們會發現,代碼走到了found_middle標籤下。好吧,讓咱們來看看found_middle下有什麼。

e_5

found_middle:
    return result + __ffs(tmp);

 這裏出現了一個新的函數__ffs,它定義在include/asm-generic/bitops/__fss.h中,咱們先來看一下這個函數

 1 /**
 2  * __ffs - find first bit in word.
 3  * @word: The word to search
 4  *
 5  * Undefined if no bit exists, so code should check against 0 first.
 6  */
 7 static inline unsigned long __ffs(unsigned long word)
 8 {
 9     int num = 0;
10 
11 /*若是BITS_PER_LONG等於64,要先對低32位進行檢查;不然(即BITS_PER_LONG等於32),直接對低16位進行檢查*/
12 #if BITS_PER_LONG == 64
13     if ((word & 0xffffffff) == 0) {   //若是與0xffffffff相與得0,說明word的低32位全爲0
14         num += 32;   //索引加32
15         word >>= 32;   //word右移32位,把低32位的0移走
16     }
17 #endif
18     if ((word & 0xffff) == 0) {
19         num += 16;
20         word >>= 16;
21     }
22     if ((word & 0xff) == 0) {
23         num += 8;
24         word >>= 8;
25     }
26     if ((word & 0xf) == 0) {
27         num += 4;
28         word >>= 4;
29     }
30     if ((word & 0x3) == 0) {
31         num += 2;
32         word >>= 2;
33     }
34     if ((word & 0x1) == 0)
35         num += 1;
36     return num;
37 }

該函數的功能是在參數word中找到第一個值爲1的bit位,返回該位的索引。能夠看到,該函數沒有對參數word=0的狀況(這意味着不可能在word中找到值爲1的bit位)進行檢查,因此咱們應該在調用該函數前對傳入的參數是否爲0進行檢查,這就是上面代碼中第5行的英文註釋的意思,而該函數的調用者find_next_bit在調用該函數前已經進行過檢查了,正如咱們在e_4中分析的那樣。

該函數的算法仍是很巧妙的,它相似於「折半查找」。假設參數word是32位的,先執行word & 0xffff,析取低16位,這時可能出現兩種結果:

1.若是結果爲0,說明低16位(0~15位)全爲0,那麼,可能爲1的bit位最小是第16位,因此num += 16,而且,執行word >>= 16,將全0的低16位移走,這樣,可能有1存在的高16位變成了低16位;接着,如法炮製,word與0xff相與,對剩下的16位進行「折半查找」。
2.若是結果不爲0,說明低16位中有1存在,因爲咱們是要找第一個爲1的bit位,因此,高16位就不用看了,直接在低16位中尋找,因此,下一步就是執行word & 0xff,對低16位進行「折半查找」。

可見,不管word & 0xffff的結果是否爲0,word & 0xff都是要執行的,只不過,若是word & 0xffff結果爲0,須要進行增長計數和右移的工做。

按照上面的步驟,逐步進行「折半查找」,最終就能獲得word中第一個爲1的bit位的索引。讀者能夠本身舉個例子,手動執行一下__ffs函數,就更加清楚了,這裏再也不贅述。

有一點你們要意識到,find_next_bit在調用__ffs時傳入的參數是tmp,而在e_4中咱們看到,tmp中查找起點前的bit位和「無效位」都被置爲0了,而且,也已經判斷出tmp不爲0,因此,__ffs必定能找到爲1的bit位且保證該bit位在合法的搜索空間(圖3的白色區域)內

如今咱們再來看found_middle下的那句return result + __ffs(tmp)。__ffs(tmp)獲得的是tmp中自查找起點起第一個爲1的bit位的索引,而result表示的是位圖中tmp以前的全部元素中bit位的總和,因此,二者相加,就是咱們要找的bit位在位圖中的索引,即find_next_bit的最終結果,因而,將這個結果return。

至此,find_next_bit的查找工做就結束了,可是,咱們對find_next_bit的分析還沒結束。不知讀者是否還記得,咱們是從e_3中if (size < BITS_PER_LONG)成立這個「路口」進入,一路追蹤,才走到這裏來的。因此咱們還要分析if (size < BITS_PER_LONG)不成立的狀況,因而,讓咱們再次回到最初的源碼……

e_6

if (tmp)
    goto found_middle;

若是if (size < BITS_PER_LONG)不成立,就會執行上面的代碼。if (size < BITS_PER_LONG)不成立,說明了什麼呢?這說明tmp不是數組的最後一個元素,所以就不可能有「無效位」,也就不存在將「無效位」置爲0的問題(即不用goto found_first了),又由於,在e_4中,已經將查找起點前的bit位都置0了,因此,這裏直接判斷tmp是否爲0,如不爲0,說明在這個tmp中必定能找到爲1的bit位,因此,轉到found_middle,找到第一個爲1的bit位在位圖中的索引,而後返回結果。

那麼,若是這裏的tmp爲0呢,該執行什麼樣的代碼,咱們往下看。

e_7

size -= BITS_PER_LONG;
result += BITS_PER_LONG;

若是tmp爲0,說明在當前數組元素中不可能找到爲1的bit位,因而須要在下一個數組元素中尋找,在此以前,將「未處理」bit位總數減去32,將「已處理」bit位總數增長32。有讀者可能會問:怎麼沒見指針後移呢?不要忘了,在e_1中,這個工做已經作過了

至此,整個e中的代碼就都分析完了 。可是,革命還沒有成功,喘口氣,咱們還得往下看。

f

while (size & ~(BITS_PER_LONG-1)) {
    if ((tmp = *(p++)))
        goto found_middle;
    result += BITS_PER_LONG;
    size -= BITS_PER_LONG;
}

首先要明白,有兩種狀況代碼會走到這裏:

1.這個while循環上面的if(offset)(就是e中的代碼)條件不成立(即查找起點位於數組元素的第0位),if中的語句體沒有被執行。

2.if(offset)的語句體被執行了,但該語句體內部的兩個if條件都不成立,這又分爲兩種狀況:

   1.查找起點所在的數組元素不是數組的最後一個元素,而且,該元素中全是0,沒有1。
   2.查找起點是數組最後一個元素,但該元素中沒有「無效位」,而且,該元素中全是0,沒有1。

在上面的兩種狀況下,咱們就要執行上面的while循環,在後面的數組元素中依次查找。

咱們先來看一下循環進入條件

while (size & ~(BITS_PER_LONG-1))

咱們將BITS_PER_LONG換成32,並轉換爲二進制形式,上面的語句就變成了while(size & 1110 0000),這是什麼呢?其實就是while(size>=32)!由於小於32(0010 0000B)的無符號數(注意size是unsigned long型,請看最初的源碼)與1110 0000相與的結果都是0000 0000,而大於等於32的無符號數與1110 0000相與的結果都將大於0010 0000。好了,如今咱們知道了,上面那句故弄玄虛的while語句其實就是while(size>=32)

好了,如今咱們來看循環體。先看第一句

if ((tmp = *(p++)))

這句代碼依次作了三件事:

1.將p指向的數組元素(固然也是咱們要進行查找的數組元素)複製到tmp
2.p指針自增,指向下一元素
3.判斷tmp是否爲0

這裏須要解釋一下:
1.若是查找起點在一個數組元素的第0位,那麼,e中的if語句體就不會被執行,也就不會執行到if語句體中的tmp = *(p++),因此當第一次進入while循環並執行if ((tmp = *(p++)))時,tmp就是查找起點所在的數組元素的拷貝。
2.若是查找起點不在一個數組元素的第0位,e中的if語句體就會被執行,當第一次進入while循環並執行if ((tmp = *(p++)))時,tmp就是if語句體中那個tmp對應的元素的下一個元素的拷貝。

如今讓咱們在總體看一下這個循環體

if ((tmp = *(p++)))
    goto found_middle;
result += BITS_PER_LONG;
size -= BITS_PER_LONG;

若是tmp不爲0,說明咱們要找的爲1的bit位必定在這個tmp中,因而轉到found_middle,進行查找並返回結果,find_next_bit函數結束;若是tmp等於0,說明該元素中不含1,將「已處理」位數增長32,「待處理」位數減小32,而後判斷循環條件(「待查找」位數是否大於32),若條件成立,進入循環體,對下一個數組元素進行查找,不然,退出循環。

若是上面的while循環是正常退出(即因爲size小於32而退出)的,那就說明整個整個while循環都沒找到爲1的bit位(不然,代碼將從循環體轉到found_middle,while循環將不會正常退出)。若是是這種狀況,接下來該怎麼辦呢?咱們仍是繼續看源碼吧。

g

    if (!size)
        return result;
    tmp = *p;

found_first:
    tmp &= (~0UL >> (BITS_PER_LONG - size));
    if (tmp == 0UL)
        return result + size;
found_middle:
    return result + __ffs(tmp);

首先要明白,代碼走到這裏,有四種狀況:

1.查找起點位於數組的最後一個元素中,且位於該元素的第0位,同時,該元素含有「無效位」,即傳入的參數size不是32的整數倍,而且,執行完d中的代碼後,size小於32(這種狀況容易被忽略)。在這種狀況下,e中的if語句和f中的while循環都不會被執行,代碼直接從d處走到g處。
2.代碼執行完e中的if後再也不知足while循環的條件,直接走到g中代碼處。這種狀況是:查找起點在倒數第二個元素中且不位於第0位且該元素32位全是0,同時,最後一個數組元素中有「無效位」(傳入參數size不是32的整數倍)。
3.while循環因爲size=0而退出(傳入參數剛好是32的整數倍),這種狀況下,整個位圖都查找過了且並無找到爲1的bit位,換句話說,不可能再找到了。
4.while循環因爲size介於1~31之間而退出,即數組的最後一個元素中有「無效位」(傳入參數size不是32的整數倍)。

下面讓咱們看看,在這四種狀況下,代碼是怎麼作的:

首先,代碼先判斷size是否等於0,若等於0,直接返回result,這正對應了上面的狀況3。注意,咱們前面就說過,size+result的值老是等於傳參時size的值(位圖總大小),而此時size=0,因此,返回result,就是返回位圖總大小。

若是size不等於0,那就對應一、二、4三種狀況,這三種狀況的共同點是:都須要在數組的最後一個元素中查找,而且該元素中含有「無效位」。在這種情形下,find_next_bit函數的處理是:

1.將最後一個數組元素拷貝到tmp
2.在found_first中,將「無效位」置0,檢驗tmp是否爲0,如果,返回位圖總大小,不然,進入found_middle
3.在found_middle中,找到tmp中第一個爲1的bit位的索引,返回該位在位圖中的索引

至此,find_next_bit函數結束。

Ⅴ.總結

至此,咱們對find_next_bit函數的分析就所有完成了。咱們能夠看到:

1.若函數查找成功,返回自查找起點起第一個爲1的bit位的索引;若查找失敗,返回位圖總大小。
2.find_next_bit函數只是在位圖中進行查找,自始至終都沒有對位圖進行任何修改

最後,咱們給出find_next_bit函數源碼的簡要註釋版,做爲最後的總結梳理

/*
 *@addr:位圖首地址
 *@size:位圖總大小
 *@offset:查找起點
 */
unsigned long find_next_bit(const unsigned long *addr, unsigned long size,
        unsigned long offset)
{
    /*找到查找起點所在數組元素的地址*/
    const unsigned long *p = addr + BITOP_WORD(offset);
    /*計算查找起點所在數組元素前的數組元素中bit位的總個數,即「已處理」bit位個數*/
    unsigned long result = offset & ~(BITS_PER_LONG-1);
    unsigned long tmp;

    /*若是查找起點大於等於位圖大小,返回位圖大小*/
    if (offset >= size)
        return size;
    /*size:「未處理」bit位個數*/
    size -= result;
    /*offset:查找起點在所在數組元素中的索引*/
    offset %= BITS_PER_LONG;
    /*若是查找起點不在數組元素中的第0位*/
    if (offset) {
        /*拷貝元素*/
        tmp = *(p++);
        /*將查找起點前的bit位都置0*/
        tmp &= (~0UL << offset);
        /*若是查找起點位於最後一個數組元素且該元素含有「無效位」*/
        if (size < BITS_PER_LONG)
            goto found_first;
        /*若是tmp不爲0,必定能找到*/
        if (tmp)
            goto found_middle;
        /*「待查找」bit位總數減小32,「已查找」bit位總數增長32*/
        size -= BITS_PER_LONG;
        result += BITS_PER_LONG;
    }
    /*while(size>=32)*/
    while (size & ~(BITS_PER_LONG-1)) {
        /*拷貝元素,若不爲0,必定能找到*/
        if ((tmp = *(p++)))
            goto found_middle;
        /*「待查找」bit位總數減小32,「已查找」bit位總數增長32*/
        result += BITS_PER_LONG;
        size -= BITS_PER_LONG;
    }
    /*若size=0,返回位圖總大小*/
    if (!size)
        return result;
    /*不然,拷貝最後一個數組元素*/
    tmp = *p;

found_first:
    /*將元素中「無效位」都置爲0*/
    tmp &= (~0UL >> (BITS_PER_LONG - size));
    /*若tmp爲,不可能再找到,直接返回位圖總大小*/
    if (tmp == 0UL)
        return result + size;
found_middle:
    /*走到這裏,就必定能找到,查找,返回結果*/
    return result + __ffs(tmp);
}

寫在後面

自認爲寫得很詳細了,但在下才疏學淺,錯誤疏漏之處在所不免,懇請廣大讀者批評指正,您的批評指正是在下前進的不竭動力!

相關文章
相關標籤/搜索