Unix基礎

概述

操做系統的任務是爲其上運行的程序服務:包括運行、打開文件、讀文件、分配內存以及時間獲取。shell

架構

定義:

In a strict sense, an operating system can be defined as the software that controls the hardware resources of the computer and provides an environment under which programs can run.安全

UNIX的體系結構

由內而外:
- 內核 其接口被稱爲系統調用(System call)。
- shell 特殊的應用程序,爲其餘應用程序提供接口。
- 庫函數 在系統調用接口基礎上的函數庫。
- 應用 就是應用。。架構

登陸

登陸方式

系統根據輸入的用戶名去/etc/passwd目錄下檢索用戶名並找到對應的密碼文件進行密碼匹配。
密碼文件長這樣:ide

sar:x:205:105:Stephen Rago:/home/sar:/bin/ksh

上述對應各字段值類型:
登陸名:加密的密碼:用戶ID:組ID:註釋域:起始:shell程序函數

shell

shell是一個命令解釋器,與用戶進行交互。ui

文件與目錄

  • 文件系統 樹狀層級結構
  • 文件名
  • 路徑名 建立目錄時候自動生成當前目錄.和父目錄..
  • 工做目錄 每一個進程都有一個工做目錄
  • 起始目錄

栗子:ls簡要實現加密

#include "apue.h"
#include <dirent.h>

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

    // 輸入參數不爲2, 暗示使用方法不對
    if (argc != 2) {
        err_quit("usage: ls directory_name");
    } 

    // 打開指定目錄失敗
    if ((dp = opendir(argv[1])) == NULL) {
        err_sys("can't not open %s", argv[1]);
    }

    // 遍歷dp文件夾 打印內部文件的文件名
    while ((dirp = readdir(dp)) != NULL) {
        printf("%s\n", dirp->d_name);
    }

    // 關閉文件夾句柄 && 退出程序
    closedir(dp);
    exit(0);
}

這裏可能遇到一個apue.3e的編譯問題,解決方案戳這裏spa

命令行編譯執行結果以下:操作系統

圖片描述

輸入與輸出

文件描述符 (file descriptor)

一般是一個小的非負整數,內核用它標識一個特定進程訪問的文件。命令行

標準輸入 標準輸出 標準錯誤

通常來講,全部派系的shell都會爲程序打開標準輸入、標準輸出、標準錯誤三個文件描述符。而且這三個文件描述符都能重定向到某個文件。

ls > file.list

非緩衝的I/O

非緩衝函數如:open, read, write, lseek, close。這些函數都與文件描述符協做。

舉個栗子:從標準輸入讀寫入標準輸出

#include <stdio.h>
#include <apue.h>

#define BUFFSIZE 4096

int main(void) {
    int n;
    char buf[BUFFSIZE];

    // 頭文件<unistd.h>中包含了`STDIN_FILENO`與`STDOUT_FILENO`兩個常量
    while ((n = (int)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);
}

命令運行結果截圖:
圖片描述

標準I/O

標準I/O函數提供一種對不用緩衝I/O函數的帶緩衝接口。 無腦說就是不用設置緩衝區大小。好比fgets函數讀完一行的長度而read函數須要制定讀入的字節數。
咱們最熟悉得標準I/O是printf。。。

舉個栗子: 用標準I/O將標準輸入複製到標準輸出

#include <stdio.h>
#include <apue.h>

int main(void) {
    int c;


    // 頭文件<unistd.h>中包含了`STDIN_FILENO`與`STDOUT_FILENO`兩個常量
    while ((c = getc(stdin)) != EOF) {
        if (putc(c, stdout) == EOF) {
            err_sys("output error");
        }

        if (ferror(stdin)) {
            err_sys("input error");
        }
    }

    exit(0);
}

運行結果略。。

程序和進程

程序

程序是放在磁盤上的。

進程和進程ID

程序執行的實例叫作進程。每一個進程都有本身的惟一標識符PID。

栗子:打印進程ID

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

運行結果:
圖片描述

進程控制

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

舉個栗子:

#include <stdio.h>
#include <sys/wait.h>
#include <apue.h>

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

    printf("%% ");

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

        if ((pid = fork()) < 0) {
            err_sys("fork error");
        } else if (pid == 0) {
            execlp(buf, buf, (char *)0);
            err_ret("couldn't execute: %s", buf);
            exit(127);
        }

        if ((pid = waitpid(pid, &status, 0)) < 0) {
            err_sys("watipid error");
        }

        printf("%% ");
    }

    exit(0);
}

