owned this note
owned this note
Published
Linked with GitHub
###### tags: 系統程式
# 系統程式上課筆記
:::info
筆記內容為看上課重播,外部圖片及解釋框架在運用地方下方有放來源連結
指導老師:陳鍾誠
:::
### gcc
- 用gcc將程式碼編譯成執行檔
`gcc sum.c`
預設輸出檔案名稱為 a.exe 或 a.out
- 也可用 -o 參數來指定輸出檔名
gcc a.c -o hsien
此時輸出檔案則是 hsien.exe 或 hsien.out
>問題:使用gcc失敗
>要注意環境變數
- 將多個檔案編譯並連結產生執行檔
`gcc 1.c 2.c -o run`
- 將檔案編譯產生組合語言,再進行連結產生執行檔
gcc -S 1.c -o 1.s
gcc -S 2.c -o 2.s
gcc 1.s 2.s -o run
- 將檔案組譯產生目的檔,再進行連結產生執行檔
gcc -c 1.c -o 1.o
gcc -c 2.c -o 2.o
gcc 2.o 1.o -o run
- 將檔案編譯產生組合語言
gcc -S 1.c -o 1.s
- 將組合語言組譯產生目的檔
gcc -c 1.s -o 1.o
- 將目的檔與函式庫連結後產生執行檔
gcc 1.o -o 1
- 可將 gcc 改成 g++
### gcc編譯流程
高階語言(系統程式)
↓
編譯器(系統軟體)
↓
組合語言(系統程式)
↓
組譯器(系統軟體)
↓
目的檔
↓
連結器(系統軟體)
↓
執行檔
↓
載入器(系統軟體)
↓
記憶體
>[框架出處](https://www.slideshare.net/ccckmit/1-73472884)
### make
- makeExe
用 makefile 將檔案進行編譯
```
CC := gcc
CFLAGS = -std=c99 -O0 #CFLAGS 語法設定
TARGET = run
all: $(TARGET)
$(TARGET): sum.c main.c
$(CC) $(CFLAGS) $^ -o $@
clean:
rm -f *.o *.exe $(TARGET) #rm:清除指定 f:尋找
```
>由於 rm 只能在bash終端機執行,所以要更改終端機的種類
- makeLib
用 makefile 先將檔案組譯,並創建資料庫後,再進行連結
```
CC := gcc
AR := ar
CFLAGS = -std=c99 -O0
TARGET = run
LIB = libstat.a
all: $(TARGET)
$(TARGET): $(LIB) main.o
$(CC) $(CFLAGS) $^ -L ./ -lstat -o $@
$(LIB): sum.o
$(AR) -r $@ $^
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f *.o *.a *.exe $(TARGET)
```
$@ : 該規則的目標文件 (Target file)
$* : 代表 targets 所指定的檔案,但不包含副檔名
$< : 依賴文件列表中的第一個依賴文件 (Dependencies file)
$^ : 依賴文件列表中的所有依賴文件
$? : 依賴文件列表中新於目標文件的文件列表
?= 語法 : 若變數未定義,則替它指定新的值。
:= 語法 : make 會將整個 Makefile 展開後,再決定變數的值。
### C語言
Compiler(編譯器) -> Assembler(組譯器) -> Machine Language(機器語言) -> CPU
### Compiler
編譯器六大階段:詞彙掃描 ->語法剖析 -> 語意分析 -> 中間碼產生 -> 最佳化 -> 組合語言產生
### genExp
```
#include "rlib.h"
void E();
void T();
void F();
// === EBNF Grammar =====
// E=T ([+-] T)*
// T=F ([*/] F)?
// F=[0-9] | (E)
int main(int argc, char * argv[]) {
timeSeed();
// E();
int i;
for (i=0; i<10; i++) {
E();
printf("\n");
}
}
// E=T ([+-] T)*
void E() {
T();
while (randInt(10) < 3) {
printf("%c", randChar("+-"));
T();
}
}
// T=F ([*/] F)?
void T() {
F();
if (randInt(10) < 7) {
printf("%c", randChar("*/"));
F();
}
}
// F=[0-9] | (E)
void F() {
if (randInt(10) < 8) {
printf("%c", randChar("0123456789"));
} else {
printf("(");
E();
printf(")");
}
}
```
### genEnglish
```
#include "rlib.h"
// === EBNF Grammar =====
// S = NP VP
// NP = DET N
// VP = V NP
// N = dog | cat
// V = chase | eat
// DET = a | the
char* n[] = {"dog", "cat"};
char* v[] = {"chase", "eat"};
char* det[] = {"a", "the"};
void N() {
printf("%s", randSelect(n, 2));
}
void V() {
printf("%s", randSelect(v, 2));
}
void DET() {
printf("%s", randSelect(det, 2));
}
void NP() {
DET();
printf(" ");
N();
}
void VP() {
V();
printf(" ");
NP();
}
void S() {
NP();
printf(" ");
VP();
printf("\n");
}
int main() {
timeSeed();
S();
}
```
### esp0
```
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <ctype.h>
int tokenIdx = 0;
char *tokens;
int E();
int F();
void error(char *msg) {
printf("%s", msg);
assert(0);
}
// 取得目前字元
char ch() {
char c = tokens[tokenIdx];
return c;
}
// 取得目前字元,同時進到下一格
char next() {
char c = ch();
tokenIdx++;
return c;
}
// ex: isNext("+-") 用來判斷下一個字元是不是 + 或 -
int isNext(char *set) {
char c = ch();
return (c!='\0' && strchr(set, c)!=NULL);
}
// 產生下一個臨時變數的代號, ex: 3 代表 t3。
int nextTemp() {
static int tempIdx = 0;
return tempIdx++;
}
// F = Number | '(' E ')'
int F() {
int f;
char c = ch();
if (isdigit(c)) {
next(); // skip c
f = nextTemp();
printf("t%d=%c\n", f, c);
} else if (c=='(') { // '(' E ')'
next();
f = E();
assert(ch()==')');
next();
} else {
error("F = (E) | Number fail!");
}
return f;
}
// E = F ([+-] F)*
int E() {
int i1 = F();
while (isNext("+-")) {
char op=next();
int i2 = F();
int i = nextTemp();
printf("t%d=t%d%ct%d\n", i, i1, op, i2);
i1 = i;
}
return i1;
}
void parse(char *str) {
tokens = str;
E();
}
int main(int argc, char * argv[]) {
printf("argv[0]=%s argv[1]=%s\n", argv[0], argv[1]);
printf("=== EBNF Grammar =====\n");
printf("E=F ([+-] F)*\n");
printf("F=Number | '(' E ')'\n");
printf("==== parse:%s ========\n", argv[1]);
parse(argv[1]);
}
```
### exp0hack
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <ctype.h>
int tokenIdx = 0;
char *tokens;
int E();
int F();
void error(char *msg) {
printf("%s", msg);
assert(0);
}
char ch() {
char c = tokens[tokenIdx];
return c;
}
char next() {
char c = ch();
tokenIdx++;
return c;
}
int isNext(char *set) {
char c = ch();
return (c!='\0' && strchr(set, c)!=NULL);
}
int nextTemp() {
static int tempIdx = 0;
return tempIdx++;
}
// ex : t1=3
void genOp1(int i, char c) {
printf("# t%d=%c\n", i, c);
// t1=3 轉成 @3; D=A; @t1; M=D
printf("@%c\n", c);
printf("D=A\n");
printf("@t%d\n", i);
printf("M=D\n");
}
// ex : t2 = t0+t1
void genOp2(int i, int i1, char op, int i2) {
printf("# t%d=t%d%ct%d\n", i, i1, op, i2);
// t0=t1+t2 轉成 @t1; D=M; @t2; D=D+M; @t0; M=D;
printf("@t%d\n", i1);
printf("D=M\n");
printf("@t%d\n", i2);
printf("D=D%cM\n", op);
printf("@t%d\n", i);
printf("M=D\n");
}
// F = Number | '(' E ')'
int F() {
int f;
char c = ch();
if (isdigit(c)) {
next(); // skip c
f = nextTemp();
genOp1(f, c);
} else if (c=='(') { // '(' E ')'
next();
f = E();
assert(ch()==')');
next();
} else {
error("F = (E) | Number fail!");
}
return f;
}
// E = F ([+-] F)*
int E() {
int i1 = F();
while (isNext("+-")) {
char op=next();
int i2 = F();
int i = nextTemp();
genOp2(i, i1, op, i2);
i1 = i;
}
return i1;
}
void parse(char *str) {
tokens = str;
E();
}
int main(int argc, char * argv[]) {
printf("=== EBNF Grammar =====\n");
printf("E=F ([+-] F)*\n");
printf("F=Number | '(' E ')'\n");
printf("==== parse:%s ========\n", argv[1]);
parse(argv[1]);
}
### fopen
```
int readText(char *fileName, char *text, int size) {
FILE *file = fopen(fileName, "r"); //讀檔
int len = fread(text, 1, size, file);
text[len] = '\0';
fclose(file);
return len;
}
```
>參數:
>r:讀-打開文件進行輸入操作。該文件必須存在。
>w:寫-創建一個空文件用於輸出操作。如果已經存在同名文件,則其內容將被丟棄,並且該文件將被視為新的空文件。
>a:加附-打開文件以在文件末尾輸出。輸出操作始終將數據寫入文件的末尾,然後對其進行擴展。重新定位操作(fseek,fsetpos,rewind)將被忽略。如果文件不存在,則創建該文件。
>r+:讀/更新-打開文件進行更新(用於輸入和輸出)。該文件必須存在。
>w+:寫/更新-創建一個空文件並打開以進行更新(用於輸入和輸出)。如果已經存在同名文件,則將其內容丟棄,並將該文件視為新的空文件。
>a+:加附/更新-打開文件進行更新(用於輸入和輸出),所有輸出操作均在文件末尾寫入數據。重新定位操作(fseek,fsetpos,rewind)會影響下一個輸入操作,但是輸出操作會將位置移回文件末尾。如果文件不存在,則創建該文件。
### C語言指標
- pointer1.c
```
# include <stdio.h>
int main(){
char x='a';
char *p=&x;
*p='b';
printf("*p=%c x=%c\n",*p,x);
}
```
result
```
$ ./pointer1
*p=b x=b
```
- pointer1bug.c(野指標)
```
# include <stdio.h>
int main(){
char x='a';
char *p; // =&x;
*p='b';
printf("*p=%c x=%c\n",*p,x);
}
```
result
```
$ ./pointer1bug
Segmentation fault //分段錯誤
```
>因為p指標沒有值,所以他會亂指,導致發生錯誤。
### 虛擬機
- 虛擬機v.s模擬器
虛擬機:有模擬整套CPU的指令集
模擬器:模擬電腦外部的行為
三種虛擬機:
1. 記憶體機(Memory Machine): 可直接對記憶體變數進行運算
2. 暫存器機(Register Machine): 必須將變數載入暫存器中,才能進行運算
3. 堆疊機(Stack Machine): 會取出堆疊上層元素進行運算,並將結果存回堆疊中
### C4 編譯器
- C4 編譯完成後,會產生一種《堆疊機機器碼》放在記憶體內,虛擬機會立刻執行該機器碼
### 堆疊段
- power2.c
```
#include <stdio.h>
int power2(int n){
if(n==0) return 1;
return power2(n-1)*2;
}
int main(){
int p = power2(3);
printf("p=%d\n", p);
}
```
![](https://hackmd.io/_uploads/r1JAhisD2.png)
>[圖片出處](https://www.slideshare.net/ccckmit/1-73472884)
### vim
i:進入編輯模式
Esc:離開編輯模式
o:插入一行
yy:複製一行
dd:刪除一行
cc:剪下一行
p:貼上
在非編輯模式下輸入 :wq 再 Enter,就會存檔和離開程式。
### 程式的位置
text:這個區段通常位於 heap 或 stack 之後,避免因 heap 或 stack 溢位而覆寫 CPU 指令。
data:儲存的是一些已經初始化的靜態變數。變數又可分為唯讀區域(read-only area)以及可讀寫區域(read-write area),可讀寫區域用於存放一般變數,其資料會隨著程式的執行而改變,而唯讀區域則是存放固定的常數。
bss:儲存尚未被初始化的靜態變數,而這些變數在程式執行之前會被系統初始化為 0 或是 null。
stack:一般的狀況會從高記憶體位址往低記憶體位址成長,儲存函數的區域變數,以及各種函數呼叫時需要儲存的資訊,每次函式呼叫會儲存該次呼叫的所有變數與狀態,不會互相干擾。
heap:儲存動態配置的變數,例如 C 語言的 malloc 以及 C++ 的 new 所建立的變數都是儲存於此。
system:儲存一些命令列參數與環境變數,這部分會跟系統有關。
### 作業系統的五大功能
- 行程管理 打造一個環境讓任何程式能不受干擾的執行
- 記憶體管理 打造一個記憶體配置環境,當程式向系統請求記憶體時,就能獲得所需空間,且不需考慮其他程式
- 輸出入系統 能將輸出入裝置包裝成系統函數,讓輸出入作業變容易使用
- 檔案系統 能讓程式師及使用者輕鬆存取永久儲存裝置中的檔案
- 使用者介面 讓程式師及使用者能輕鬆操作的環境
### X86架構的通用暫存器(GPR)
1.累加器暫存器(AX)。用在算術運算。
2.基址暫存器(BX)。作為一個指向資料的指標(在分段模式下,位於段暫存器DS)。
3.計數器暫存器(CX)。用於移位/迴圈指令和迴圈。
4.資料暫存器(DX)。用在算術運算和I/O操作。
5.堆疊指標暫存器(SP)。用於指向堆疊的頂部。
6.棧基址指標暫存器(BP)。用於指向堆疊的底部。
7.源變址暫存器(SI)。在流操作中用作源的一個指標。
8.目標索引暫存器(DI)。用作在流操作中指向目標的指標。
>[資料出處](https://zh.m.wikibooks.org/zh-tw/X86%E7%B5%84%E5%90%88%E8%AA%9E%E8%A8%80/X86%E6%9E%B6%E6%9E%84%E5%8F%8A%E5%AF%84%E5%AD%98%E5%99%A8%E8%A7%A3%E9%87%8A)
### MMU硬體
- 重定位暫存器
![](https://hackmd.io/_uploads/BkpUynjP2.png)
- 基底界限暫存器
![](https://hackmd.io/_uploads/Bym_yhjP3.png)
- 分段機制
![](https://hackmd.io/_uploads/ryKK13oP3.png)
- 分頁機制
![](https://hackmd.io/_uploads/BJA9y2sv3.png)
>[圖片出處](https://www.slideshare.net/ccckmit/10-73472927)
### blocking & nonblocking
```
$ gcc blocking1.c -o blocking1
$ ./blocking1
abc
buf is abc
test
123
buf is 123
test
^C
$ gcc nonblocking1.c -o nonblocking1
$ ./nonblocking1
read /dev/tty: Resource temporarily unavailable
no input,buf is null
read /dev/tty: Resource temporarily unavailable
no input,buf is null
adsjkf;lasf
ret = 12, buf is adsjkf;lasf
read /dev/tty: Resource temporarily unavailable
no input,buf is null
adfkread /dev/tty: Resource temporarily unavailable
no input,buf is null
a;
adlsfkret = 7, buf is adfka;
read /dev/tty: Resource temporarily unavailable
no input,buf is null
^C
$ gcc nonblocking2.c -o nonblocking2
$ ./nonblocking2
read /dev/tty: Resource temporarily unavailable
no input,buf is null
aldjf
ret = 6, buf is aldjf
```
### telnet1
```
$ make
gcc -Wall -std=gnu99 server.c ../net.c -o server
gcc -Wall -std=gnu99 client.c ../net.c -o client
$ ./server&
[1] 24441
$ ./client
connect to server 127.0.0.1 success!
127.0.0.1 $ ls
cmd=ls
client
client.c
Makefile
README.md
server
server.c
127.0.0.1 $ exit
```
### 管線
是一系列將標準輸入輸出連結起來的行程,其中每一個行程的輸出被直接作為下一個行程的輸入。 每一個連結都由匿名管道(pipe)實現。管道中的組成元素也被稱作過濾程式。
>[參考資料](https://zh.wikipedia.org/wiki/%E7%AE%A1%E9%81%93_(Unix))
### fifo(命名管道)
可以通過呼叫mkfifo或od(8進位)來構建,當被呼叫時表現為輸入或輸出的檔案。這樣可以允許建立多個管道,並且將其同標準錯誤重新導向或tee結合起來使用更為有效。
- install screen
```
sudo apt-get install screen
```
- 指令
screen : 執行他
exit : 離開
ctrl + a + c : 新增視窗
ctrl + a + n : 變換視窗
>fifo1先寫再讀,fifo2先讀再寫,如果沒按照順序執行會錯誤
### multithread & poll & epoll 差異
能支撐的客戶端及效能 epoll > poll > mutithread
mutithread/server.c: 每多一個客戶要多一個thread
poll/server.c: 整個server只用一個thread(主程式+一個thread),事件要一個一個測
epoll/server.c: 整個server只用一個thread(主程式+一個thread),只要測有事件發生的串流,而不需要全測