linux內核IDR機制詳解【轉】

来源:https://www.cnblogs.com/linhaostudy/archive/2019/03/15/10535455.html
-Advertisement-
Play Games

這幾天在看Linux內核的IPC命名空間時候看到關於IDR的一些管理性質的東西,剛開始看有些迷茫,深入看下去豁然開朗的感覺,把一些心得輸出共勉。 我們來看一下什麼是IDR?IDR的作用是什麼呢? 先來看下IDR的作用:IDR主要實現ID與數據結構的綁定。剛開始看的時候感覺到有點懵,什麼叫“ID與數據 ...


這幾天在看Linux內核的IPC命名空間時候看到關於IDR的一些管理性質的東西,剛開始看有些迷茫,深入看下去豁然開朗的感覺,把一些心得輸出共勉。

我們來看一下什麼是IDR?IDR的作用是什麼呢?

先來看下IDR的作用:IDR主要實現ID與數據結構的綁定。剛開始看的時候感覺到有點懵,什麼叫“ID與數據結構的綁定”?舉一個例子大家就會明白了:在IPC通信的時候先要動態獲取一個key值或者使用現有的key值進行通信,那麼系統怎麼知道這個key值是否使用了呢?這個就需要IDR來進行判斷了。以上就是IDR的一些淺顯的概念,IDR本質上就是通過對於ID一些有效的管理進而管理和這些ID有關的數據結構----不限於IPC通信的key值。

IDR怎麼對於數據ID管理呢?傳統上我們對於未使用的ID進行管理的時候可以使用點陣圖進行管理,也可以使用數組進行管理,也可以使用鏈表進行ID管理,三個個各有優缺點:

  1. 使用點陣圖進行管理的時候優點是使用空間少,但是對於點陣圖對應的數據結構支持不太友好。
  2. 使用數組進行管理的時候定址快速,但是只能管理比較少量的ID數目。
  3. 使用鏈表進行管理的時候雖然可以支持大量的數據ID,但是通過鏈表的指針定址比較慢。

所以引入了以上三者的優點進行IDR管理。

IDR管理的核心

IDR把每一個ID分級數據進行管理,每一級維護著ID的5位數據,這樣就可以把IDR分為7級進行管理(5*7=35,維護的數據大於32位),如下所示:

31 30 | 29 28 27 26 25 | 24 23 22 21 20 | 19 18 17 16 15 | 14 13 12 11 10 | 9 8 7 6 5 | 4 3 2 1 0

例如數據ID為0B 10 11111 10011 00111 11001 100001 00001,定址如下:

1. 第一級定址 ary1[0b10]得到第二級地址ary2[]
 2. ary3 = ary2[0b11111]
 3. ary4 = ary3[ob10011]
 4. ary5 = ary4[0b00111]
 5. ary6 = ary5[0b11001]
 6. ary7 = ary6[0b100001]
 7. ary8 = ary7[0b00001]

ary8即為要定址到的ID對應的IDR指針。

如下圖:
image

上圖中每一個分級中的IDR數組中的值不為空代表相應位有效的ID位,但是使用數組下標標示有效的ID位還是有點慢----需要通過數組下標以及數組內容判斷有效的ID位,所以對於每一個IDR引入了有效的ID點陣圖來表示,每一個點陣圖為32位剛好給出了相應的有效的ID位。方便查找。

上圖中只是使用了IDR的32個數組表示,並沒有給出IDR的點陣圖以及層數標誌,下麵給出相應的數據結構:

IDR 數據結構:

struct idr_layer {
    //點陣圖,ary數組結構哪個有效
        unsigned long            bitmap; /* A zero bit means "space here" */
        //IDR數組
        struct idr_layer __rcu  *ary[1<<IDR_BITS];
        標示
        int                      count;  /* When zero, we can release it */
        //層數,代表所在的ID位
        int                      layer;  /* distance from leaf */
        struct rcu_head          rcu_head;
};

struct idr {
    //IDR層數頭,實際上就是32叉樹
        struct idr_layer __rcu *top;
    //尚未使用的IDR
        struct idr_layer *id_free;
        //層數
        int               layers; /* only valid without concurrent changes */
        //id_free未用的個數;
        int               id_free_cnt;
        spinlock_t        lock;
};

下麵討論一下IDR的初始化以及增刪改查ID問題:

  1. IDR的初始化
  2. IDR的增加
  3. IDR的查找

IDR的初始化:

IDR的初始化相對來說比較簡單,使用IDR_INIT可以初始化一個IDR,原型如下:

#define IDR_INIT(name)                                          \
{                                                               \
        .top            = NULL,                                 \
        .id_free        = NULL,                                 \
        .layers         = 0,                                    \
        .id_free_cnt    = 0,                                    \
        .lock           = __SPIN_LOCK_UNLOCKED(name.lock),      \
}

