APUE(1)----UNIX基礎知識

1、UNIX體系結構shell

  全部操做系統都爲他們所運行的程序提供服務,典型的服務包括:執行新程序、打開文件、讀文件、分配存儲區等。嚴格意義上來講,操做系統能夠定義爲一種軟件,它控制計算機硬件資源,提供程序運行環境。咱們也將這種軟件成爲內核,由於它相對較小,而且位於環境的核心。內核的接口被稱爲系統調用。公共函數庫構建在之上,普通的應用程序能夠調用系統調用,也能夠調用公共函數庫。shell是一個特殊的應用程序,位運行其餘應用程序提供了一個接口。函數

2、文件和目錄ui

1.文件系統spa

  UNXI文件系統是目錄和文件的一種層次結構,全部東西的起點稱爲根(root)的目錄,這個目錄的名稱是一個字符「/」。目錄是一個包含目錄項的文件,由文件名和文件屬性(文件類型、文件大小、文件全部者、文件權限等)組成。這裏須要注意的是邏輯視圖和實際存放在磁盤上的方式是不一樣的。UNIX文件系統的大多數實現並不在目錄項中存放屬性,這是由於當一個文件具備多個硬連接時,很難保持多個屬性副本之間的同步。操作系統

2.文件名和路徑名線程

  建立新目錄時會自動建立兩個文件名:「.」指向當前目錄,「..」指向父目錄,在最高層次,兩者指向相同。由斜線分隔的一個或多個文件名組成的序列構成路徑名,以斜線開頭的路徑名稱爲絕對路徑名,不然稱爲相對路徑名。code

#include<stdio.h>
#include<stdlib.h>
#include<dirent.h>

int main(int argc, char *argv[])
{
        DIR           *dp;
        struct dirent *dirp;

        if(2 != argc)
        {
                printf("usage: ls directory_name\n");
                return -1;
        }

        if(NULL == (dp = opendir(argv[1])))
        {
                printf("can't open %s\n", argv[1]);
                return -1;
        }

        while(NULL != (dirp = readdir(dp)))
        {
                printf("%s\n", dirp->d_name);
        }

        closedir(dp);
        exit(0);
}

1-1:列出一個目錄中的全部文件blog

3.工做目錄和起始目錄接口

  每一個進程都有一個工做目錄,全部相對路徑名都從工做目錄開始解釋,進程能夠用chdir函數更改其工做目錄。登錄時,工做目錄設置爲起始目錄,這個目錄是從口令文件中相應用戶的登陸項中獲取的。進程

3、輸入和輸出

1.文件描述符

  文件描述符一般是一個小的非負整數,內核用以標識一個特定進程正在訪問的文件。當內核打開一個先用文件或建立一個新文件時,它都返回一個文件描述符,在讀寫文件時,可使用這個文件描述符。按慣例,每當運行一個新程序時,全部的shell都爲其打開3個文件描述符,即標準輸入、標準輸出以及標準錯誤,若是不作特殊處理,這三個文件描述符都連接向終端。

2.不帶緩衝的I/O

  函數open、read、write、lseek以及close提供了不帶緩衝的I/O。這些函數都使用文件描述符。

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>

#define BUFFSIZE 4096

int main(void)
{
        int     n;
        char    buf[BUFFSIZE];
        while((n = read(STDIN_FILENO, buf, BUFFSIZE)) > 0)
        {
                if(write(STDOUT_FILENO, buf, n) != n)
                {
                        printf("write error.\n");
                        return -1;
                }
        }

        if(n > 0)
        {
                printf("read error.\n");
                return -1;
        }

        exit(0);
}

1-2:將標準輸入複製到標準輸出

註釋:1.頭文件<unistd.h>包含了不少UNIX系統服務的函數原型,STDID_FILENO/STDOUT_FILENO,以及read/write都定義在其中。2.read函數返回其讀取到的字節數,當發生錯誤時,read返回-1;3輸入ctrl+D做爲文件結束符,終止程序;

3.標準I/O

  標準I/O函數爲那些不帶緩衝I/O函數提供了一個帶緩衝的接口。使用標準I/O函數無需擔憂如何選取最佳的緩衝區大小,不用像上例那樣須要定義BUFFSIZE。而且還簡化了對輸入行的處理。

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
        int c;
        while((c = getc(stdin)) != EOF)
        {
                if(putc(c, stdout) == EOF)
                {
                        printf("output error.\n");
                        return -1;
                }
        }

        if(ferror(stdin))
        {
                printf("input error.\n");
        }

        exit(0);
}

1-3:用標準I/O將標準輸入複製到標準輸出

4、程序和進程

1.程序、進程和進程ID

  程序是一個存儲在磁盤上某個目錄中的可執行文件。內核使用exec函數將程序讀入內存,並執行程序。程序的執行實例稱爲進程,每個進程都有一個惟一的數字標識符,被稱爲進程ID(非負整數)。

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>

