1.2 UNIX體系結構linux
從嚴格意義上,可將操做系統定義爲一種軟件(內核),它控制計算機硬件資源,提供程序運行環境。內核的接口被稱爲系統調用。公用函數庫構建在系統調用接口之上,應用軟件便可使用公用函數庫,也可以使用系統調用。shell是一種特殊的應用程序,它爲運行其餘應用程序提供了一個接口shell
從廣義上,操做系統包括了內核和一些其餘軟件,這些軟件使得計算機可以發揮做用,並給予計算機以獨有的特性(軟件包括系統實用程序,應用軟件,shell以及公用函數庫等)編程
1.3 shellbash
shell是一個命令行解釋器,它讀取用戶輸入,而後執行命令。用戶一般用終端(交互式shell),有時則經過文件(shell script)向shell進行輸入編程語言
UNIX系統常見shell:函數
名稱 路徑ui
Boune shell: /bin/shspa
Bourne-again shell: /bin/bash操作系統
C shell: /bin/csh.net
Korn shell: /bin/ksh
TENEX C shell: /bin/tcsh
C shell其控制流相似於C語言,它支持Bourne shell沒有的某些特點功能,例如做業控制、歷史記憶機制以及命令行編輯等
Korn shell它與Bourne shell向上兼容,並具備使C shell普遍獲得應用的一些特點功能,包括做業控制以及命令行編輯
Bourne-again shell是GNUshell,全部linux都提供這種shell,它被設計成遵循POSIX的,同時也保留了與Bourne shell的兼容性。它支持C shell和Korn shell二者的特點功能
TENEX C shell 是C shell的增強版本。它從TENEX操做系統借鑑了不少特點,例如命令完備。TENEX C shell在C shell的基礎上增長了不少特徵,常被用來替換C shell
注:linux默認shell是Bourne-again shell.事實上,/bin/sh將連接到/bin/bash.FreeBSD 和Mac OS X的默認用戶shell是TENEX C shell,可是由於使用C shell編程語言極其困難,因此它們使用Bourne shell編寫用於管理方面的shell腳本
1.4文件和目錄
1.文件系統
UNIX文件系統是目錄和文件組成的一種層次結構,目錄的起點稱爲根(root),其名字是一個字符/。文件屬性是指文件類型(是普通文件仍是目錄)、文件大小、文件全部者、文件權限以及文件最後的修改時間等。
2.文件名
目錄中的各個名字稱爲文件名。不能出如今文件名中的字符只有斜線(/)和空操做符(null)兩個。
建立新目錄時會自動建立兩個文件名:.(當前目錄),..(父目錄)
3.路徑名
以斜線開頭的路徑名稱稱爲絕對路徑名,不然稱爲相對路徑名
實例:1-1 列出一個目錄中的全部文件
1 #include"apue.h" 2 #include<dirent.h> 3 int main(int argc,char *argv[]) //argc表示參數個數;argv表示參數內容 4 { 5 DIR *dp; 6 struct dirent *dirp; 7 if(argc!=2) 8 err_quit("usage:ls directory_name"); 9 if((dp=opendir(argv[1]))==NULL) //opendir函數返回指向DIR結構的指針 10 err_sys("can't open%s",argv[1]); 11 while((dirp=readdir(dp))!=NULL) //readdir返回一個指向dirent結構的指針 12 printf("%s\n",dirp->d_name); 13 closedir(dp); 14 exit(0); 15 }
這段代碼相似於ls命令,不過這段代碼只打印一個目錄中各個文件的名字,不顯示其餘信息
編譯方法:$ cc xxx.c 編譯
$ ./a.out 運行
(可能不少初學者在編譯這段代碼時會出現找不到apue.h,這裏我發個配置apue.h頭文件的連接 http://www.linuxidc.com/Linux/2013-01/77686.htm)
1.5輸入與輸出
1.文件描述符
文件描述符一般是一個小的非負整數,內核用它標識一個特定進程訪問的文件。當內核打開一個已有文件或建立一個新文件時,它返回一個文件描述符。在讀寫文件時,就可使用它。
3.不用緩衝的I/O
函數open、read、write、lseek以及close提供了不用緩衝的I/O。這些函數都使用文件描述符
程序清單1_2 將標準輸入複製到標準輸出
1 #include"apue.h" 2 #define BUFFSIZE 4096 3 int main(void) 4 { 5 int n; 6 char buf[BUFFSIZE]; 7 while((n=read(STDIN_FILENO,buf,BUFFSIZE))>0) 8 if(write(STDOUT_FILENO,buf,n)!=n) 9 err_sys("write error"); 10 if(n<0) 11 err_sys("read error"); 12 exit(0); 13 }
4.標準I/O
標準I/O函數提供一種對不用緩衝I/O函數的帶緩衝的接口。使用標準I/O函數能夠無需擔憂如何選取最佳的緩衝區大小,例如1_2中BUFFSIZE常量的大小。使用標準I/O函數的另外一個優勢是簡化了對輸入行的處理。例如,fgets函數讀一完整的行,而read函數讀指定字節數。(printf函數即是標準I/O函數)
實例:1_3 用標準I/O將標準輸入複製到標準輸出
1 #include"apue.h" 2 int main() 3 { 4 int c; 5 while((c=getc(stdin))!=EOF) 6 if(putc(c,stdout)==EOF) 7 err_sys("output error"); 8 if(ferror(stdin)) 9 err_sys("input error"); 10 exit(0); 11 }
附:無緩衝I/O操做和標準I/O操做的區別 http://blog.csdn.net/cowbane/article/details/6630298
1.6程序和進程
1.程序是存放在磁盤上,處於某個目錄中的一個可執行文件。使用6個exec函數中的一個由內核將程序讀入存儲器,並使其執行
2.進程和進程ID
程序的執行實例被稱爲進程。UNIX系統確保每一個進程都有一個唯一的數字標識符,稱爲進程ID,進程ID老是一非負整數
實例:1_4 打印進程ID
1 #include"apue.h" 2 int main() 3 { 4 printf("hello world from process ID %d\n",getpid()); 5 exit(0); 6 }
此程序運行時,它調用函數getpid獲得其進程ID
3.進程控制
有三個用於進程控制的主要函數:fork,exec和waitpid
實例: 1_5 從標準輸入讀命令並執行(類shell程序)
1 #include"apue.h" 2 #include<sys/wait.h> 3 4 5 int main(void) 6 { 7 char buf[MAXLINE]; 8 pid_t pid; 9 int status; 13 printf("%% "); 14 while(fgets(buf,MAXLINE,stdin)!=NULL){ 15 if(buf[strlen(buf)-1]=='\n') 16 buf[strlen(buf)-1]=0; //用null替換換行符 17 if((pid=fork())<0){ //fork向父進程返回新子進程的進程ID(非負),對子進程則返回0;因此說它被調用一次,返回兩次 18 err_sys("fork error"); 19 } 20 else if(pid==0){ 21 execlp(buf,buf,(char *)0); //execlp執行從標準輸入讀入的命令。fork和跟隨其後的exec二者的組合是某些操做系統所稱的產生(spawn)一個新進程 22 err_ret("couldn't execute:%s",buf); 23 exit(127); 24 } 25 if((pid=waitpid(pid,&status,0))<0) //waitpid函數返回子進程的終止狀態 26 err_sys("waitpid error"); 27 printf("%% "); 28 } 29 exit(0); 30 }
該程序的主要限制是不能向所執行的命令傳遞參數,例如不能指定要列表的目錄名,只能對工做目錄執行ls命令
4.線程和線程ID
在一個進程內的全部線程共享同一地址空間、文件描述符、棧以及進程相關的屬性。線程ID只在它所屬進程內起做用。一個進程中的線程ID在另外一個進程中並沒有意義。
1.7出錯處理
當unix函數出錯時,經常返回一個負值,並且整型變量errno一般被設置爲含有附加信息的一個值。例如,open函數如成功執行則返回一個非負文件描述符,如出錯則返回-1。
實例:1_6 示例strerror和perror兩個出錯函數的使用方法
1 #include"apue.h" 2 #include<errno.h> 3 int main(int argc,char *argv[]) 4 { 5 fprintf(stderr,"EACCES: %s\n",strerror(EACCES)); 6 errno=ENOENT; 7 perror(argv[0]); 8 exit(0); 9 }
1.8用戶標識
用戶ID 和組ID
示例:1_7 打印用戶ID和組ID
1 #include"apue.h" 2 int main() 3 { 4 printf("uid=%d,gid= %d,\n",getuid(),getgid()); 5 exit(0); 6 }
1.9信號
信號是通知進程已發生某種狀況的一種技術。
進程處理信號有三種選擇:a.忽略該信號
b.按系統默認方式處理。對於除以0的狀況,系統默認是終止該進程
c.提供一個函數,信號發生時則調用該函數,這被稱爲捕捉該信號。使用這種方式,咱們只要提供自編的函數就將能知道何時產生了信號,並按所但願 的方式處理它
實例:1_8 從標準輸入讀命令並執行(比1_5多了信號處理)
1 #include"apue.h" 2 #include<sys/wait.h> 3 static void sig_int(int); 4 5 int main(void) 6 { 7 char buf[MAXLINE]; 8 pid_t pid; 9 int status; 10 if(signal(SIGINT,sig_int)==SIG_ERR) 11 err_sys("signal error"); 12 13 printf("%% "); 14 while(fgets(buf,MAXLINE,stdin)!=NULL){ 15 if(buf[strlen(buf)-1]=='\n') 16 buf[strlen(buf)-1]=0; 17 if((pid=fork())<0){ 18 err_sys("fork error"); 19 } 20 else if(pid==0){ 21 execlp(buf,buf,(char *)0); 22 err_ret("couldn't execute:%s",buf); 23 exit(127); 24 } 25 if((pid=waitpid(pid,&status,0))<0) 26 err_sys("waitpid error"); 27 printf("%% "); 28 } 29 exit(0); 30 } 31 void sig_int(int signo) 32 { 33 printf("interrupt\n%%"); 34 }
1.10時間值
UNIX系統使用三個進程時間值:時鐘時間,用戶cpu時間,系統cpu時間
時鐘時間又稱爲牆上時鐘時間(wall clock time)。它是進程運行的時間總量,其值與系統中跟同時運行的進程數有關。
用戶CPU時間是執行用戶指令所用的時間。
系統cpu時間是爲該進程執行內核程序所經歷的時間
例如:每當一個進程執行一個系統服務時,如read或write,則在內核內執行該服務所花費的時間就計入該進程的系統cpu時間。用戶cpu時間和系統cpu時間之和常被稱爲cpu時間
取得任意進程的時鐘時間,用戶時間,和系統時間:$cd /usr/include;time -p grep _POSIX_SOURCE */*.h > /dev/null