UNIX基礎(chǔ)知識
- UNIX體系結(jié)構(gòu)
- 登陸
- 文件和目錄
- 文件系統(tǒng)【1消玄、目錄是一個包含目錄項的文件;2再层、根目錄:所有東西的起點缩赛;】
- 文件屬性:
stat
和fstat
返回包含所有文件屬性的一個信息結(jié)構(gòu);
- 文件屬性:
- 文件名
-
斜線
/
和空字符不能出現(xiàn)在文件名中聪廉,斜線用來分隔構(gòu)成路徑名的各文件名蹄葱,空字符用來終止一個路徑名 - 兩個特殊文件
.
(點)和..
(點點),前者指向當前目錄锄列,后者指向父目錄
-
斜線
- 路徑名【絕對路徑以
/
開頭图云,相對路徑以.
開頭】
此程序讀取一個文件夾并輸出文件夾下的文件及目錄信息,其核心代碼可以簡略如下:#include<dirent.h> #include<apue.h> #include <error.h> int dir(char *path) { DIR *dp; struct dirent *dirp; if (path == NULL) err_quit("usage: directory_name not null"); if ((dp = opendir(path)) == NULL) err_sys("can't open %s", path); while ((dirp = readdir(dp)) != NULL) printf("%s\n", dirp->d_name); closedir(dp); return 0; } int main(int argc, char *argv[]) { char *path = ".."; return dir(path); }
可以看一下DIR *dp;//目錄 struct dirent *dirp;//文件或者目錄文件結(jié)構(gòu) dp = opendir(path);//打開目錄 while ((dirp = readdir(dp)) != NULL)//不斷讀取目錄 printf("%s\n", dirp->d_name);//輸出文件名字 closedir(dp);//結(jié)束讀取之后關(guān)閉目錄
strut dirent
#if __DARWIN_64_BIT_INO_T struct dirent __DARWIN_STRUCT_DIRENTRY; #endif /* __DARWIN_64_BIT_INO_T */ #define __DARWIN_STRUCT_DIRENTRY { \ __uint64_t d_ino; /* file number of entry */ \ __uint64_t d_seekoff; /* seek offset (optional, used by servers) */ \ __uint16_t d_reclen; /* length of this record */ \ __uint16_t d_namlen; /* length of string in d_name */ \ __uint8_t d_type; /* file type, see below */ \ char d_name[__DARWIN_MAXPATHLEN]; /* entry name (up to MAXPATHLEN bytes) */ \ }
- 工作目錄
- 每個進程都有一個工作目錄:當前工作目錄邻邮,相對路徑從工作目錄開始解釋竣况。
- 可以使用
chdir函數(shù)
改變當前工作目錄。
- 起始目錄
- 用戶登陸時的工作目錄設(shè)置為
起始目錄
筒严,起始目錄在登陸口令文件
中定義丹泉。
- 用戶登陸時的工作目錄設(shè)置為
- 文件系統(tǒng)【1消玄、目錄是一個包含目錄項的文件;2再层、根目錄:所有東西的起點缩赛;】
- 輸入和輸出
-
文件描述符
- 一個小的非負整數(shù)情萤,內(nèi)核用于標識一個特定進程正在訪問的文件
- 當內(nèi)核打開一個文件時或者創(chuàng)建一個文件時,它都返回一個文件描述符
-
標準輸入摹恨、標準輸出和標準錯誤
- 當運行一個新程序時筋岛,所有的
shell
都為其打開3個文件描述符,標準輸入
晒哄、標準輸出
和標準錯誤
- 3個文件描述符都可重定向到文件(詳細參考shell編程)
- 當運行一個新程序時筋岛,所有的
-
不帶緩沖的I/O
- 函數(shù)
open
睁宰、read
、write
寝凌、lseek
以及close
提供了不帶緩沖的I/O柒傻。這些函數(shù)都適用文件描述符。
#include <apue.h> #include <error.h> #define BUFFSIZE 4096 int main() { int n; char buf[BUFFSIZE]; while ((n = read(STDIN_FILENO, buf, BUFFSIZE)) > 0) if (write(STDOUT_FILENO, buf, n) != n) err_sys("write error"); if (n < 0) err_sys("read error"); exit(0); }
#define STDIN_FILENO 0 /* standard input file descriptor */ #define STDOUT_FILENO 1 /* standard output file descriptor */ #define STDERR_FILENO 2 /* standard error file descriptor */
read
函數(shù)返回讀取的字節(jié)數(shù)较木,此值用作要寫的字節(jié)數(shù)红符。當?shù)竭_輸入文件的尾端時,read
返回0
伐债,程序停止執(zhí)行预侯。如果發(fā)生了一個讀錯誤,read
返回-1
峰锁。 - 函數(shù)
-
標準I/O
- 標準I/O為那些不帶緩沖的I/O函數(shù)提供了一個帶緩沖的接口萎馅。使用標準I/O函數(shù)無需擔心如何選取最佳的緩沖區(qū)大小。
- 常見的標準I/O函數(shù):
printf
祖今、fgets
(讀取一行)校坑、getc
、putc
- 標準I/O常量:
stdin
(標準輸入)千诬、stdout
(標準輸出)耍目、EOF
#include <apue.h> #include <error.h> int main() { int c; while ((c = getc(stdin))!=EOF) if (putc(c,stdout)==EOF) err_sys("output error"); if (ferror(stdin)) err_sys("input error"); exit(0); }
-
- 程序和進程
- 程序【內(nèi)核使用
exec函數(shù)
(7個之一)將存儲在磁盤上的可執(zhí)行文件(程序)讀入內(nèi)存,并執(zhí)行】 - 進程與進程ID
進程:程序的執(zhí)行實例
進程ID:每個進程唯一的數(shù)字標識符徐绑,非負整數(shù)
-
打印進程ID邪驮,
getpid()
函數(shù)得到當前進程ID,其返回一個pid_t
類型數(shù)據(jù)傲茄,返回值保證在long
數(shù)據(jù)范圍內(nèi)(在LLDB編譯器中毅访,看到pid_t實際上是一個int32)。#include <apue.h> int main(void){ printf("hello world from process id %ld\n",(long)getpid()); exit(0); }
- 進程控制
- 進程控制的主要函數(shù):
fork
盘榨、exec
和waitpid
喻粹,exec
函數(shù)有7種變體。
#include<apue.h> #include<error.h> #include<sys/wait.h> int main(){ char buf[MAXLINE]; pid_t pid; int status; printf("%% "); //fgets讀取一行草巡,當鍵入文件終止符(一般crtl+D)守呜,返回null指針 //fgets返回的每一行都以換行符終止,后隨一個null字節(jié),因此可用strlen計算字符串長度 while(fgets(buf,MAXLINE,stdin)!=NULL){ if(buf[strlen(buf)-1]=='\n') buf[strlen(buf)-1]=0;//execlp函數(shù)要求的參數(shù)是以null結(jié)尾而不是換行符 //fork創(chuàng)建一個新進程查乒,新進程是調(diào)用進程的一個副本 //調(diào)用進程為父進程弥喉,新進程為子進程 //fork對父進程返回子進程ID,對子進程則返回0 //fork調(diào)用一次玛迄,返回兩次(分別在父進程和子進程中) if((pid=fork())<0) err_sys("fork error"); else if(pid==0){ //在子進程中由境,調(diào)用execlp以執(zhí)行從標準輸入讀入的命令 execlp(buf,buf,(char*)0); err_ret("couldn't execute: %s",buf); exit(127); } //父進程等待子進程終止 //waitpid指定要等待的子進程,并返回子進程的終止狀態(tài) if((pid=waitpid(pid,&status,0))<0) err_sys("waitpid error"); printf("%% "); } exit(0); }
- 進程控制的主要函數(shù):
- 線程與線程ID
- 一個進程內(nèi)部可以有多個控制線程
- 一個進程內(nèi)的所有線程共享同一地址空間蓖议、文件描述符虏杰、棧以及與進程相關(guān)的屬性。
- 因為他們能訪問同一存儲區(qū)拒担,所以各線程訪問共享數(shù)據(jù)時需要采取同步措施以避免不一致性嘹屯。
- 線程ID:線程ID只在其所屬的進程內(nèi)起作用攻询。
- 程序【內(nèi)核使用
- 出錯處理
- 文件
<errno.h>
中定義了errno
以及可以賦予它的各種常量从撼,這些錯誤常量一般以E
開頭 -
errno
使用的兩條規(guī)則:- 1、如果沒有出錯钧栖,其值不會被例程清除低零,所以僅在出錯時檢驗其值;
- 2拯杠、任何函數(shù)都不會將
errno
設(shè)置為0.
- 打印出錯信息
-
strerror
函數(shù):將errnum(通常就是errno)映射為一個出錯消息字符串掏婶,并返回 -
perror
函數(shù):基于errno值,在標準錯誤上產(chǎn)生一條出錯信息潭陪,然后返回
#include <apue.h> #include<errno.h> int main(int argc,char* argv[0]){ fprintf(stderr,"EACCES: %s\n",strerror(EACCES)); errno=ENOENT; perror(argv[0]); exit(0); }
EACCES: Permission denied /Users/test/aupe3/build/1_basic/basic: No such file or directory
-
- 出錯恢復(fù)
- 致命錯誤:無法執(zhí)行恢復(fù)動作雄妥,最多能打印出錯信息,寫入日志依溯;
- 非致命性錯誤:對于資源相關(guān)的非致命錯誤典型的恢復(fù)操作是延時一段時間老厌,然后重試。對于延時策略黎炉,一些應(yīng)用使用指數(shù)補償算法枝秤。
- 文件
- 用戶標識
- 用戶ID:每個用戶有一個唯一的用戶ID,向系統(tǒng)標識不同的用戶慷嗜,用戶不能更改其用戶ID淀弹,root用戶的用戶ID為0.
- 組ID:多個登陸項具有相同的組ID,組ID被用于將若干用戶集合到項目或者部門中去庆械,允許同組的成員之間共享資源薇溃。組文件將組名映射為數(shù)值的組ID,組文件通常是/etc/group缭乘。
- 使用用戶ID和組ID沐序,只需4字節(jié)保存兩個信息(每個以雙字節(jié)整型值存放),而使用ASCII字符需要消耗更多的內(nèi)存,且檢驗權(quán)限時薄啥,字符串比較相對比較耗時辕羽。
- 但是對于用戶而言,使用名字相比使用ID數(shù)值更加方便垄惧,所以口令文件中包含了登陸名和用戶ID之間的映射關(guān)系刁愿,而組文件則包含了組名與組ID之間的映射關(guān)系。
#include<apue.h> int main(){ printf("uid = %d, gid = %d\n",getuid(),getgid()); exit(0); }
- 信號
- 用于通知進程發(fā)生了某種情況到逊,進程有3種處理信號的方式:
- 忽略信號
- 按系統(tǒng)默認方式處理
- 提供一個函數(shù)捕捉該信號進行處理
- 為之前的shell進程控制例程添加信號處理函數(shù)
#include<apue.h> #include<error.h> #include<sys/wait.h> static void sig_int(int); int main() { char buf[MAXLINE]; pid_t pid; int status; //signal當產(chǎn)生了指定的信號(SIGINT)時铣口,執(zhí)行sig_int函數(shù) if (signal(SIGINT, sig_int) == SIG_ERR) err_sys("signal error"); printf("%% "); //fgets讀取一行,當鍵入文件終止符(一般crtl+D)觉壶,返回null指針 //fgets返回的每一行都以換行符終止脑题,后隨一個null字節(jié),因此可用strlen計算字符串長度 while (fgets(buf, MAXLINE, stdin) != NULL) { if (buf[strlen(buf) - 1] == '\n') buf[strlen(buf) - 1] = 0;//execlp函數(shù)要求的參數(shù)是以null結(jié)尾而不是換行符 //fork創(chuàng)建一個新進程铜靶,新進程是調(diào)用進程的一個副本 //調(diào)用進程為父進程叔遂,新進程為子進程 //fork對父進程返回子進程ID,對子進程則返回0 //fork調(diào)用一次争剿,返回兩次(分別在父進程和子進程中) if ((pid = fork()) < 0) err_sys("fork error"); else if (pid == 0) { //在子進程中已艰,調(diào)用execlp以執(zhí)行從標準輸入讀入的命令 execlp(buf, buf, (char *) 0); err_ret("couldn't execute: %s", buf); exit(127); } //父進程等待子進程終止 //waitpid指定要等待的子進程,并返回子進程的終止狀態(tài) if ((pid = waitpid(pid, &status, 0)) < 0) err_sys("waitpid error"); printf("%% "); } exit(0); } void sig_int(int signo){ printf("interrupt\n%% "); }
- 用于通知進程發(fā)生了某種情況到逊,進程有3種處理信號的方式:
- 時間值
- 日歷時間:1970.1.1 00:00:00(UTC) 這個特定時間以來所經(jīng)過的秒數(shù)累計值蚕苇,
time_t
數(shù)據(jù)結(jié)構(gòu)保存這種時間類型哩掺; - 進程時間:CPU時間,用以度量進程使用的中央處理器資源涩笤,
clock_t
數(shù)據(jù)類型保存這種時間類型嚼吞。 - 度量一個進程的執(zhí)行時間時,UNIX系統(tǒng)為一個進程維護了3個進程時間值:
- 時鐘時間蹬碧;
- 墻上時鐘時間舱禽,進程運行的時間總量,其值與系統(tǒng)中同時運行的進程數(shù)有關(guān)锰茉;
- 用戶CPU時間呢蔫;
- 執(zhí)行用戶指令所用的時間量;
- 系統(tǒng)CPU時間飒筑。
- 為該進程執(zhí)行內(nèi)核程序所經(jīng)歷的時間片吊。
- 時鐘時間蹬碧;
- 日歷時間:1970.1.1 00:00:00(UTC) 這個特定時間以來所經(jīng)過的秒數(shù)累計值蚕苇,
- 系統(tǒng)調(diào)用與庫調(diào)用
- 系統(tǒng)調(diào)用:各種版本的UNIX實現(xiàn)都提供良好定義、數(shù)量有限协屡、直接進入內(nèi)核的入口點俏脊,這些入口點稱為系統(tǒng)調(diào)用;
- 庫調(diào)用
- 區(qū)別:
- 一般可以替換庫函數(shù)肤晓,但是系統(tǒng)調(diào)用通常是不能被替換的爷贫;
- 很多庫函數(shù)會調(diào)用系統(tǒng)調(diào)用认然;
- 系統(tǒng)調(diào)用通常提供一個最小接口,而庫函數(shù)通常提供比較復(fù)雜的功能漫萄;
- 習(xí)題
- 1.4 若日歷時間存放在帶符號的32位整型數(shù)中卷员,那么到哪一年溢出?怎么解決腾务?
- 帶符號32位整型數(shù)最大值為
,可知將在
年溢出毕骡,可以換用int64位
- 帶符號32位整型數(shù)最大值為
- 1.5 若進程時間存放在帶符號的32位整型數(shù)中,而且每秒100時鐘滴答岩瘦,那么經(jīng)過多少天后該時間溢出未巫?
- 1.4 若日歷時間存放在帶符號的32位整型數(shù)中卷员,那么到哪一年溢出?怎么解決腾务?
- Reference
- 《UNIX環(huán)境高級編程》第3版
- 其他