運行結果:
圖片描述

線程和線程ID

一般一個進程只有一個控制線程,同一時刻只能執行一組機器指令。
在同一個進程內得線程共享同一個地址空間、文件描述符、棧以及進程相關屬性。
由於共享資源的關係,因此這裏有個線程安全的概念。
與進程相同,線程也用ID標識。線程ID只在進程中起做用。出了進程就沒有意義了。

出錯處理

UNIX函數出錯的時候一般返回一個負值,用errno表示錯誤得種類。某些函數不返回負值而是使用另一種約定,好比返回一個只想對象的指針的大多數函數,出錯時候返回null指針。

使用errno的兩條規則:

  1. 若是沒有出錯,則errno值不會被例行清除。只有當函數返回值錯誤的時候纔去檢查其值。
  2. 沒有函數會將errno值設置爲0,在<errno.h>中定義的全部常量都不爲0。

標準C鍾定義了下面兩個方程來實現打印錯誤消息。

char *strerror(int errnum);
void perror(const char *msg);

第一個函數代表根據errno找到錯誤信息字符串指針,第二個函數代表根據指針,打印出錯誤信息。

舉個栗子:

#include <string.h>
#include <errno.h>

int main(int argc, char *argv[]) {
    fprintf(stderr, "EACCES: %s\n", strerror(EACCES));
    errno = ENOENT;
    perror(argv[0]);
    exit(0);
}

經過將./a.out傳入perror函數,這樣能夠在管道中知道是哪一個程序出錯了。

錯誤恢復

<errno.h>的錯誤定義劃分爲兩種:致命與非致命。致命錯誤沒有恢復操做。而非致命錯誤相反,遇到此類錯誤時候能夠更爲妥善的處理。
資源相關的非致命錯誤包含EAGAIN, ENFILE, ENOBUFS, ENOLCK, ENOSPC, EWOULDBLOCK,有些時候還有ENOMEMEBUSY問題出在共享資源被佔用的時候也能夠視爲非致命錯誤。
資源相關非致命錯誤的恢復策略通常是延遲重試。
適時的採用恢復策略能夠增長應用的健壯性從而避免異常退出。

用戶標識

用戶ID

用戶ID用來讓系統區分不一樣的用戶。
用戶ID爲0得用戶爲超級用戶。操做系統的不少權限只向超級用戶提供。

組ID

組ID是系統管理員在指定用戶名提供的。這種機制容許組內成員共享資源。
組文件將組名映射爲數字組ID,它一般是/etc/group.

舉個栗子:

int main(int argc, char *argv[]) {
    printf("uid = %d, gid = %d\n", getuid(), getgid());
    exit(0);
}

附加組ID

大多數UNIX版本還容許一個用戶屬於另一個用戶組。

信號

信號是通知進程已經發生某種狀況的一種技術。舉個栗子,一個進程在執行除法運算操做,除數爲0,則將名爲SIGFPE的信號發給該進程。

栗子:
爲了讓程序能捕捉到信號,須要讓其調用signal函數來指定對應信號應當執行的動做(函數)。下慄是在捕獲信號時候進行打印輸出:

// 聲明信號handler
static void sig_int(int);

// 簡單handler實體 - 打印
void sig_int(int signo) {
    printf("interrupt\n%% ");
}

時間值

UNIX系統有兩類時間值:

  • 日曆時間
  • 進程時間

日曆時間:廣義時間,用time_t修飾。
進程時間:CPU時間,用來度量進程使用的CPU資源,用clock_t修飾。進程時間又分爲:

  • 時鐘時間 進程運行的時間總量。與總進程數有關。
  • 用戶CPU時間 CPU執行用戶指令所用的時間
  • 系統CPU時間 CPU執行內和程序所用的時間

用戶CPU時間與系統CPU時間統稱CPU時間。能夠經過time函數來獲取時間值組成。

系統調用和庫函數(理論)

從實現者角度看,系統調用和庫函數之間有重大區別,但從用戶角度,區別並非很重要。

應用程序能夠調用系統函數或者庫函數,而不少庫函數會調用系統調用。
系統調用一般提供最小接口,而庫函數提供比較複雜的功能。

總結

UNIX概述

相關文章
相關標籤/搜索