在linux操作系統內核實現里經常使用的紅黑樹如下:
二叉樹,按中序遍歷後為一遞增數組,自平衡意味著樹的高度有一個上限,對於紅黑樹,其為2log(n+1),所以時間復雜度為最差為Olog(n)。
賦予二叉搜索樹自平衡特性的方法有多種,紅黑樹通過一下4條約束實現自平衡:
Every node is either red or black.
All NIL nodes (figure 1) are considered black.
A red node does not have a red child.
Everypathfrom a given node to any of its descendant NIL nodes goes through the same number of black nodes.
其中根節點為黑色。
紅黑樹的搜索與二叉搜索樹無異,但是插入和刪除可能會違背上述四條原則。需要用到左旋右旋操作。左旋右旋上圖,可以看到左旋右旋本身不改變二叉搜索樹的特性,旋轉後必要時改變節點的顏色可消除插入或者刪除帶來的紅沖突和黑沖突,有時紅黑樹的重新平衡需要迭代進行。
紅黑樹比較適合的應用場景:
需要動態插入、刪除、查找的場景,包括但不限於:
某些資料庫的增刪改查,比如select * from xxx where 這類條件檢索。
linux內核中進程通過紅黑樹組織管理,便於快速插入、刪除、查找進程的task_struct。
linux內存中內存的管理:分配和回收。用紅黑樹組織已經分配的內存塊,當應用程序調用free釋放內存的時候,可以根據內存地址在紅黑樹中快速找到目標內存塊。
hashmap中(key,value)增、刪、改查的實現;java 8就採用了RBTree替代鏈表。
Ext3文件系統,通過紅黑樹組織目錄項。
❷ FILE的類型
在不同的系統中,FILE結構體的內部不盡相同
如Linux 2.6.23內核中
struct file {
union {
struct list_head fu_list;
struct rcu_head fu_rcuhead;
} f_u;
struct path f_path;
#define f_dentry f_path.dentry
#define f_vfsmnt f_path.mnt
const struct file_operations *f_op;
atomic_t f_count;
unsigned int f_flags;
mode_t f_mode;
loff_t f_pos;
struct fown_struct f_owner;
unsigned int f_uid, f_gid;
struct file_ra_state f_ra;
unsigned long f_version;
#ifdef CONFIG_SECURITY
void *f_security;
#endif
/* needed for tty driver, and maybe others */
void *private_data;
#ifdef CONFIG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks to this file */
struct list_head f_ep_links;
spinlock_t f_ep_lock;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;
};
❸ 請有各位大蝦幫我分析下面關於LINUX文件系統編程的代碼,然後回答幾個問題
(1)num=read(0,buffer,MAX); write(fd,buffer,num);
表示從標准輸入設備中(鍵盤輸入) 讀取數據放入buffer 再寫到「file1」中
0代表標准輸入設備, 1代表標准輸出設備 , 2,代表錯誤輸出設備
(2)execv( path , argv ) 用來執行path字元串所代表的文件路徑文 argv是執行命令
總的來說就是執行 ls -l file1這個命令 這個命令執行後 會列出file1的文件屬性
包括訪問許可權
❹ 在linux編程中若一個用戶程序希望將一組數據傳遞給kernel有幾種方式
教科書里的代碼例子都已作古,所以看到的代碼不能當真,領會意思就行了
比如以前的init進程的啟動代碼
execve(init_filename,argv_init,envp_init);
現在改為
static void run_init_process(char *init_filename)
{
argv_init[0] = init_filename;
kernel_execve(init_filename, argv_init, envp_init);
}
好的,聰明人就發現,linux內核中調用用戶空間的程序可以使用init這樣的方式,調用 kernel_execve
不過內核還是提供了更好的輔助介面call_usermodehelper,自然最後也是調用kernel_execve
調用特定的內核函數(系統調用)是 GNU/Linux 中軟體開發的原本就有的組成部分。但如果方向反過來呢,內核空間調用用戶空間?確實有一些有這種特性的應用程序需要每天使用。例如,當內核找到一個設備, 這時需要載入某個模塊,進程如何處理?動態模塊載入在內核通過 usermode-helper 進程進行。
讓我們從探索 usermode-helper 應用程序編程介面(API)以及在內核中使用的例子開始。 然後,使用 API 構造一個示例應用程序,以便更好地理解其工作原理與局限。
usermode-helper API
usermode-helper API 是個很簡單的 API,其選項為用戶熟知。例如,要創建一個用戶空間進程,通常只要設置名稱為 executable,選項都為 executable,以及一組環境變數(指向 execve 主頁)。創建內核進程也是一樣。但由於創建內核空間進程,還需要設置一些額外選項。
內核版本
本文探討的是 2.6.27 版內核的 usermode-helper API。
表 1 展示的是 usermode-helper API 中一組關鍵的內核函數
表 1. usermode-helper API 中的核心函數
API 函數
描述
call_usermodehelper_setup 准備 user-land 調用的處理函數
call_usermodehelper_setkeys 設置 helper 的會話密鑰
call_usermodehelper_setcleanup 為 helper 設置一個清空函數
call_usermodehelper_stdinpipe 為 helper 創建 stdin 管道
call_usermodehelper_exec 調用 user-land
表 2 中還有一些簡化函數,它們封裝了的幾個內核函數(用一個調用代替多個調用)。這些簡化函數在很多情況下都很有用,因此盡可能使用他們。
表 2. usermode-helper API 的簡化
API 函數
描述
call_usermodehelper 調用 user-land
call_usermodehelper_pipe 使用 stdin 管道調用 user-land
call_usermodehelper_keys 使用會話密鑰調用 user-land
讓我們先瀏覽一遍這些核心函數,然後探索簡化函數提供了哪些功能。核心 API 使用了一個稱為subprocess_info 結構的處理函數引用進行操作。該結構(可在 ./kernel/kmod.c 中找到)集合了給定的 usermode-helper 實例的所有必需元素。該結構引用從 call_usermodehelper_setup 調用返回。該結構(以及後續調用)將會在 call_usermodehelper_setkeys(用於存儲憑證)、call_usermodehelper_setcleanup 以及 call_usermodehelper_stdinpipe 的調用中進一步配置。最後,一旦配置完成,就可通過調用 call_usermodehelper_exec 來調用配置好的用戶模式應用程序。
聲明
該方法提供了一個從內核調用用戶空間應用程序必需的函數。盡管這項功能有合理用途,還應仔細考慮是否需要其他實現。這是一個方法,但其他方法會更合適。
核心函數提供了最大程度的控制,其中 helper 函數在單個調用中完成了大部分工作。管道相關調用(call_usermodehelper_stdinpipe 和 helper 函數 call_usermodehelper_pipe)創建了一個相聯管道供 helper 使用。具體地說,創建了管道(內核中的文件結構)。用戶空間應用程序對管道可讀,內核對管道可寫。對於本文,核心轉儲只是使用 usermode-helper 管道的應用程序。在該應用程序(./fs/exec.c do_coremp())中,核心轉儲通過管道從內核空間寫到用戶空間。
這些函數與 sub_processinfo 以及 subprocess_info 結構的細節之間的關系如圖 1 所示。
圖 1. Usermode-helper API 關系
表 2 中的簡化函數內部執行 call_usermodehelper_setup 函數和 call_usermodehelper_exec 函數。表 2 中最後兩個調用分別調用的是 call_usermodehelper_setkeys 和 call_usermodehelper_stdinpipe。可以在 ./kernel/kmod.c 找到 call_usermodehelper_pipe 和 call_usermodehelper 的代碼,在 ./include/linux/kmod.h 中找到 call_usermodhelper_keys 的代碼。
為什麼要從內核調用用戶空間應用程序?
現在讓我們看一看 usermode-helper API 所使用的內核空間。表 3 提供的並不是專門的應用程序列表,而是一些有趣應用的示例。
表 3. 內核中的 usermode-helper API 應用程序
應用程序
源文件位置
內核模塊調用 ./kernel/kmod.c
電源管理 ./kernel/sys.c
控制組 ./kernel/cgroup.c
安全密匙生成 ./security/keys/request_key.c
內核事件交付 ./lib/kobject_uevent.c
最直接的 usermode-helper API 應用程序是從內核空間載入內核模塊。request_mole 函數封裝了 usermode-helper API 的功能並提供了簡單的介面。在一個常用的模塊中,內核指定一個設備或所需服務並調用 request_mole 來載入模塊。通過使用 usermode-helper API,模塊通過 modprobe 載入到內核(應用程序通過 request_mole 在用戶空間被調用)。
與模塊載入類似的應用程序是設備熱插拔(在運行時添加或刪除設備)。該特性是通過使用 usermode-helper API,調用用戶空間的 /sbin/hotplug 工具實現的。
關於 usermode-helper API 的一個有趣的應用程序(通過 request_mole) 是文本搜索 API(./lib/textsearch.c)。該應用程序在內核中提供了一個可配置的文本搜索基礎架構。該應用程序使用 usermode-helper API 將搜索演算法當作可載入模塊進行動態載入。在 2.6.30 內核版本中,支持三個演算法,包括 Boyer-Moore(./lib/ts_bm.c),簡單固定狀態機方法(./lib/ts_fsm.c),以及 Knuth-Morris-Pratt 演算法(./lib/ts_kmp.c)。
usermode-helper API 還支持 Linux 按照順序關閉系統。當需要系統關閉電源時,內核調用用戶空間的 /sbin/poweroff 命令來完成。其他應用程序如 表 3 所示,表中附有其源文件位置。
Usermode-helper API 內部
在 kernel/kmod.c 中可以找到 usermode-helper API 的源代碼 和 API(展示了主要的用作內核空間的內核模塊載入器)。這個實現使用 kernel_execve 完成臟工作(dirty work)。請注意 kernel_execve是在啟動時開啟 init 進程的函數,而且未使用 usermode-helper API。
usermode-helper API 的實現相當簡單直觀(見圖 2)。usermode-helper 從調用call_usermodehelper_exec 開始執行(它用於從預先配置好的 subprocess_info 結構中清除用戶空間應用程序)。該函數接受兩個參數:subprocess_info 結構引用和一個枚舉類型(不等待、等待進程中止及等待進程完全結束)。subprocess_info(或者是,該結構的 work_struct 元素)然後被壓入工作隊列(khelper_wq),然後隊列非同步執行調用。
圖 2. usermode-helper API 內部實現
當一個元素放入 khelper_wq 時,工作隊列的處理函數就被調用(本例中是__call_usermodehelper),它在 khelper 線程中運行。該函數從將 subprocess_info 結構出隊開始,此結構包含所有用戶空間調用所需信息。該路徑下一步取決於 wait 枚舉變數。如果請求者想要等整個進程結束,包含用戶空間調用(UMH_WAIT_PROC)或者是根本不等待(UMH_NO_WAIT),那麼會從 wait_for_helper 函數創建一個內核線程。否則,請求者只是等待用戶空間應用程序被調用(UMH_WAIT_EXEC),但並不完全。這種情況下,會為____call_usermodehelper() 創建一個內核線程。
在 wait_for_helper 線程中,會安裝一個 SIGCHLD 信號處理函數,並為 ____call_usermodehelper 創建另一個內核線程。但在 wait_for_helper 線程中,會調用 sys_wait4 來等待____call_usermodehelper 內核線程(由 SIGCHLD 信號指示)結束。然後線程執行必要的清除工作(為UMH_NO_WAIT 釋放結構空間或簡單地向 call_usermodehelper_exec() 回送一個完成報告)。
函數 ____call_usermodehelper 是實際讓應用程序在用戶空間啟動的地方。該函數首先解鎖所有信號並設置會話密鑰環。它還安裝了 stdin 管道(如果有請求)。進行了一些安裝以後,用戶空間應用程序通過 kernel_execve(來自 kernel/syscall.c)被調用,此文件包含此前定義的 path、argv 清單(包含用戶空間應用程序名稱)以及環境。當該進程完成後,此線程通過調用 do_exit() 而產生。
該進程還使用了 Linux 的 completion,它是像信號一樣的操作。當 call_usermodehelper_exec 函數被調用後,就會聲明 completion。當 subprocess_info 結構放入 khelper_wq 後,會調用wait_for_completion(使用 completion 變數作為參數)。請注意此變數會存儲到 subprocess_info 結構作為 complete 欄位。當子線程想要喚醒 call_usermodehelper_exec 函數,會調用內核方法complete,並判斷來自 subprocess_info 結構的 completion 變數。該調用會解鎖此函數使其能繼續。可以在 include/linux/completion.h 中找到 API 的實現。
應用程序示例
現在,讓我們看看 usermode-helper API 的簡單應用。首先看一下標准 API,然後學習如何使用 helper 函數使事情更簡單。
在該例中,首先開發了一個簡單的調用 API 的可載入內核模塊。清單 1 展示了樣板模塊功能,定義了模塊入口和出口函數。這兩個函數根據模塊的 modprobe(模塊入口函數)或 insmod(模塊入口函數),以及 rmmod(模塊出口函數)被調用。
清單 1. 模塊樣板函數
#include
#include
#include
MODULE_LICENSE( "GPL" );
static int __init mod_entry_func( void )
{
return umh_test();
}
static void __exit mod_exit_func( void )
{
return;
}
mole_init( mod_entry_func );
mole_exit( mod_exit_func );
usermode-helper API 的使用如 清單 2 所示,其中有詳細描述。函數開始是聲明所需變數和結構。以subprocess_info 結構開始,它包含所有的執行用戶空間調用的信息。該調用在調用call_usermodehelper_setup 時初始化。下一步,定義參數列表,使 argv 被調用。該列表與普通 C 程序中的 argv 列表類似,定義了應用程序(數組第一個元素)和參數列表。需要 NULL 終止符來提示列表末尾。請注意這里的 argc 變數(參數數量)是隱式的,因為 argv 列表的長度已經知道。該例中,應用程序名是 /usr/bin/logger,參數是 help!,然後是 NULL 終止符。下一個所需變數是環境數組(envp)。該數組是一組定義用戶空間應用程序執行環境的參數列表。本例中,定義一些常用的參數,這些參數用於定義 shell 並以 NULL 條目結束。
清單 2. 簡單的 usermode_helper API 測試
static int umh_test( void )
{
struct subprocess_info *sub_info;
char *argv[] = { "/usr/bin/logger", "help!", NULL };
static char *envp[] = {
"HOME=/",
"TERM=linux",
"PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL };
sub_info = call_usermodehelper_setup( argv[0], argv, envp, GFP_ATOMIC );
if (sub_info == NULL) return -ENOMEM;
return call_usermodehelper_exec( sub_info, UMH_WAIT_PROC );
}
下一步,調用 call_usermodehelper_setup 來創建已初始化的 subprocess_info 結構。請注意使用了先前初始化的變數以及指示用於內存初始化的 GFP 屏蔽第四個參數。在安裝函數內部,調用了kzalloc(分配內核內存並清零)。該函數需要 GFP_ATOMIC 或 GFP_KERNEL 標志(前者定義調用不可以休眠,後者定義可以休眠)。快速測試新結構(即,非 NULL)後,使用 call_usermodehelper_exec 函數繼續調用。該函數使用 subprocess_info 結構以及定義是否等待的枚舉變數(在 「Usermode-helper API 內部」 一節中有描述)。全部完成! 模塊一旦載入,就可以在 /var/log/messages 文件中看到信息。
還可以通過 call_usermodehelper API 函數進一步簡化進程,它同時執行 call_usermodehelper_setup和 call_usermodehelper_exec 函數。如清單 3 所示,它不僅刪除函數,還消除了調用者管理subprocess_info 結構的必要性。
清單 3. 更簡單的 usermode-helper API 測試
static int umh_test( void )
{
char *argv[] = { "/usr/bin/logger", "help!", NULL };
static char *envp[] = {
"HOME=/",
"TERM=linux",
"PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL };
return call_usermodehelper( argv[0], argv, envp, UMH_WAIT_PROC );
}
請注意在清單 3 中,有著同樣的安裝並調用(例如初始化 argv 和 envp 數組)的需求。此處惟一的區別是 helper 函數執行 setup 和 exec 函數。
❺ linux操作系統中 使用系統調用的一般方式是怎樣的
給個例子給你看看吧,這里是linux中一個C語言程序,他用到了linux提供的系統調用,很長的程序了,或許你沒耐心看,我在最後給你列出那些地方用了系統調用
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define BUFF_LEN 1024
#define RET_ERROR 1
#define RET_OK 0
typedef struct msg_send_struct { //這就是一個消息的數據結構
long my_type; //我們就是根據這個欄位來區分每塊消息的
char my_text[BUFF_LEN];
} msg_send_struct;
int main() {
char * path = "/";
int i_porject_id = 7;
key_t key;
msg_send_struct msg_send; //定義發送消息
int i_ret;
int i_msg_id;
int i_flag = 0666|IPC_CREAT; //為消息管道的創建指定參數,IPC_CREAT表示這個消息隊列是創建,而不是搜索已經存在的消息隊列
key = ftok(path, i_porject_id);//為消息隊列生成一個key,當然你也可以手動指定,當你運氣很好沒有和已經竄在的消息隊列的key起沖突的時候
if(key == 1) {
printf("building key error\n");
exit(1);
}
i_msg_id = msgget(key, i_flag);//根據你的參數決定是創建還是搜索KEY值得消息隊列
if(i_msg_id == -1) {
printf("create msg queue error\n");
exit(1);
}
printf("i_msg_id = %d\n", i_msg_id);
msg_send.my_type = 1;
strcpy(msg_send.my_text, "hello world"); //初始化消息
i_ret = msgsnd(i_msg_id, &msg_send, strlen("hello world") + 1, IPC_NOWAIT);//開始發送,nowait表示如果隊列中消息滿了當前進程不等待直接返回錯誤,反之很容易理解吧
if(i_ret == -1) {
printf("msg send error\n");
exit(1);
}
exit(0);
}
下面是系統調用:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>這些頭文件可不是庫函數,他裡面就是linux提供的系統調用。
key = ftok(path, i_porject_id);//為消息隊列生成一個key,當然你也可以手動指定,當你運氣很好沒有和已經竄在的消息隊列的key起沖突的時候
linux中系統調用,利用文件系統和ID來創建KEY。
i_msg_id = msgget(key, i_flag);//根據i_flag值決定是創建還是尋找消息隊列的系統調用。
i_ret = msgsnd(i_msg_id, &msg_send, strlen("hello world") + 1, IPC_NOWAIT);//發送消息的系統調用msgsnd函數。
這里涉及到進程通信中的消息隊列內容,如果不明白沒什麼關系,可以看出來他和C的庫函數調用一模一樣,只不過實現方式,這需要你的知識積累到一定程度,有很大差別。對於一個程序員來說,我們看不出什麼他們和庫函數有什麼區別,這算是一種對我們的透明性。
補充:這個例子是我寫來學習進程通信內容的,由於採用了linux系統調用,所以只能在linux下面運行,還有就是我沒有考慮許可權問題,所以要編譯請用超級用戶root,由於消息隊列的特性,這個程序沒有釋放隊列(我把釋放代碼寫在了接受消息的程序中),第2次運行就會報消息隊列不能創建的錯誤。
❻ Ubuntu下make編譯,出現「error: 『struct file』 has no member named 『f_dentry』」
宏定義一個d_path 換掉f_dentry
❼ linux內核態,在LSM框架中的文件操作hook介面中如何獲取一個正在被操作的文件的內容(linux4.4版本)
LSM是Linux Secrity Mole的簡稱,即linux安全模塊。其是一種輕量級通用訪
問控制框架,適合於多種訪問控制模型在它上面以內核可載入模塊的形實現。用
戶可以根據自己的需求選擇合適的安全模塊載入到內核上實現。
LSM設計思想:
LSM的設計思想:在最少改變內核代碼的情況下,提供一個能夠成功實現強制訪
問控制模塊需要的結構或者介面。LSM避免了利用如在systrace系統調用中的出
現過的系統調用干預,因為它不能擴展到多處理器內核,並且它受制於參數替換
攻擊。還有LSM在設計時做了兩點考慮:對不使用的人來說盡量少引入麻煩,對
使用的人來說要帶來效率。以Linus Torvalds為代表的內核開發人員對Linux安
全模塊(LSM)提出了三點要求:
1、真正的通用,當使用一個不同的安全模型的時候,只需要載入一個不同的內
核模塊。
2、概念上簡單,對Linux內核影響最小,高效,並且。
3、能夠支持現存的POSIX.1e capabilities邏輯,作為一個可選的安全模塊。
還有,針對linux上提出的各種不同的Linux安全增強系統對Linux安全模塊(LSM
)提出的要求是:能夠允許他們以可載入內核模塊的形式重新實現其安全功能,
並且不會在安全性方面帶來明顯的損失,也不會帶來額外的系統開銷。
LSM框架結構:
LSM框架主要由五部分構成:
1、在特定的內核數據結構中加入安全域。
2、在內核源代碼中不同的關鍵點插入對安全鉤子函數的調用。
3、加入一個通用的安全系統調用。
4、提供了函數允許內核模塊注冊為安全模塊或者注銷。
5、5、將capabilities邏輯的大部分移植為一個可選的安全模塊。
安全域是一個void*類型的指針,它使得安全模塊把安全信息和內核內部對象聯
系起來。下面列出被修改加入了安全域的內核數據結構,以及各自所代表的內核
內部對象:
task_struct結構:代表任務(進程)
linux_binprm結構:代表程序
super_block結構:代表文件系統
inode結構:代表管道,文件,或者Socket套接字
file結構:代表打開的文件
sk_buff結構:代表網路緩沖區(包)
net_device結構:代表網路設備
kern_ipc_perm結構:代表Semaphore信號,共享內存段,或者消息隊列
msg_msg:代表單個的消息
Linux安全模塊(LSM)提供了兩類對安全鉤子函數的調用:一類管理內核對象的
安全域,另一類仲裁對這些內核對象的訪問。對安全鉤子函數的調用通過鉤子來
實現,鉤子是全局表security_ops中的函數指針,這個全局表的類型是
security_operations結構,這個結構定義在include/linux/security.h這個頭
文件中。
LSM介面的核心是security_ops,當系統啟動時,他們被初始化為傳統的DAC策略
。傳統DAC訪問控制是指控制系統中的主體(如進程)對系統中的客體(如文件
目錄、文件)的訪問(讀、寫和執行等)。自主訪問控制DAC 是指主體(進程,
用戶)對客體(文件、目錄、特殊設備文件、IPC等)的訪問許可權是由客體的屬
主或超級用戶決定的,而且此許可權一旦確定,將作為以後判斷主體對客體是否有
訪問許可權的依據。
在載入安全模塊時,我們必需先對模塊進行注冊,我們可以使用
register_security()函數向LSM注冊一個安全模塊。在我們的模塊被載入成
功後,就可以進行訪問控制操作。如果此時還有一個安全模塊要使用
register_security()函數進行載入,則會出現錯誤,直到使用
unregister_security()函數向框架注銷後,下一個模塊才可以載入。當然LS
M還提供了mod_reg_security()函數和mod_unreg_security()函數,可以連續注
冊多個安全模塊。如果有其他後來的模塊需要載入,可以通過mod_reg_security
()向第一個模塊注冊,形成支持不同策略的模塊棧。
註:以上出現的函數均基於2.6.22以前的版本,對於後續的版本,出現了
register_security()函數未被導出或者取消掉了unregister_security()函數。
LSM執行過程:
根據下圖的執行步驟:用戶在執行系統調用時,先通過原有的內核介面依次執行
功能性的錯誤檢查,接著進行傳統的DAC檢查,並在即將訪問內核的內部對象之
前,通過LSM鉤子函數調用LSM。LSM再調用具體的訪問控制策略來決定訪問的合
法性。圖三顯示了LSM鉤子的調用:
圖三:基於LSM的內核對象訪問過程
Lilinux安全模塊(LSM)主要支持"限制型"的訪問控制決策:當Linux內核授予
文件或目錄訪問許可權時,Linux安全模塊(LSM)可能會拒絕,而當 Linux內核拒
絕訪問時,可以跳過LSM。
========
使用LSM實現自己的訪問控制
首先對LSM 進行簡單介紹。雖然linux下的各位基本都知道一些,但是還要羅嗦
一下。
LSM中文全稱是linux安全模塊。英文全稱:linux security mole.
LSM是一種輕量級、通用的訪問控制框架,適合多種訪問控制模型以內核模塊的
形式實現。其特點是通用、簡單、高效、支持POSIX。1e能力機制。
LSM的架構圖如下:
通過系統調用進入內核之後,系統首先進行傳統的許可權檢查(傳統許可權檢查主要
是基於用戶的,用戶通過驗證之後就可以訪問資源),通過之後才會進行強制訪
問控制。(強制訪問控制是不允許主體干涉的一種訪問控制,其採用安全標識、
信息分級等信息敏感性進行訪問控制。並且通過比較主體的級別和資源的敏感性
來確定是否允許訪問。比如說系統設置A用戶不允許訪問文件B,即便A是文件B的
所有者,訪問也是受限制的。)從圖上看來,LSM實現訪問控制主要通過安全模
塊的鉤子函數實現。
LSM框架主要由五部分組成:這個網上資料很多。
在關鍵的特定內核數據結構中加入了安全域;
在內核源碼中不同的關鍵點處插入對安全鉤子函數的調用;
提供了一個通用的安全系統調用;
提供了注冊和注銷函數,使得訪問控制策略可以以內核模塊方式實現;
將capabilities邏輯的大部分功能移植為一個可選的安全模塊。
我們這里重點結合源碼對LSM框架進行解釋。我使用的源碼是3.5.4
首先介紹安全域欄位,它是一個空類型的指針,在內核中的很多內核結構中都存
在,比如inode、superblock、dentry、file等等。類型欄位為void *
security;
那麼安全域怎麼和安全模塊中的信息關聯起來?
當安全模塊載入之後,安全域中的指針便指向安全模塊中的安全信息。這里以
selinux為例進行介紹。
內核裡面security/selinux/include/objsec.h中定義了不同對象的安全信息,
格式為XXX_security_strut.
上面的文件的安全信息裡麵包含打開文件描述符時的安全ID、文件所有者的安全
ID等等。
要聯系安全模塊中安全信息和安全域需要幾個控制鉤子函數。這些鉤子函數實現
了對內核關鍵信息的設置和管理。這里主要介紹alloc_security、
free_security。
selinux裡面通過實現安全信息空間分配實現關聯。比如以文件安全信息為例
這里分配空間成功之後,通過file->f_security = fsec實現了關聯。
撤銷關聯是在安全模塊卸載之後調用file_free_security.
這里具體通過設置file->f_secrity為NULL,然後釋放安全信息結構實現。
現在來看看內核如何實現selinux的訪問控制。這里主要就是實現LSM裡面的鉤子
函數了。LSM裡面給出了結構體security_operations,裡面給出了很多鉤子函數
,實現了相關鉤子函數就可以實現訪問控制了。
上面的函數就實現了file_permission鉤子函數。可以看下inode結構體的獲得,
感受內核是通過文件->目錄項->inode。該函數主要實現自己的訪問控制策略就
OK 了。
哪selinux來說,在獲得文件安全ID之後,主要對掩碼和文件打開時相關的安全
信息進行檢測,符合就通過訪問控制。
selinux基本實現了LSM裡面的所有鉤子函數,待鉤子函數實現後,對LSM裡面鉤
子域進行填充就OK了。
做完以上這些還需要注冊安全模塊到LSM,這里注冊和注銷使用了
register_security和unregister_security。
比如selinux在注冊時使用語句register_security(&selinux_ops)實現。
接下來通過上面的分析我們可以實現簡單的基於LSM的訪問控制。
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/mole.h>
#include <linux/fs.h>
#include <linux/security.h>
#include <linux/types.h>
#include <asm/uaccess.h>
#include <linux/fcntl.h>
#include <linux/uaccess.h>
#include <linux/file.h>
#include <linux/namei.h>
static int lsm_test_file_permission(struct file *file,int mask)
{
int path=0;
struct file *filp;
struct nameidata nd;
path = path_lookup(FILENAME,LOOKUP_FOLLOW,&nd);
if(!mask)
return 0;
if(path)
{
printk("lookup file failed!\n");
return -1;
}
filp = filp_open("/home/yuyunchao/code/sb.c",O_RDONLY,0);
{
printk("open failed!\n");
}
return 0;
}
static struct security_operations lsm_test_security_ops = {
.file_permission = lsm_test_file_permission,
};
static int __init lsm_file_init(void)
{
if(register_security(&lsm_test_security_ops)){
printk("register error ..........\n");
return -1;
}
printk("lsm_file init..\n ");
return 0;
}
static void __exit lsm_file_exit(void)
{
if(unregister_security(&lsm_test_security_ops)){
printk("unregister error................\n");
return ;
}
printk("mole exit.......\n");
}
MODULE_LICENSE("GPL");
mole_init(lsm_file_init);
mole_exit(lsm_file_exit);
========
LSM(Linux Security Mole)應用方法(簡單例子)
LSM在內核中很多地方已經插入了hook函數,並且在security.c函數中聲明了
security_ops結構,要實現你自己的安全模塊,只需要定義你自己的struct
security_operations,並且用register_security注冊即可,下面舉個簡單例子
:
test.c代碼如下:
/*
* Test Linux Security Mole
*
* Author: penghuan <[email protected]>
*
* Copyright (C) 2010 UbuntuKylin, Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*
*/
#include <linux/security.h>
#include <linux/sysctl.h>
#include <linux/ptrace.h>
#include <linux/prctl.h>
#include <linux/ratelimit.h>
#include <linux/workqueue.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/dcache.h>
#include <linux/path.h>
int test_file_permission(struct file *file, int mask)
{
char *name = file->f_path.dentry->d_name.name;
if(!strcmp(name, "test.txt"))
{
file->f_flags |= O_RDONLY;
printk("you can have your control code here!\n");
}
return 0;
}
static struct security_operations test_security_ops = {
.name = "test",
.file_permission = test_file_permission,
};
static __init int test_init(void)
{
printk("enter test init!\n");
printk(KERN_INFO "Test: becoming......\n")
if (register_security(&test_security_ops))
panic("Test: kernel registration failed.\n");
return 0;
}
security_initcall(test_init);
將該文件以模塊的形式放到security/下編譯進內核,啟用新的內核後,當你操
作文件test.txt時,通過dmesg命令就能再終端看到」you can have your
control code here!「輸出
所以一般的做法是:定義你自己的struct security_operations,實現你自己的
hook函數,具體有哪些hook函數可以查詢include/linux/security.h文件,然後
調用register_security來用你的test_security_ops初始化全局的security_ops
指針
樓主,我剛開始研究LSM,但網上資料太少,您這個代碼,我編譯成ko文件老是
有警告,並且insmod時,說Unknown symbol register_security。我最近看了看
內核模塊變成,沒有對內核進行太深入的了解。不知能否把LSM的實驗步驟給出
的再詳細點,謝謝。
你需要把代碼編進內核
是需要把那段源碼拷到內核目錄下,然後重新編譯內核?。。沒有不編譯內核的
方法嗎?。。直接按照模塊進行編譯。另外那個test.txt放在哪個文件夾里?。
是需要把那段源碼拷到內核目錄下,然後重新編譯內核?。。沒有不編譯內核的
方法嗎?。。直接按照模塊進行 ...
是的,你去網上找下怎麼把模塊編進內核,lsm模塊不能以模塊方式載入,涉及
安全;test.txt是測試文件,當你把代碼編進內核後,用新內核啟動,然後操作
test.txt文件,就會有輸出,test.txt隨便放哪裡
樓主,您好,我剛開始學習lsm模塊,把您的模塊編譯進內核,新的內核載入後
,register_security總是失敗,請問下可能是什麼原因導致的。我的內核版本
是3.13.11。
register_security的返回值是-11
========
LSM在Linux中的實現方式
LSM(Linux Secure Model)一種輕量級訪問控制機制.
其實現方式有如在系統調用中加入一個後門....
方式如下:
static struct file *__dentry_open(struct dentry *dentry, struct
vfsmount *mnt,
struct file *f,
int (*open)(struct inode *, struct file *),
const struct cred *cred)
{
struct inode *inode;
int error;
...............................................................
error = security_dentry_open(f, cred); //LSM機制實現方式,在此加入了
一個LSM函數.
//security_dentry_open的實現如下,相當於一個介面,對一個函數指針再
//封裝一下.
//只返回是與否,這樣的控制信息.
if (error)
goto cleanup_all;
................................................................
return f;
cleanup_all:
.................................................................
return ERR_PTR(error);
}
//========簡單封裝一個指針結構體===========================
int security_dentry_open(struct file *file, const struct cred *cred)
{
int ret;
ret = security_ops->dentry_open(file, cred);
if (ret)
return ret;
return fsnotify_perm(file, MAY_OPEN);
}
========
利用LSM實現更安全的linux
LSM的全稱是Linux Security Moles,它是linux內核中用來支持更靈活的
安全策略的一個底層框架,雖然聽起來比較復雜,但是可以就把它理解成一組安
插在linux內核的鉤子函數和一些預留的被稱為安全域的數據結構,下面先說說
這個框架的由來吧。
linux本身的機制就保證了linux擁有更好的安全機制,但是在這個機制下面
,還是隱藏了許多的問題:
1、許可權粒度太大。用過linux的人應該對0644這樣的訪問許可權設置不陌生,
它對能夠操作這個文件的用戶做了限制,但是這個只是限制到了組,而沒有更進
一步的細分,當然,如果LSM只是用來限制這個的話,那麼也就太沒意思了,因
為實現文件更細的控制粒度,ACL就能夠很出色的完成,順便提一下,ACL有一個
分配的限制,如果哪位朋友需要用ACL進行粒度更細的訪問許可權控制的話,可能
需要注意一下這方面的東西。
2、root用戶的許可權太大。在linux中,root用戶就是至高無上的,他擁有對
機器的完全控制許可權,可以做他想做的一切事情。但是很多時候,我們可能並不
希望有root有這么大的許可權,比如在現在比較流行的雲存儲中,用戶肯定不希望
服務提供商能夠隨意訪問我們的文件,那麼這個時候,就需要對root用戶進行一
定的設置了。
由於這些問題的存在,所以出現了像SE Linux(Securiy Enhanced Linux )
這樣的增強補丁。但是每個系統對於具體安全細節的控制不盡相同, 所以Linus
Tovalds 提出應該要有一個 Linux 內核所能接受的安全框架來支持這些安全策
略,這個安全框架應該提供包含內核數據結構中的透明安全域以及用來控制、維
護安全域操作的安全鉤子,於是就有了LSM。
LSM在內核中的位置,可以用下圖來表示:
當用戶態程序調用某些操作系統提供的函數的時候,比如read()函數,其會
對應於內核中的一個系統調用,然後該首先會進行一些常規的錯誤檢測,接著進
行DAC(Discretionary Access Control)檢測,再接著它會進行LSM檢測。從上
圖中能夠看出來,LSM其實是一個非常底層的安全策略框架,利用LSM,可以接管
所有的系統調用,這樣,我們就能對包括root在內的所有用戶的許可權進行控制,
並且實現粒度更細的訪問許可權控制。
當系統初始化的時候,LSM就是一個空的框架,它不提供任何的檢測,其所
做的全部工作幾乎就是返回0,當然,有些不帶返回值的函數除外。而我們則可
以針對自己特定的需求來編寫LSM,然後將我們編寫的LSM鉤子函數,通過其數據
結構struct security_operations注冊到系統中去,這樣,我們的LSM檢測就開
始起作用了。
更多信息可參考《Linux就該這么學》
❽ linux下用簡單c語言代碼怎麼實現實現文件夾所有內容的復制
||#include <sys/stat.h>
#include <unistd.h>
// 目錄
int isdir(char *path)
{
struct stat buf;
int cc;
cc=stat(path,&buf);
if(!cc && (buf.st_mode & S_IFDIR)) return(1);
return(cc);
}
// 可讀普通文件
int isrfile(char *path)
{
struct stat buf;
int cc;
int euid,egid;
cc=stat(path,&buf);
if(!cc) {
if((buf.st_mode & S_IFMT) != S_IFREG) return 0;
euid=geteuid();
egid=getegid();
if(euid==0) {
if(buf.st_mode & S_IRUSR || buf.st_mode & S_IRGRP ||
buf.st_mode & S_IROTH)
return 1;
else return 0;
}
if((buf.st_mode & S_IROTH)!=0) return 1;
if((buf.st_gid == egid) && ((buf.st_mode & S_IRGRP)!=0))
return 1;
if((buf.st_uid == euid) && ((buf.st_mode & S_IRUSR)!=0))
return 1;
}
return cc;
} 【江西新華】
❾ linux struct path 怎麼找安裝點
你可以這樣,通過struct inode* inode = file->f_inode;struct dentry * xx = inode->i_dentry;然後通過dentry結構體指針的parent或child來path_walk一下,當然,出現文件鏈接的時候,就比較麻煩了。