目錄
- 檔案描述符(句柄)
- 系統呼叫介面:
- open()
- write()
- lseek()
- read()
- dup2()
- 重定向
- Linux檔案系統(inode與軟硬鏈接)
- inode
- 硬鏈接
- 軟連接
- 動態庫與靜態庫
- 概念
- 靜態庫
- 動態庫
檔案描述符(句柄)
- 計算機磁盤上有很多個檔案,作業系統要對這些檔案進行管理,就必須建立相應的資料結構(struct結構體)來描述這個檔案,然后在通過這個檔案進行管理,事實上顯示幕,鍵盤對于作業系統來講也就是一個檔案,將其用結構體描述起來,與其他檔案格一樣,寫程式時通過這個不同檔案的結構體來對檔案進行操作,
- 一個行程可能會打開多個檔案,每個行程也要對這些被打開的檔案進行管理,所以在每個行程的pcb(行程控制塊)中會有一個結構體指標陣列,陣列的每一個元素都是一個指向描述檔案的結構體的指標,每打開一個檔案,這個陣列就多一個元素,關閉一個檔案陣列對應位置就被置為空,檔案描述符就是該陣列的下標,通過檔案描述符可以找到描述該檔案的結構體,進而可以對檔案進行操作,
- 行程運行開始時默認打開三個檔案:標準輸入,標準輸出,標準錯誤,
- 作業系統會為新打開的檔案分配最小的未被使用的陣列下標,所以新打開檔案時默認分配的檔案描述符從3開始
系統呼叫介面:
open()

open()函式,打開一個檔案,第一個引數,打開檔案的路徑與檔案名,默認在當前路徑,第二個引數,打開檔案的方式(可一次指定多個引數,用"|"隔開),第三個引數,如果要打開的檔案不存在且第二個引數表明不存在就創建檔案,第三個引數為新創建檔案的權限,
第二個引數有這幾個常用的:
O_CREAT:如果要打開的檔案不存在,則創建該檔案
O_RDONLY:打開的檔案有可讀權限
O_WRONLY:打開的檔案有可寫權限
O_RDWR:打開的檔案具有可讀可寫權限
O_TRUNC:打開檔案時清空檔案內容
O_APPEND:以追加方式打開檔案
回傳值:成功回傳檔案描述符,失敗回傳-1;

)
打開當前目錄下的test.txt檔案,打開的檔案具有可讀可寫權限,如果檔案不存在則創建該檔案且檔案的權限為0664.
write()

write()函式,往檔案內寫內容,第一個引數,檔案描述符,往哪個檔案里寫入;第二個引數,字串,要寫入的內容;第三個引數,期望寫入多少個位元組,
回傳值:實際寫入的位元組數,

打開一個檔案,往檔案里寫入字串"i like linux!",
lseek()
lseek()函式

lseek()函式,調整當前檔案的指標,每次對檔案進行操作后,指標就會跳轉到操作結束的位置,下一次對檔案進行操作時就從該位置開始(如寫操作,第一次寫入一個字串,指標指向字串的結尾,這使要從檔案讀內容時就從該位置開始),
第一個引數:檔案描述符,對哪個檔案進行操作;
第二個引數:從whence指定的位置,向前或者向后移動指定位元組數;
第三個引數:
SEEK_SET:調到檔案起始位置
SEEK_CUR:調到檔案當前讀寫的位置
SEEK_END:調到檔案末尾位置
回傳值:回傳當前讀寫位置相對于檔案開始位置的偏移量(位元組),

執行寫操作后,調整檔案指標到檔案最開始的位置,
read()

read()函式,從檔案中讀取內容,
第一個引數:檔案描述符,從哪個檔案中讀取內容;
第二個引數:讀取到buf內;
第三個引數:期望讀取多少個位元組;
回傳值:實際讀到的位元組數,

將檔案內容讀到字串buff里,
dup2()

dup2()函式,將舊的舊的檔案描述符的內容拷貝到新的檔案描述符里面,兩個檔案描述符指向指向同一個檔案,
重定向

-
實際上就是往檔案里面寫內容,執行echo命令時,實際上就是往顯示幕列印字串,從檔案的角度來看就是往1號檔案(標準輸出)里寫入了"hello world"字串,在增加重定向符號時,就相當于關閉當前行程的1號檔案,然后在當前目錄打開重定向符號后面的檔案名,如果沒有則創建,根據檔案描述符分配規則,新打開的的檔案的檔案描述符會是1,然后還是將字串寫入1號檔案,最后關閉該檔案,重新打開標準輸出,標準輸出檔案描述符還是1,

-
追加重定向實際上就是以追加方式打開檔案,
Linux檔案系統(inode與軟硬鏈接)
inode
-
Linux使用ext2檔案系統,磁盤被分為好多個塊,檔案存放在塊中,為了提高效率,檔案并不一定存放在連續的塊中,所以檔案被分別存放在哪些塊里需要被保存起來,并且一個檔案不只有檔案內容,還有檔案的屬性資訊,這些東西不屬于檔案內容,但是也要被保存起來,由此就產生了inode,
-
每一個對應一個唯一的inode編號,inode中保存該檔案的所有屬性資訊以及該檔案被保存在哪些塊中,所用通過inode可以找到檔案具體存放在磁盤的那個位置,從而對檔案進行操作,
硬鏈接
鏈接就是將A檔案指向B檔案,對A檔案進行操作就相當于對B檔案進行操作
硬鏈接本質上沒有創建新的檔案,只是有一個檔案名,它的inode與他連接的檔案的inode一樣,
命令
ln 原檔案 要創建的鏈接檔案
兩個檔案的inode一樣,
軟連接
軟連接本質上是創建了一個新的檔案,該檔案里保存了鏈接物件的檔案名與路徑,
命令
ln -s 原檔案 要創建的鏈接檔案

兩個檔案的inode不一樣,
動態庫與靜態庫
概念
- 我們寫的程式都會包含頭檔案,因為里面有我們要用到的庫函式,這里所說的庫就是動態庫或靜態庫,
- 靜態庫(.a):程式在編譯鏈接的時候把庫的代碼鏈接到可執行檔案中,程式運行的時候將不再需要靜態庫
- 動態庫(.so):程式在運行的時候才去鏈接動態庫的代碼,多個程式共享使用庫的代碼,
- 一個與動態庫鏈接的可執行檔案僅僅包含它用到的函式入口地址的一個表,而不是外部函式所在目標檔案的整個機器碼
- 在可執行檔案開始運行以前,外部函式的機器碼由作業系統從磁盤上的該動態庫中復制到記憶體中,這個程序稱為動態鏈接(dynamic linking)
- 動態庫可以在多個程式間共享,所以動態鏈接使得可執行檔案更小,節省了磁盤空間,作業系統采用虛擬記憶體機制允許物理記憶體中的一份動態庫被要用到該庫的所有行程共用,節省了記憶體和磁盤空間,
靜態庫
靜態庫的命名
lib[庫名稱].a
假如一個庫的檔案名為libhello.a,那么這個庫的實際名稱為hello

這里我寫了三份代碼,myprint.h里面放函式的宣告,myprint.c里面放函式的實作,
print.c里面呼叫函式,
要把檔案生成庫檔案,首先要將所有的.c檔案編譯為.o檔案,然后將.o檔案進行打包生成靜態庫,
gcc -c *.c
將所有的.o檔案打包成靜態庫
ar -rc libprint.a myprint.o print.o

然后創建mylib檔案夾,將頭檔案與靜態庫放到mylib檔案下,

這樣其他程式要使用該頭檔案與其頭檔案內定義的函式,就可以在編譯時指定頭檔案查與靜態庫的路徑路徑,然后就可以直接使用了,

可以看到,我當前寫的程式沒有包含stdio.h,也沒有定義Print函式,但是包含了myprint.h,只要在編譯時指定頭檔案與要使用的庫的路徑,就可以正常使用了,

//-I
指要包含的頭檔案的路徑,這里頭檔案在當前目錄的mylib/include路徑下
//-L
指庫檔案路徑,這里庫檔案在當前目錄的mylib/lib路徑下
//l
指要用的庫的名字,注意這里的名字要去除前綴與后綴,
//-static
指靜態鏈接,即鏈接的是一個靜態庫

然后我們對其進行編譯運行也沒有任何問題,
動態庫
動態庫的命名與靜態庫的名稱稍有不同,
lib[庫名稱].so
先將所有的.c檔案生成.o檔案,
gcc -fPIC -c *.c
-fPIC命令列標記告訴GCC產生的代碼不要包含對函式和變數具體記憶體位置的參考,這是因為現在還無法知道使用該訊息代碼的應用程式會將它連接到哪一段記憶體地址空間,

然后將所有.o檔案生成libprint.so動態庫檔案,
將生成的動態庫與.h檔案放在mylib.so檔案下,

編譯時鏈接動態庫

但我們在運行時卻發現不可以運行,因為鏈接動態庫時程式在運行時遇到庫函式才會把庫函式加載到記憶體里運行,而我們只是在編譯時加入動態庫的路徑,程式運行時并不知道動態庫在哪里,

這個環境變數是指程式在運行時找動態庫的路徑,只需要把我們的動態庫加進去就可以了,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/273632.html
標籤:其他
