應老大要求,從今天開始APUE的讀書筆記。其實就是一些比較需要注意的點
第一章. UNIX基礎知識
一、登陸
拿到一臺Linux主機,最新開始的是登陸。
Linux 登陸的驗證是靠 /etc/passwd 這個文件的。
$ grep kiosk /etc/passwd
kiosk:x:1000:1000:kiosk:/home/kiosk:/bin/bash
這些子段分別是用戶名,加密後的密碼,用戶id,用戶組id,描述,家目錄,使用的shell。
整個登陸過程如下:
read /etc/inittab get username in tty get password
init ---------------------> mingetty ---------------------------> login ------------------/etc/pam.d-----------> call PAM modules -------------> act/rej
Linux PAM是一個通用的認證機制,是以庫的模式。
二、文件和目錄
文件系統:
目錄(directory)是一個包含目錄項的文件,在邏輯上,可以認爲每個目錄項都包含一個文件名,同時還包含說明該文件屬性的信息。文件屬性是:文件類型,文件長度,文件所有者,文件的許可權(例如,其他用戶能否能訪問該文件),文件最後的修改時間等,可以使用stat和fstat去查看詳細信息。
使用stat命令去查看文件的詳細信息。
$ stat google-chrome.desktop
File: ‘google-chrome.desktop’
Size: 8392 Blocks: 24 IO Block: 4096 regular file
Device: fd02h/64770d Inode: 134751621 Links: 1
Access: (0755/-rwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root)
Context: unconfined_u:object_r:user_home_t:s0
Access: 2018-03-19 15:06:39.280035865 +0800
Modify: 2018-03-08 17:50:18.060889472 +0800
Change: 2018-03-08 17:50:26.282852535 +0800
Birth: -
解釋一下:
- File:顯示文件名
- Size:顯示文件大小
- Blocks:文件使用的數據塊總數
- IO Block:IO塊大小
- regular file:文件類型(常規文件)
- Device:設備編號
- Inode:Inode號
- Links:鏈接數
- Access:文件的權限
- Gid、Uid:文件所有權的Gid和Uid。
三、輸入和輸出
首先清楚文字描述符的概念。
文字描述符是一個小的非負整數,內核用以標識一個特定進程正在存訪的文件。當內核打開一個現存文件或創建一個新文件時,它就返回一個文件描述符。當讀、寫文件時,就可使用它。
按慣例,每當運行一個新程序時,所有的 s h e l l都爲其打開三個文件描述符:標準輸入、標準輸出以及標準出錯。如果像簡單命令 l s那樣沒有做什麼特殊處理,則這三個描述符都連向終端。
文件描述符的本質是 數組元素的下標。
右側的表稱爲i節點表,在整個系統中只有1張。該表可以視爲結構體數組,該數組的一個元素對應於一個物理文件。
中間的表稱爲文件表,在整個系統中只有1張。該表可以視爲結構體數組,一個結構體中有很多字段,其中有3個字段比較重要:
左側的表稱爲文件描述符表,每個進程有且僅有1張。該表可以視爲指針數組,數組的元素指向文件表的一個元素。最重要的是:數組元素的下標就是大名鼎鼎的文件描述符。
open系統調用執行的過程:新建一個i節點表元素,打開的物理文件(如果對應於該物理文件的i節點元素已經建立,就不做任何操作);新建一個文件表的元素,根據open的第2個參數設置file status flags字段,將current file offset字段置0,將v-node ptr指向剛建立的i節點表元素;在文件描述符表中,尋找1個尚未使用的元素,在該元素中填入一個指針值,讓其指向剛建立的文件表元素。最重要的是:將該元素的下標作爲open的返回值返回。
不帶緩存的IO和標準IO
不帶緩存的IO是指 函數open、read、write、lseek以及close提供了不用緩存的 I / O。這些函數都用文件描述符進行工作。 如read和write函數都有一個參數--- ‘buf’。這個參數用來指向讀取或者寫入的地方。通常我們都是自己新建一個數組,用來讀取或者寫入,這就是不帶緩存的IO,需要自己考慮讀取內容的存放位置(或者寫入內容的存放位置),同時還得考慮讀取或者寫入的長度。
與之相對的就是標準IO。最常見的是printf()。
四、進程控制
有三個用於進程控制的主要函數: fork、exec和waitpid(exec函數有六種變體,但經常把它們統稱爲exec函數)。
首先看下面的代碼
#include <sys/types.h>
#include <sys/wait.h>
#include "ourhdr.h"
#include "myerror.h"
int main(void){
char buf[MAXLINE];
pid_t pid;
int status;
printf("%% ");
while(fgets(buf,MAXLINE,stdin) != NULL) {
buf[strlen(buf) - 1] = 0; /*replace newline with null*/
if ((pid=fork()) < 0)
err_sys("fork error");
else if (pid == 0) { /*child*/
execlp(buf,buf,(char *) 0);
err_ret("couldn't execute: %s",buf);
exit(127);
}
if ((pid = waitpid(pid,&status,0)) < 0) /*parent*/
err_sys("waitpid error");
printf("%% ");
}
exit(0);
}
調用fork創建一個新進程。新進程是調用進程的複製品,故稱調用進程爲父進程,新創建的進程爲子進程。 fork對父進程返回新子進程的非負進程ID,對子進程則返回0。因爲fork創建一新進程,所以說它被調用一次 (由父進程),但返回兩次(在父進程中和在子進程中 )。
在子進程中,調用 execlp以執行從標準輸入讀入的命令。這就用新的程序文件替換了子進程。fork和跟隨其後的exec的組合是某些操作系統所稱的產生一個新進程。
子進程調用 execlp執行新程序文件,而父進程希望等待子進程終止,這一要求由調用waitpid實現,其參數指定要等待的進程 (在這裏, pid參數是子進程 I D )。waitpid函數也返回子進程的終止狀態( status變量)。在此簡單程序中,沒有使用該值。如果需要,可以用此值精確地確定子進程是如何終止的。
$ ./a.out
% date
2018年 03月 19日 星期一 17:24:24 CST
% pwd
/home/kiosk/C/apue
%
五、UNIX時間值
UNIX中有三個時間值。
時鐘時間,用戶cpu時間,系統cpu時間。
時鐘時間(牆上時鐘時間wall clock time):從進程從開始運行到結束,時鐘走過的時間,這其中包含了進程在阻塞和等待狀態的時間。
用戶CPU時間:就是用戶的進程獲得了CPU資源以後,在用戶態執行的時間。
系統CPU時間:用戶進程獲得了CPU資源以後,在內核態的執行時間。
進程的三種狀態爲阻塞、就緒、運行。
時鐘時間 = 阻塞時間 + 就緒時間 +運行時間
用戶CPU時間 = 運行狀態下用戶空間的時間
系統CPU時間 = 運行狀態下系統空間的時間。
$ time grep "_POSIX_SOURCE" /usr/lib64/*.h &> /dev/null
real 0m0.004s
user 0m0.003s
sys 0m0.001s