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


寫在前面

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

約定:所有代碼均來自Linux內核2.6.24版。

建議:本文介紹得十分詳細,但也略顯繁瑣,讀者可以先看“Ⅴ.總結”部分帶注釋的源碼,如果哪里不清楚,再回頭看詳細解釋。

正文

預備知識

位圖:

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

假設在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,那就對應1、2、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);
}

寫在后面

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM