1. 程式人生 > >linux核心V2.6.11學習筆記(1)--pid點陣圖

linux核心V2.6.11學習筆記(1)--pid點陣圖

開始系統的學習linux核心了,手頭的參考書是<<深入理解linux核心>>第三版,裡面是基於2.6.11版來講解的,所以我這裡的筆記也是基於這個版本.我的目的是將該書中我覺得講的不太詳細或者可以展開討論理解的地方寫出來供別人參考.計劃三個月內精讀完該書,爭取每週更新約三次筆記.

其它的參考資料還有:
<<深入理解計算機系統>>
<<linux核心情景分析>>上冊
<<Linux核心設計與實現>>
<<Linux核心完全剖析>>
<<UNIX作業系統設計>>
這幾本書都是看到相關部分時拿出來的參考資料,閱讀還是以<<深入理解linux核心>>為主展開.

==================分割線=====================
熟悉unix的人都知道,程序號也就是pid實際上是整型的資料,每次建立一個新的程序就返回一個id號,這個id號一直遞增,直到最大的時候開始"迴繞",也就是從0開始尋找當前最小的可用的pid.

linux核心中,採用點陣圖來實現pid的分配與釋放.簡單的說,就是分配一個與系統最大pid數目相同大小的點陣圖,每次分配了一個pid,就將點陣圖中的相應位置置1;釋放則置0;迴繞的時候則從0開始繼續前面的查詢,如果遍歷了整個點陣圖都找不到,那麼返回-1.

我將核心中相關部分的程式碼提取出來寫了一個簡單的demo:
#include 
<stdio.h>/* max pid, equal to 2^15=32768 */#define PID_MAX_DEFAULT 0x8000/* page size = 2^12 = 4K */#define PAGE_SHIFT    12#define PAGE_SIZE    (1UL << PAGE_SHIFT)#define BITS_PER_BYTE        8#define BITS_PER_PAGE        (PAGE_SIZE * BITS_PER_BYTE)#define BITS_PER_PAGE_MASK    (BITS_PER_PAGE - 1)


typedef 
struct pidmap 
{
    unsigned 
int nr_free;
    
char page[PID_MAX_DEFAULT];
} pidmap_t;

static pidmap_t pidmap = { PID_MAX_DEFAULT, {'0'} };

staticint last_pid =-1;

staticint test_and_set_bit(int offset, void*addr)
{
    unsigned 
long mask =1UL<< (offset & (sizeof(unsigned long* BITS_PER_BYTE 
-1));    
    unsigned 
long*= ((unsigned long*)addr) + (offset >> (sizeof(unsigned long+1));
    unsigned 
long old =*p;    

    
*= old | mask;    

    
return (old & mask) !=0;
}

staticvoid clear_bit(int offset, void*addr)
{
    unsigned 
long mask =1UL<< (offset & (sizeof(unsigned long* BITS_PER_BYTE -1));    
    unsigned 
long*= ((unsigned long*)addr) + (offset >> (sizeof(unsigned long+1));
    unsigned 
long old =*p;    

    
*= old &~mask;    
}

staticint find_next_zero_bit(void*addr, int size, int offset)
{
    unsigned 
long*p;
    unsigned 
long mask;

    
while (offset < size)
    {
        p 
= ((unsigned long*)addr) + (offset >> (sizeof(unsigned long+1));
        mask 
=1UL<< (offset & (sizeof(unsigned long* BITS_PER_BYTE -1));    

        
if ((~(*p) & mask))
        {
            
break;
        }

        
++offset;
    }

    
return offset;
}

staticint alloc_pidmap()
{
    
int pid = last_pid +1;
    
int offset = pid & BITS_PER_PAGE_MASK;

    
if (!pidmap.nr_free)
    {
        
return-1;
    }

    offset 
= find_next_zero_bit(&pidmap.page, BITS_PER_PAGE, offset);
    
if (BITS_PER_PAGE != offset &&!test_and_set_bit(offset, &pidmap.page))
    {
        
--pidmap.nr_free;
        last_pid 
= offset;
        
return offset;
    }

    
return-1;
}

staticvoid free_pidmap(int pid)
{
    
int offset = pid & BITS_PER_PAGE_MASK;

    pidmap.nr_free
++;
    clear_bit(offset, 
&pidmap.page);
}

int main()
{
    
int i;
    
for (i =0; i < PID_MAX_DEFAULT +100++i)
    {
        printf(
"pid = %d\n", alloc_pidmap());
        
if (!(i %100))
        {
            // 到整百時釋放一次pid,看回繞的時候是不是都是使用整百的pid
            free_pidmap(i);
        }
    }

    
return0;
}

說明幾點:
1) 核心中對應的程式碼在pid.c和bitops.h檔案中.
2) 這裡的幾個位操作函式實現linux核心中基於不同的CPU架構分別都做了優化,有的用到了彙編,我這裡使用純C語言完成這幾個函式.但是我想,不論是C還是彙編,這裡隱含的演算法思想都是一樣的(見下面提到的第四點),在演算法確定了之後,再針對可以優化的地方去做優化.
3) 程式碼裡面還做了一些簡化,核心中pidmap物件可能是陣列,但是這裡的實現只有一個pidmap物件.
4) "點陣圖"是非常常見的資料結構,一般適用於如下的場景:
首先,需要分配/釋放的資料是整型相關的;其次,它們是連續的,從0開始的;最後,它們的狀態只有兩種:分配或者空閒.回頭看看pid的這個場景,滿足了使用點陣圖的情況.在其它的一些書籍中,比如<<程式設計珠璣>>,也提到了點陣圖演算法,它那裡的場景與這裡類似.
5) 位操作我還不是很熟悉,寫這幾個位操作演算法費了不少功夫.