UNIX基礎(chǔ)知識

UNIX基礎(chǔ)知識

  • UNIX體系結(jié)構(gòu)
  • 登陸
  • 文件和目錄
    • 文件系統(tǒng)【1消玄、目錄是一個包含目錄項的文件;2再层、根目錄:所有東西的起點缩赛;】
      • 文件屬性:statfstat返回包含所有文件屬性的一個信息結(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ù)情萤,內(nèi)核用于標識一個特定進程正在訪問的文件
      • 當內(nèi)核打開一個文件時或者創(chuàng)建一個文件時,它都返回一個文件描述符
    • 標準輸入摹恨、標準輸出和標準錯誤

      • 當運行一個新程序時筋岛,所有的shell都為其打開3個文件描述符,標準輸入晒哄、標準輸出標準錯誤
      • 3個文件描述符都可重定向到文件(詳細參考shell編程)
    • 不帶緩沖的I/O

      • 函數(shù)open睁宰、readwrite寝凌、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峰锁。

    • 標準I/O

      • 標準I/O為那些不帶緩沖的I/O函數(shù)提供了一個帶緩沖的接口萎馅。使用標準I/O函數(shù)無需擔心如何選取最佳的緩沖區(qū)大小
      • 常見的標準I/O函數(shù):printf祖今、fgets(讀取一行)校坑、getcputc
      • 標準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盘榨、execwaitpid喻粹,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);
      }
      
    • 線程與線程ID
      • 一個進程內(nèi)部可以有多個控制線程
      • 一個進程內(nèi)的所有線程共享同一地址空間蓖议、文件描述符虏杰、棧以及與進程相關(guān)的屬性。
      • 因為他們能訪問同一存儲區(qū)拒担,所以各線程訪問共享數(shù)據(jù)時需要采取同步措施以避免不一致性嘹屯。
      • 線程ID:線程ID只在其所屬的進程內(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種處理信號的方式:
        1. 忽略信號
        1. 按系統(tǒng)默認方式處理
        1. 提供一個函數(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%% ");
        }
    
  • 時間值
    • 日歷時間: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)歷的時間片吊。
  • 系統(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ù)最大值為2^{31}-1=2147483647\ s \approx 68 \ years,可知將在2038年溢出毕骡,可以換用int64位
    • 1.5 若進程時間存放在帶符號的32位整型數(shù)中,而且每秒100時鐘滴答岩瘦,那么經(jīng)過多少天后該時間溢出未巫?
      • \frac{2^{31}-1}{100} \approx 21474836\ s \approx 248\ days
  • Reference
    • 《UNIX環(huán)境高級編程》第3版
  • 其他
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市启昧,隨后出現(xiàn)的幾起案子叙凡,更是在濱河造成了極大的恐慌,老刑警劉巖密末,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件握爷,死亡現(xiàn)場離奇詭異,居然都是意外死亡苏遥,警方通過查閱死者的電腦和手機饼拍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進店門赡模,熙熙樓的掌柜王于貴愁眉苦臉地迎上來田炭,“玉大人,你說我怎么就攤上這事漓柑〗塘颍” “怎么了?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵辆布,是天一觀的道長瞬矩。 經(jīng)常有香客問我,道長锋玲,這世上最難降的妖魔是什么景用? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮惭蹂,結(jié)果婚禮上伞插,老公的妹妹穿的比我還像新娘。我一直安慰自己盾碗,他們只是感情好媚污,可當我...
    茶點故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著廷雅,像睡著了一般耗美。 火紅的嫁衣襯著肌膚如雪京髓。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天商架,我揣著相機與錄音堰怨,去河邊找鬼。 笑死蛇摸,一個胖子當著我的面吹牛诚些,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播皇型,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼诬烹,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了弃鸦?” 一聲冷哼從身側(cè)響起绞吁,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎唬格,沒想到半個月后家破,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡购岗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年汰聋,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片喊积。...
    茶點故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡烹困,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出乾吻,到底是詐尸還是另有隱情髓梅,我是刑警寧澤,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布绎签,位于F島的核電站枯饿,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏诡必。R本人自食惡果不足惜奢方,卻給世界環(huán)境...
    茶點故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望爸舒。 院中可真熱鬧蟋字,春花似錦、人聲如沸碳抄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽剖效。三九已至嫉入,卻和暖如春焰盗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背咒林。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工熬拒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人垫竞。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓澎粟,卻偏偏與公主長得像,于是被迫代替她去往敵國和親欢瞪。 傳聞我的和親對象是個殘疾皇子活烙,可洞房花燭夜當晚...
    茶點故事閱讀 45,851評論 2 361