int main(void)
{
        printf("Hello world from process ID %ld\n", (long)getpid());
        exit(0);
}

1-4:打印進程ID

2.進程控制

  進程控制主要函數:fork、exec和waitpid。

#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>

#define MAXLINE 1024

int main(void)
{
        char  buff[MAXLINE];
        pid_t pid;
        int   status;

        printf("%% ");

        while(NULL != (fgets(buff, MAXLINE, stdin)))
        {
                if('\n' == buff[strlen(buff) - 1])
                {
                        buff[strlen(buff) - 1] = 0;
                }

                if(0 > (pid = fork()))
                {
                        printf("fork error\n");
                        return -1;
                }
                else if(0 == pid)
                {
                        execlp(buff, buff, (char *) 0);
                        printf("couldn't execute:%s\n", buff);
                        return -1;
                }

                if(0 > (pid == waitpid(pid, &status, 0)))
                {
                        printf("wait pid error\n");
                }

                printf("%% ");
        }
}

1-5:從標準輸入讀命令並執行

註釋:1.fgets是一個標準I/O函數,一次讀取一行。和read的區別在於本身管理緩衝區,不返回讀取的長度,在讀取的末尾加入"\n"。而execlp要求參數以null結束,因此須要進行替換;2.fork調用一次,返回兩次,在子進程中返回0,父進程返回子進程PID;3.execlp以執行從標準輸入讀取的命令,這就用新的程序文件替換了子進程原先執行的程序文件;4.父進程和子進程同步是經過waipid實現的。

3.線程和線程ID

  一個進程內的全部線程共享同一地址空間、文件描述符、棧以及與進程相關的屬性。由於他們能訪問贊成存儲區,因此各線程在訪問共享數據時須要採起同步措施以免不一致性。和進程ID相同, 線程也有ID,可是線程ID只在它所屬的進程內其做用。

5、出錯處理

  當UNIX系統函數出錯時,一般會返回一個負值,並且整形變量errno一般被設置爲具備特定信息的值。對於errno應當注意兩條規則:1.若是沒有出錯,其值不會被例程清楚,所以,僅當函數的返回值指明出錯時,才檢驗其值;2.任何函數都不會將errno值設爲0.

#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<errno.h>

int main(int argc, char * argv[])
{
        fprintf(stderr, "EACCES: %s\n", strerror(EACCES));

        errno = ENOENT;
        perror(argv[0]);
        exit(0);
}

1-6:例示strerror和perror

註釋:1.咱們將程序名(argv[0])做爲參數傳遞給main,這是一個標準的UNIX慣例。
6、用戶標識

  口令文件登陸項中的用戶ID是一個數值,它向系統標識不一樣的用戶。根用戶的ID爲0.

#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>

int main(void)
{
        printf("uid = %d, gid = %d\n", getuid(), getgid());
        exit(0);
}

1-7:打印用戶ID和組ID

7、信號

  信號(signal)用於通知進程發生了某種狀況。進程有如下三種處理方式:1.忽略信號;2.按系統默認方式處理;3.提供一個函數。該信號發生時調用該函數。當向一個進程發送信號時,咱們必須是那個進程的全部者或者是超級用戶。

#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>

#define MAXLINE 1024

static void sig_int(int);

int main(void)
{
    char  buff[MAXLINE];
    pid_t pid;
    int   status;

    if(signal(SIGINT, sig_int) == SIG_ERR)
    {
        printf("signal error\n");
        return -1;
    }


    printf("%% ");

    while(NULL != (fgets(buff, MAXLINE, stdin)))
    {
        if('\n' == buff[strlen(buff) - 1])
        {
            buff[strlen(buff) - 1] = 0;
        }
        
        if(0 > (pid = fork()))
        {
            printf("fork error\n");
            return -1;
        }
        else if(0 == pid)
        {
            execlp(buff, buff, (char *) 0);
            printf("couldn't execute:%s\n", buff);
            return -1;
        }

        if(0 > (pid == waitpid(pid, &status, 0)))
        {
            printf("wait pid error\n");
        }
        
        printf("%% ");
    }

}

void sig_int(int signo)
{
    printf("interrupt \n%% ");
}

1-8:從標準輸入讀命令並執行

8、時間值

  UNIX系統爲一個進程維護了3個進程時間值:1.時鐘時間:進程運行的時間總量,其值與系統中同時運行的進程數有關;2.用戶CPU時間:執行用戶指令所用的時間量;3.系統CPU時間:爲該進程執行內核程序所經歷的時間。2和3之和又稱爲CPU時間。

9、系統調用和庫函數

  從實現者的角度來看,系統調用和庫函數之間有根本的區別,但從用戶的角度來看,其區別不重要。用戶能夠替換庫函數,可是不能替換系統調用。而且系統調用一般提供一種最小接口,而庫函數一般提供比較複雜的功能。

相關文章
相關標籤/搜索