可以看到IDR只是把各個數據值為零,原子鎖初始化下。

IDR的增加:

IDR增加比較複雜,在C中編程大部分情況可以分為如下兩點討論:

1.idr.top為NULL的情況;
 2.idr.top不為NULL的情況;
 以上考慮問題也是可以的,但是沒有考慮到如下問題:
 每一個idr_layer結構體有一個layer標示,我們每每增加一層,就要遍歷整個idr的32叉樹。無形中增加了系統負擔。

idr設計者在考慮問題時候恰恰相反,沒增加一個idr_layer層,就把要增加的idr_layer->ary[0]指向舊的idr_layer樹的根,把要增加idr_layer->layer賦予舊的根部的idr_layer->layer + 1值,這樣就不會考慮到idr->top為NULL的情況了。ps:只需要判斷在增加第一個idr_layer時候判斷一下,並且把idr_layer->layer值賦為0.

IDR的查找:

在查找IDR時侯會先查找IDR根節點,然後根據ID位所在的層的值遍歷IDR樹,如果查找到某一段樹為NULL,則會返回NULL。

以下是IDR查找的過程:

void *idr_find(struct idr *idp, int id)                         
{       
        int n;
        struct idr_layer *p;                                    
        //獲取根IDR
        p = rcu_dereference_raw(idp->top);
        if (!p)
                return NULL;
        /**
        根據IDR的層數獲取要遍歷的個數;
    **/
        n = (p->layer+1) * IDR_BITS;

        /* 去除我們不需要查找的位數. */
        id &= MAX_ID_MASK;
    /***如果ID值大於n, 1<<n為根據層數換算過來的ID的最大值**/
        if (id >= (1 << n))
                return NULL;
        BUG_ON(n == 0);
    /***
        遍歷順序:28---->0,每次減少5位,可以遍歷完全IDR的32叉樹
    ***/
        while (n > 0 && p) {
                n -= IDR_BITS;
                BUG_ON(n != p->layer*IDR_BITS);
                p = rcu_dereference_raw(p->ary[(id >> n) & IDR_MASK]);
        }
        return((void *)p);
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 Linux上提供了兩款工具用於查找文件,一款是locate,另一款是find。 locate的特點是根據已生成的資料庫查找,速度較快,但是查找的是快照數據,不准確。 因此在日常使用中,為了準確性,使用find的情況比較常見。並且find可自定義查找條件,十分靈活。 locate Linux上有 ...
  • 配置虛擬主機: 從預設的模板文件中複製過來一份進行自己的配置: 將證書上傳到自己的伺服器上可以訪問到的位置, 然後再這個文件裡面填寫自己的證書路徑 編輯好規則以後檢查自己編輯的規則是否有誤: 檢查無誤以後啟用規則: 重啟apache: ...
  • local_irq_disable: local_irq_disable的功能是屏蔽當前CPU上的所有中斷,通過操作arm核心中的寄存器來屏蔽到達CPU上的中斷,此時中斷控制器中所有送往該CPU上的中斷信號都將被忽略。 disable_irq: 在全局範圍內屏蔽某一個中斷號(irq num)。該ir ...
  • 由於公司內部伺服器沒有聯通外網,只能苦逼的手動安裝gcc(自帶的版本太老) rpm -ivh ppl-0.10.2-11.el6.x86_64.rpm rpm -ivh cloog-ppl-0.15.7-1.2.el6.x86_64.rpm rpm -ivh mpfr-2.4.1-6.el6.x86 ...
  • 新申請的阿裡雲ECS,用xshell連接時修改密碼: 實例密碼就是xshell連接密碼 遠程連接密碼為登錄阿裡雲後臺的密碼 ...
  • 轉自:https://blog.csdn.net/techviewer/article/details/26485017 unattend.txt文件: 主要註釋: dcpromo.exe /unattend:C:\Users\Administrator\Desktop\123.txt,這個是應答文 ...
  • 安裝準備: 1、安裝前需要先關閉selinux和firewall. 關閉Linux: [root@zabbix ~]# vi /etc/selinux/config 將SELINUX=enforcing改為SELINUX=disabled 設置後需要重啟才能生效 配置zabbix的yum源: rpm ...
  • 自己用錯了命令,直接將加入域的電腦使用dsrm刪除了,本來應該使用netdom remove的,結果在域控制器上使用netdom remove錯誤,在客戶端上登錄時一樣提示:netdom remove 解決辦法 先退域,再加域。 1.管理員登錄客戶端 設置工作組可隨意輸入一個工作組名稱,實現退域 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...