作業系統系統呼叫實驗
作業系統實驗一(記錄)
思路:
1.實驗目標及要求
2.實驗結果演示
3.實驗流程
4.遇到的問題及解決
5.實驗思考
一、實驗目標
題目5:把指定檔案變為長度為0的空檔案
要求:
新增系統呼叫:
\1. 程式能正常演示,功能正確實現;
\2. 熟悉系統呼叫詳細處理過程;
\3. 系統呼叫新增過程,包括要修改的檔案、修改的內容等;
\4. 能解釋所有新增的程式碼;
\5. 能詳細分析解釋程式碼中所用到的核心函式的原始碼。
二、實驗結果演示
1.新建一個文字檔案test.txt
2.如圖,可以看見檔案大小為22bytes
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-s15ZyFsX-1611652521505)(…/typora_tupian/QQ%E5%9B%BE%E7%89%8720201128110945.png)]
3.編譯使用者測試程式
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-HGCs6pac-1611652521510)(…/typora_tupian/QQ%E5%9B%BE%E7%89%8720201128111502.png)]
4.編譯測試程式
5.生成a.out
檔案
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-nwN0iPDE-1611652521512)(…/typora_tupian/QQ%E5%9B%BE%E7%89%8720201128111619.png)]
6.執行測試程式:
7.檢視結果:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-r8BPKMwW-1611652521515)(…/typora_tupian/QQ%E5%9B%BE%E7%89%8720201128111930.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-LRZfrgUR-1611652521517)(…/typora_tupian/QQ%E5%9B%BE%E7%89%8720201128112108.png)]
檔案長度變為0bytes,開啟檔案後無內容
實驗成功!!
三、實驗經過
1.系統呼叫過程
系統呼叫是啥?
我在使用者態,想要實現一個在核心態下實現的功能,所以需要呼叫核心函式,進入核心態執行。
通過一條訪管指令“int $0x80
”來呼叫——產生訪管中斷——暫停當前程序——傳入系統呼叫號——查詢相應的服務例程的入口——進入核心態——執行函式呼叫
使用者態到核心態的轉變:
使用者態——使用者空間——Shell
核心態——核心空間——kernel
GUI–圖形介面
Linux作業系統一般是通過軟體中斷從使用者態切換到核心態
使用者態到核心態的切換,其實就是一個程序通過系統呼叫到核心的一些介面。從而實現切換
系統呼叫是對介面的封裝,介面是對核心操作的封裝(介面相當於銀行櫃員,使用者想要去銀行處理業務,但是不能自己處理,需要銀行櫃員幫忙處理,系統呼叫相當於主管,我們要找哪個櫃員需要通過主管)
先通過軟體中斷呼叫了0x80的這個程式設計異常,這個程式設計異常對應的是中斷描述符表IDT中的第128項——也就是對應的系統門描述符。門描述符中含有一個預設的核心空間地址,它指向了系統呼叫處理程式:system_call()
(別和系統呼叫服務程式混淆,這個程式在entry.S
檔案中用匯編語言編寫)。走到這一步,我們應該畫個圖來理解。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-nKdWl8C4-1611652521519)(…/typora_tupian/20181018173953114)]
現在我們就知道通過系統描述符中的核心空間來找到系統呼叫程式,從而進入到核心態。可很顯然,所有的系統呼叫都會統一地轉到這個地址,但Linux一共有319個系統呼叫都從這裡進入核心後又該如何派發到它們到各自的服務程式去呢?
解決這個問題的方法很簡單就是: 首先Linux為每個系統呼叫都進行了編號 (0—NR_syscall)
,同時在核心中儲存了一張系統呼叫表 ,該表中儲存了系統呼叫編號和其對應的服務例程,因此在系統調入通過系統門陷入核心前,需要把系統呼叫號一併傳入核心。在x86上,這個傳遞動作是通過在執行int0x80前把呼叫號裝入eax
暫存器實現的。這樣系統呼叫處理程式一旦執行,就可以從eax
中得到資料,然後再去系統呼叫表中尋找相應服務例程了。為了方便理解,我把這些操作用圖表示出來。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-g75DErXw-1611652521520)(…/typora_tupian/20181018183154271)]
除了需要傳遞系統呼叫號以外,許多系統呼叫還需要傳遞一些引數到核心,比如sys_write(unsigned int fd, const char * buf, size_t count)
呼叫就需要傳遞檔案描述符fd
、要寫入的內容buf
、以及寫入位元組數count等幾個內容到核心。碰到這種情況,Linux會有6個暫存器可被用來傳遞這些引數:eax
(存放系統呼叫號)、 ebx
、ecx
、edx
、esi
及edi
來存放這些額外的引數(以字母遞增的順序)。具體做法是在system_call( )
中使用SAVE_ALL巨集把這些暫存器的值儲存在核心態堆疊中。
做好上述工作之後於,再執行系統呼叫處理程式,這樣就從使用者態切換到核心態。
因此:實驗的思路就是1.先進入系統呼叫表,增加系統呼叫編號和其對應的服務例程,2.然後需要將想要呼叫的也就是剛剛寫的系統呼叫號通過暫存器先傳入核心 3.正常進行系統編譯
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-rWKsoye2-1611652521521)(…/typora_tupian/20180603145400866)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-y2NSspNM-1611652521521)(…/typora_tupian/1-7.png)]
總而言之:
在使用者態:
1.寫了一段程式碼,進行系統呼叫,假設呼叫號是333
2.執行這段程式碼的時候,到進行系統呼叫號的那一步時,將系統呼叫表和系統呼叫號對應的引數通過eax
等暫存器存入核心
3.之後,軟體中斷呼叫了0x80這個程式設計異常,這個程式設計異常對應中斷描述符表IDT中的第128項——也就是對應的系統門描述符。門描述符中含有一個預設的核心空間地址,指向系統呼叫處理程式
4.到系統呼叫處理程式後,因為所有的系統呼叫都會統一地轉到這個地址,所以要查系統呼叫表(已經傳入核心了),根據系統呼叫號進入對應的服務例程,執行系統呼叫。
5.呼叫後,返回中斷處理程式,一步步返回到使用者態,繼續執行下面的程式碼。
2.實驗環境
我分別在兩臺電腦上進行了實驗
其一;
Ubantu64位:ubuntu-18.04.1-desktop-amd64.iso
待編譯的核心:linux-4.15.0
虛擬機器:WMware-15
其二:
Ubuntu:ubuntu-20.04.1-desktop-amd64.iso
待編譯核心:Linux-5.4.0
虛擬機器:WMware-15
tips:虛擬機器建議配置磁碟空間不宜過小,核心編譯過程中會產生較多的臨時檔案,若空間太小,可能在編譯核心的時候發生磁碟空間不夠的情況,甚至可能導致Ubuntu系統無法開機(我就發生了這樣的錯誤,從網上搜尋安裝虛擬機器教程時,預設給20G的磁碟空間,自己裝的時候要注意給多點),記憶體太小會影響編譯速度,建議40GB以上,我給了80GB,記憶體給了3GB。
3.下載核心原始碼
1.從官網下載:https://www.kernel.org
但從官網下載核心原始碼太慢了,得將近5個小時,查詢後用命令列去其清華映象倉庫下載核心原始碼。但效果仍然不好,建議直接在終端裡使用apt-get下載
2.命令列中使用apt-get下載
輸入命令:
sudo apt-get install linux-source-5.4.0
但是出現了以下問題:
解決措施如下:參考於網路
在Ubuntu
中,有時候運用sudo apt-get instal
l 安裝軟體時,會出現一下的情況
E: Could not get lock /var/lib/dpkg/lock - open (11: Resource temporarily unavailable)
E: Unable to lock the administration directory (/var/lib/dpkg/), is another process using it?
在這個時候,主要是因為apt還在執行,此時的解決方案是
1、找到並且殺掉所有的apt-get
和apt
程序
執行下面的命令來生成所有含有 apt
的程序列表,你可以使用ps
和grep
命令並用管道組合來得到含有apt或者apt-get的程序。
ps -A | grep apt
找出所有的 apt
以及 apt-get
程序
$ sudo kill -9 processnumber
或者
$ sudo kill -SIGKILL processnumber
比如,下面命令中的9是 SIGKILL 的訊號數,它會殺掉第一個 apt 程序
$ sudo kill -9 程序ID
或者
$ sudo kill -SIGKILL 程序ID
2、刪除鎖定檔案
鎖定的檔案會阻止Linux
系統中某些檔案或者資料的訪問,這個概念也存在於Windows
或者其他的作業系統中。
一旦你運行了 apt-get
或者 apt
命令,鎖定檔案將會創建於 /var/lib/apt/lists/
、/var/lib/dpkg/
、/var/cache/apt/archives/
中。
這有助於執行中的 apt-get 或者 apt 程序能夠避免被其它需要使用相同檔案的使用者或者系統程序所打斷。當該程序執行完畢後,鎖定檔案將會刪除。當你沒有看到 apt-get 或者 apt 程序的情況下在上面兩個不同的資料夾中看到了鎖定檔案,這是因為程序由於某個原因被殺掉了,因此你需要刪除鎖定檔案來避免該錯誤。
首先執行下面的命令來移除 /var/lib/dpkg/
資料夾下的鎖定檔案:
$ sudo rm /var/lib/dpkg/lock
之後像下面這樣強制重新配置軟體包:
$ sudo dpkg --configure -a
也可以刪除 /var/lib/apt/lists/
以及快取資料夾下的鎖定檔案:
$ sudo rm /var/lib/apt/lists/lock
$ sudo rm /var/cache/apt/archives/lock
接下來,更新你的軟體包源列表:
$ sudo apt update
或者
$ sudo apt-get update
總結一下,對於 Ubuntu
(以及它的衍生版)使用者在使用 apt-get 或者 apt 也叫 aptitude 命令[7]時遇到的問題,我們已經用兩種方法來解決了。
依據以上方法,可以解決apt-get的問題,大概30分鐘左右,原始碼可以下好。
4.解壓縮核心原始碼
檔案下載好後:
在home
目錄下有充分的root權利,建議把它解壓到home
檔案下,Linux裡的壓縮檔案是以後綴tar.bz2結尾的,這裡有六個檔案,只有最後一個是需要的,其他的是因為使用apt-get下載會自動多下點別的,我不清楚是什麼
我將檔案解壓到home/wjw下了
解壓命令:
tar -xjf linux-source-5.4.0.tar.bz2 -C /home/wjw
關於解壓命令:
例如:將y資料夾下的壓縮包x.tar.bz2解壓到z裡
1.進入y資料夾
2.命令列:
tar -xjf x.tar.bz2 -C /z
5.linux新增系統呼叫號,修改系統呼叫表
在檔案**linux/arch/x86/entry/syscalls/syscall_64.tbl
**中
建議安裝vscode
來檢視和修改檔案
因為題目是修改長度為0,所以取名change0
在較新的核心版本下操作:
335 common change0 __x64_sys_change0
--------------------------------------------------------------------------------------
335 系統呼叫號
common—————分為32/64/common CPU處理位數 common就是兩個都可以
change0 取的系統呼叫名
__x64_sys_change0 服務例程入口地址
注意:在4的版本的linux
下,不用加**_64
,只要sys_change0
**
在修改核心原始碼的時候,如這一步的操作,建議複製334的系統呼叫號再在此基礎上修改,不然不知道核心中的間隔是空格還是Tab,比較容易格式出錯。
6.申明系統呼叫服務例程原型
在檔案**linux/includelinux/syscalls.h
**中
在檔案末尾新增:
asmlinkage long sys_change0(void *path, unsigned int length)
--------------------------------------------------------------------------------------
asmlinkage:
一個限定詞,通知編譯器僅僅從堆疊提取該函式的引數,而不是從暫存器中,因為從執行服務例程之前系統就已經將通過暫存器傳遞過來的引數值壓入核心堆疊了.
是和系統呼叫號一起傳入的嗎?對,引數通過ebx ecx rdx esi edi等暫存器傳遞,系統呼叫號和相關引數在內中斷開始時放入記憶體。
對於系統呼叫服務例程,可以直接從 system_call 函式壓入的堆疊中獲得引數,對引數的修改也可以一直在堆疊中進行。在 system_call 函式退出後,使用者應用可以直接從暫存器中獲得被修改過的引數。
並不是所有的系統呼叫服務例程都有實際的內容,有一個服務例程 sys_ni_syscall 除了返回-ENOSYS外不做任何其他工作,在kernel/sys_ni.c 檔案中定義。
long:
系統呼叫在使用者態和核心態返回值不同,使用者態返回int,核心態返回long
sys_change0:
系統呼叫服務例程原型,對應系統呼叫表中的
void *path:
要修改的指定檔案地址,之所以用void*是因為方便地址傳入不會發生錯誤
unsigned int length:
檔案大小,之所以加unsigned,是因為該目錄下的其他函式傳入關於長度的長度時都採取的是unsigned int
注意:引數之間都是'', ''逗號後空一格。
7、實現系統呼叫服務例程
路徑:linux/kernel/sys.c
為**change0
編寫服務例程sys_change0
**
SYSCALL_DEFINE2(change0, void __user*, path, unsigned int, length)
{
struct file *fp; //建立一個指向檔案的指標
char * cu_any =(char*)kmalloc(length,GFP_KERNEL);//在核心開闢一個使用者態檔案地址一樣大的空間
copy_from_user(cu_any,path,length); //將使用者態檔案地址字串複製到核心態剛剛建立的空間裡
fp = filp_open(cu_any,O_TRUNC,0); //在核心態以截斷式開啟該檔案並用指標指向它
filp_close(fp, NULL); //關閉開啟的檔案
kfree(cu_any); //釋放核心空間(檔案地址字串)
return 0;
}
-------------------------------------------------------------------------------------
SYSCALL_DEFINE2:
巨集,因為有兩個引數,所以DEFINE後面是2
1.change0:
函式名,對應系統呼叫表中的
2.void __user*:
__user巨集表示此指標為使用者空間對應程序的線性地址。(通過查程序的頁表得到實體地址)
__user巨集簡單告訴編譯器(通過 noderef)不應該解除這個指標的引用(因為在當前地址空間中它是沒有意義的)
我的理解:現在是在核心空間,如果不加__user 就會預設是在核心空間中的地址。而 void __user* 說明這是一個使用者空間的地址,不能直接進行拷貝等,要使用例如copy_from_user,copy_to_user等函式。
3.path:
檔案使用者空間的地址,之所以和void __user*分開是因為核心中的其他服務例程都是這樣定義的。連起來的意思就是定義一個void*的指標,這個指標指向使用者態,存放使用者態的檔案地址,不能直接進行拷貝要使用別的函式轉換。
4.unsigned int:
同理,核心中其他的服務例程呼叫都是這樣定義的
5.length:
檔案地址字串的長度
6.struct file *fp:
宣告fp是指標,指向FILE型別的物件,也就是指向檔案的指標。
7.char * cu_any =(char*)kmalloc(length,GFP_KERNEL);
定義了一個char *型別的cu_any,併為其開闢一個空間
其中:
kmalloc(length,GFP_KERNEL):
核心提供的記憶體分配函式,常用的以位元組為單位分配。
函式原型:
void *kmalloc(size_t size, gfp_t flags);
size_t size:需要多少位元組的記憶體
gfp_t flags:要分配的記憶體型別
這裡傳入指定檔案長度,故用length
GFP_KERNEL:查詢資料知道這是最一般使用的標誌,意思是這個分配代表執行在核心空間。詳細後面再說
總之:這裡在核心開闢了和傳入檔案大小一致的記憶體空間
(char*)
因為定義的空間是char *型別,因此這裡進行一個型別轉換
8.copy_from_user(cu_any,path,length);
將使用者態下檔案複製並傳入核心
函式原型:
unsigned long __copy_from_user (void * to, const void __user * from, unsigned long n);
void * to:目標地址,在核心空間中,因此我引數用的是新建立的cu_any
const void __user * from:源地址,在使用者空間中.傳入使用者空間地址,void __user*, path,格式相符
unsigned long n:要複製的位元組數。unsigned int, length,傳入檔案長度,這也是為什麼宣告系統呼叫原型要寫這兩個引數
9.fp = filp_open(cu_any,O_TRUNC,0777);
使fp指標指向以截斷式的方法開啟的檔案
函式原型:
struct file *filp_open(const char *filename, int flags, umode_t mode)
const char *filename:要開啟或建立檔案的名稱(包括路徑部分,此時檔案以從使用者態複製到核心態,是存在cu_any裡
int flags:引數檔案的開啟方式,其取值與標準庫中的open相應引數類似,可以取O_CREAT,O_RDWR,O_RDONLY等,O_TRUNC代表截斷式,若檔案存在,則長度被截為0,屬性不變,因此用來剛好
umode_t mode:建立檔案時使用,設定建立檔案的讀寫許可權,0777代表可讀可寫可執行,其實一般寫0。
該函式返回strcut file*結構指標,供後繼函式操作使用,該返回值用IS_ERR()來檢驗其有效性。
10.filp_close(fp, NULL)
關閉fp指向的檔案
函式原型:
int filp_close(struct file *filp, fl_owner_t id)
struct file *filp:filp_open()函式的返回值,也就是把開啟的檔案關上
fl_owner_t id:根據查詢資料,表示檔案的關閉結果,值0或NULL即代表成功關閉
11.kfree(cu_any)
釋放核心中的記憶體
8.核心函式介紹:
1.關於kmalloc
:
https://www.kernel.org/doc/htmldocs/kernel-api/API-kmalloc.html
https://blog.csdn.net/macrossdzh/article/details/5627274
對於常用的以位元組為單位的分配來說,核心提供的函式時kmalloc(),其與使用者空間的malloc()非常相似,但它多了一個flags函式。kmalloc()函式是一個簡單的介面,用它可以獲得以位元組為單位的一塊記憶體。
kmalloc()在#include <linux/slab.h> 中宣告
void *kmalloc(size_t size, gfp_t flags);
該函式返回一個void *的指標,指向記憶體塊,所分配的記憶體在物理上是連續的。
出錯時返回NULL,表示記憶體不夠。
size_t size:需要多少位元組的記憶體
gfp_t flags:要分配的記憶體型別
該flags引數可能是以下之一:
GFP_KERNEL-核心記憶體的正常分配. 可能睡眠
GFP_USER-用來為使用者空間頁來分配記憶體; 它可能睡眠.
GFP_ATOMIC-用來從中斷處理和程序上下文之外的其他程式碼中分配記憶體. 從不睡眠.
GFP_HIGHUSER -如同 GFP_USER, 但是從高階記憶體分配, 如果有. 高階記憶體在下一個子節描述.。
GFP_NOIO -嘗試獲取記憶體時根本不執行任何I / O操作。
GFP_NOFS -嘗試獲取記憶體時請勿進行任何fs呼叫。
GFP_NOWAIT -分配不會睡眠。
__GFP_THISNODE -僅分配節點本地記憶體。
GFP_DMA-適用於DMA的分配。僅應用於kmalloc快取記憶體
等等
關於GFP_KERNEL:
最一般使用的標誌, GFP_KERNEL, 意思是這個分配(內部最終通過呼叫 __get_free_pages 來進行, 它是 GFP_ 字首的來源) 代表執行在核心空間的程序而進行的. 換句話說, 這意味著呼叫函式是代表一個程序在執行一個系統呼叫. 使用 GFP_KENRL 意味著 kmalloc 能夠使當前程序在少記憶體的情況下睡眠來等待一頁. 一個使用 GFP_KERNEL 來分配記憶體的函式必須, 因此, 是可重入的並且不能在原子上下文中執行. 噹噹前程序睡眠, 核心採取正確的動作來定位一些空閒記憶體, 或者通過重新整理快取到磁碟或者交換出去一個使用者程序的記憶體.
2.關於copy_from_user(cu_any,path,length)
https://www.kernel.org/doc/htmldocs/kernel-api/API—copy-from-user.html
https://blog.csdn.net/liuhangtiant/article/details/85227125
https://www.cnblogs.com/LxwEmbedded/p/4532924.html
__copy_from_user —從使用者空間複製資料塊,而無需進行檢查。
unsigned long __copy_from_user (void * to, const void __user * from, unsigned long n);
完整原始碼:
static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n)
{
unsigned long res = n;
kasan_check_write(to, n);
if (access_ok(VERIFY_READ, from, n)) {
check_object_size(to, n, false);
res = __arch_copy_from_user(to, from, n);
}
if (unlikely(res))
memset(to + (n - res), 0, res);
return res;
}
其中:
void * to:目標地址,在核心空間中。
const void __user * from:源地址,在使用者空間中
unsigned long n:要複製的位元組數。
說明:
copy_from_user函式的目的是從使用者空間拷貝資料到核心空間,失敗返回沒有被拷貝的位元組數,成功返回0.
該函式同時需要進行核心關於異常出錯的處理.從使用者空間拷貝資料到核心中時必須非常小心,如果使用者空間的資料地址是個非法的地址,或是超出使用者空間的範圍,或是那些地址還沒有被對映到,都可能對核心產生很大的影響,如oops,或者被造成系統安全的影響.所以copy_from_user函式的功能就不只是從使用者空間拷貝資料那樣簡單了,它還要做一些指標檢查以及處理這些問題的方法.
3.關於filp_open
:
https://elixir.bootlin.com/linux/latest/source/fs/open.c#L1127
https://www.cnblogs.com/leaven/archive/2010/05/26/1744274.html
struct file *filp_open(const char *filename, int flags, umode_t mode)
{
struct filename *name = getname_kernel(filename);
struct file *file = ERR_CAST(name);
if (!IS_ERR(name)) {
file = file_open_name(name, flags, mode);
putname(name);
}
return file;
}
const char *filename:要開啟或建立檔案的名稱(包括路徑部分
int flags:引數檔案的開啟方式,其取值與標準庫中的open相應引數類似,可以取O_CREAT,O_RDWR等
umode_t mode:建立檔案時使用,設定建立檔案的讀寫許可權
關於O_TRUNC:
int _open(char *pathname,int access)為讀或寫開啟一個檔案,
按後按access來確定是讀檔案還是寫檔案,access值見下表
┌──────┬────────────────────┐
│access值 │意義 │
├──────┼────────────────────┤
│O_RDONLY │讀檔案 │
│O_WRONLY │寫檔案 │
│O_RDWR │即讀也寫 │
│O_NOINHERIT │若檔案沒有傳遞給子程式,則被包含 │
│O_DENYALL │只允許當前處理必須存取的檔案 │
│O_DENYWRITE │只允許從任何其它開啟的檔案讀 │
│O_DENYREAD │只允許從任何其它開啟的檔案寫 │
│O_DENYNONE │允許其它共享開啟的檔案 │
└──────┴────────────────────┘
int open(char *pathname,int access[,int permiss])為讀或寫開啟一個檔案,
按後按access來確定是讀檔案還是寫檔案,access值見下表
┌────┬────────────────────┐
│access值│意義 │
├────┼────────────────────┤
│O_RDONLY│讀檔案 │
│O_WRONLY│寫檔案 │
│O_RDWR │即讀也寫 │
│O_NDELAY│沒有使用;對UNIX系統相容 │
│O_APPEND│即讀也寫,但每次寫總是在檔案尾新增 │
│O_CREAT │若檔案存在,此標誌無用;若不存在,建新檔案 │
│O_TRUNC │若檔案存在,則長度被截為0,屬性不變 │
│O_EXCL │未用;對UNIX系統相容 │
│O_BINARY│此標誌可顯示地給出以二進位制方式開啟檔案 │
│O_TEXT │此標誌可用於顯示地給出以文字方式開啟檔案│
└────┴────────────────────┘
permiss為檔案屬性,可為以下值:
S_IWRITE允許寫 S_IREAD允許讀 S_IREAD|S_IWRITE允許讀、寫
int creat(char *filename,int permiss) 建立一個新檔案filename,並設定讀寫性。permiss為檔案讀寫性,可以為以下值S_IWRITE允許寫 S_IREAD允許讀 S_IREAD|S_IWRITE允許讀、寫
int _creat(char *filename,int attrib) 建立一個新檔案filename,並設定檔案屬性。attrib為檔案屬性,可以為以下值FA_RDONLY只讀 FA_HIDDEN隱藏 FA_SYSTEM系統
int creatnew(char *filenamt,int attrib) 建立一個新檔案filename,並設定檔案屬性。attrib為檔案屬性,可以為以下值FA_RDONLY只讀 FA_HIDDEN隱藏 FA_SYSTEM系統
4.關於filp_closs()
https://www.cnblogs.com/chorm590/p/12565991.html
int filp_close(struct file *filp, fl_owner_t id)
{
int retval = 0;
if (!file_count(filp)) {
printk(KERN_ERR "VFS: Close: file count is 0\n");
return 0;
}
if (filp->f_op->flush)
retval = filp->f_op->flush(filp, id);
if (likely(!(filp->f_mode & FMODE_PATH))) {
dnotify_flush(filp, id);
locks_remove_posix(filp, id);
}
fput(filp);
return retval;
}
引數1就是filp_open()函式的返回值。
引數2一般填0即可。
返回值表示這個檔案的關閉結果,值0表示成功關閉。
5.關於kfree()
https://www.kernel.org/doc/htmldocs/kernel-api/API-kfree.html
void kfree(void kfree ( const void * objp) ;
如果objp為NULL,則不執行任何操作。
只能用來釋放kmalloc()申請的動態空間。不然會導致核心崩潰,出現oops資訊
9.重新編譯核心
1.清除殘留的.config檔案
開始編譯前要清理殘留的.config和.o檔案,每次編譯核心都要清理一遍
可能會提醒安裝ncurses
包,所以建議先安裝好再清理
命令:
sudo apt-get install libncurses5-dev
若安裝出現問題,參考下載核心原始碼時的操作:
1:查詢所有apt相關的程序,並用命令殺死。
命令:
ps afx|grep apt
sudo kill -9 2873
--------------------------------------------------------------------------------------
2873是程序號
2:刪除鎖定檔案
sudo rm /var/lib/dpkg/lock
sudo dpkg --configure -a
sudo apt update
3:問題解決,執行apt install
命令成功
安裝好包後,執行
make mrproper
2.配置核心
提前安裝好相關的包
sudo apt-get install libncurses5-dev
--------------------------------------------------------------------------------------
若上一步沒裝沒報錯,這裡應該會出錯,所以建議提前裝
要在root許可權下make menuconfig
sudo make menuconfig
仍然出錯了,如下:
提示找不到flex
於是參考資料:
列一下Make Menu過程中遇到錯誤(Ubuntu18.04):
Q1:3.14.38的核心
[email protected]:/home/simon/FeiLing/src/linux-3.14.38# make menuconfig
*** Unable to find the ncurses libraries or the
*** required header files.
*** 'make menuconfig' requires the ncurses libraries.
***
*** Install ncurses (ncurses-devel) and try again.
***
/home/simon/FeiLing/src/linux-3.14.38/scripts/kconfig/Makefile:199: recipe for target 'scripts/kconfig/dochecklxdialog' failed
make[1]: *** [scripts/kconfig/dochecklxdialog] Error 1
Makefile:512: recipe for target 'menuconfig' failed
make: *** [menuconfig] Error 2
A1: sudo apt-get install ncurses-devel
Q2:linux-4.17.2核心
[email protected]:/home/simon/Src/linux-4.17.2# make menuconfig
YACC scripts/kconfig/zconf.tab.c
/bin/sh: 1: bison: not found
scripts/Makefile.lib:196: recipe for target 'scripts/kconfig/zconf.tab.c' failed
make[1]: *** [scripts/kconfig/zconf.tab.c] Error 127
Makefile:528: recipe for target 'menuconfig' failed
make: *** [menuconfig] Error 2
A2:apt-get install bison -y
Q3:
[email protected]:/home/simon/Src/linux-4.17.2# make menuconfig
YACC scripts/kconfig/zconf.tab.c
LEX scripts/kconfig/zconf.lex.c
/bin/sh: 1: flex: not found
scripts/Makefile.lib:188: recipe for target 'scripts/kconfig/zconf.lex.c' failed
make[1]: *** [scripts/kconfig/zconf.lex.c] Error 127
Makefile:528: recipe for target
A3: sudo apt-get install flex
輸入命令:
sudo apt-get install flex
安裝好後繼續配置核心
但是又報錯了,如下:
於是輸入命令
sudo apt-get install bison -y
繼續配置核心
出現如下框:
按照書上選擇
save後
選擇ok
但:
查詢資料:
應該是沒有問題的
https://blog.csdn.net/kang___xi/article/details/80556633
這部分不理解是什麼意思,查詢資料如下:
https://blog.csdn.net/wangyachao0803/article/details/81380889
文章太長,附上鍊接
後面再次嘗試沒有出現如上錯誤。
3.編譯核心
先安裝好openssl
sudo apt-get install libssl-dev
開始編譯:
make -j8
--------------------------------------------------------------------------------------
這裡參考虛擬機器給的是幾個核心,就j後面寫幾
大約30分鐘後編譯完成,未報錯
(之前不斷嘗試修改核心原始碼的時候報了很多錯,但並未記錄下來)
4.編譯模組
meke modules
之前編譯模組時出現瞭如下錯誤:
查詢資料得知:
輸入命令:
sudo make CONFIG_DEBUG_SECTTION_MISMATH=y
會重新編譯一次核心,這次非常慢!!
編譯好後再編譯模組,無問題。
5.安裝核心
安裝模組:
make modules_install
安裝核心:
make install
6.配置載入程式
update-grub2
7.重啟系統
reboot
新核心開機後;
uname-a
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-ZFRG2Rzf-1611652521523)(…/typora_tupian/image-20201108110136519.png)]
至此,尚未修改系統呼叫的核心編譯完成。
10.編寫使用者態程式測試新系統呼叫
具體步驟參考:實驗結果演示 部分
測試程式如下:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
#define __NR_mysyscall 333
int main() {
const char * path = "/home/weiwei/test.txt";
syscall(__NR_mysyscall,(void *)path,strlen(path));
}
-------------------------------------------------------------------------------------
這是在舊版本的linux編寫的測試,新版本應將系統呼叫號改為335
11.思考
其實在做的時候,我用更高版本的linux
系統實驗時,執行測試程式時,被killed了,核心編譯沒有問題,後續我再去研究研究,暫時得先做別的實驗了。