操做系統的任務是爲其上運行的程序服務:包括運行、打開文件、讀文件、分配內存以及時間獲取。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.安全
由內而外:
- 內核 其接口被稱爲系統調用(System call)。
- shell 特殊的應用程序,爲其餘應用程序提供接口。
- 庫函數 在系統調用接口基礎上的函數庫。
- 應用 就是應用。。架構
系統根據輸入的用戶名去/etc/passwd
目錄下檢索用戶名並找到對應的密碼文件進行密碼匹配。
密碼文件長這樣:ide
sar:x:205:105:Stephen Rago:/home/sar:/bin/ksh
上述對應各字段值類型:
登陸名:加密的密碼:用戶ID:組ID:註釋域:起始: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
命令行編譯執行結果以下:操作系統
一般是一個小的非負整數,內核用它標識一個特定進程訪問的文件。命令行
通常來講,全部派系的shell都會爲程序打開標準輸入、標準輸出、標準錯誤三個文件描述符。而且這三個文件描述符都能重定向到某個文件。
ls > file.list
非緩衝函數如: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函數的帶緩衝接口。 無腦說就是不用設置緩衝區大小。好比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); }
運行結果略。。
程序是放在磁盤上的。
程序執行的實例叫作進程。每一個進程都有本身的惟一標識符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只在進程中起做用。出了進程就沒有意義了。
UNIX函數出錯的時候一般返回一個負值,用errno
表示錯誤得種類。某些函數不返回負值而是使用另一種約定,好比返回一個只想對象的指針的大多數函數,出錯時候返回null指針。
使用errno的兩條規則:
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
,有些時候還有ENOMEM
。EBUSY
問題出在共享資源被佔用的時候也能夠視爲非致命錯誤。
資源相關非致命錯誤的恢復策略通常是延遲重試。
適時的採用恢復策略能夠增長應用的健壯性從而避免異常退出。
用戶ID用來讓系統區分不一樣的用戶。
用戶ID爲0得用戶爲超級用戶。操做系統的不少權限只向超級用戶提供。
組ID是系統管理員在指定用戶名提供的。這種機制容許組內成員共享資源。
組文件將組名映射爲數字組ID,它一般是/etc/group.
舉個栗子:
int main(int argc, char *argv[]) { printf("uid = %d, gid = %d\n", getuid(), getgid()); exit(0); }
大多數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時間。能夠經過time
函數來獲取時間值組成。
從實現者角度看,系統調用和庫函數之間有重大區別,但從用戶角度,區別並非很重要。
應用程序能夠調用系統函數或者庫函數,而不少庫函數會調用系統調用。
系統調用一般提供最小接口,而庫函數提供比較複雜的功能。
UNIX概述