---
robots: index, follow
tags: NCTU, CS, 共筆, Unix
description: 交大資工課程學習筆記
lang: zh-tw
dir: ltr
breaks: true
disqus: calee
GA: UA-100433652-1
---
高等 UNIX 程式設計 -- 黃俊穎
===
http://people.cs.nctu.edu.tw/~chuang/courses/unixprog
unix107
up19peppa
## Syllbus
- Unix 越來越熱門
- 老師: 黃俊穎 (chuang@cs.nctu.edu.tw)
- Prerequiste
- C / Cpp
- (little) assembly (IA32, IA64)
- OS
- Computer network
- W. Richard Stevens and Stephen A. Rago, "Advanced Programming in the UNIX Environment"
- Topics
- Fundamental tools
- File & directories
- file / standard IO
- System data & info.
- Process environment
- Process control
- Signals
- Assembly
- Threads
- Thread control
- Daemon
- Advanced IO
- Inter-process communication
- Network IO
- ...
- 用 VM
- 需要開 VT-x (BIOS)
- Grading
- Mid: 25%
- Final: 35%
- HW: 40% (3~6)
## Overview & env
- prepar UNIX
```sh
sudo apt-get install gcc g++ gdb make manpages-dev manpages-posix manpages-posix-dev
```
- Kernel

- Boot
- OS loader (ex. grub)
- 從特定位址 load program
- Kernel init system hardware
- first process
- init
- systemd
- /bin/sh (系統救援時,可以叫 sh 先起來)
- after process
- mount
- network
- daemon
- login interface
- ...
- File System (FS)
- windows: partition base directory
- linux: root base directory


- Filesystem Header Standard
- Linux command
- [LSB (Linux Stanard Base)](https://wiki.linuxfoundation.org/lsb/start)
- [FHS (Filesystem Hierarchy Standard)](https://wiki.linuxfoundation.org/lsb/fhs)
- 如何 touch 有 '-' 開頭的 file? (- 感覺會被 parse 成參數):用 `touch ./-aaa`
- Redirection
- `>` `<`
- Pipe
- `|`
- man (manu page)
- `man [section] command`
- 不寫 section 了話都是 section 1
- `man -k command`: 找到有哪些 section
- 標準 section
- 
- Sample Code
- http://people.cs.nctu.edu.tw/~chuang/courses/unixprog/resources/inclass-20160530.tar.gz (老師修改的 code)
- https://people.cs.nctu.edu.tw/~chuang/courses/unixprog/resources/textbook-20180326.tar.gz (課本的 code)
- Return value
- 0: true, !0: false
- `echo $?`: 上一個 process 的 return value
- `||`: 如果前面 return true,就不用執行後面的
- `&&`: 如果前面 return true,才執行後面的
- Handle Program
- argc, argv[]
- getopt
- getopt_long
- Time
- time command
- real: average amount of time required per iteration, in microseconds. Time is measured in elapsed time, not CPU time.
- user: user space 用的時間
- sys: kernel space
- Error
- return value
- errno: error number(3) (global)
- 
- 轉換成看得懂的
- strerror
- perror
- Error Recovery
### Tools
- Compiler
- ex. gcc, clang, ...
- gcc
- `-S`: 生成 .s (assembly)
- `-c`: 只編譯,不 link
- `-l`: link library
- `-I`: add include path
- `-L`: add lib path
- 大 project 建議分開 compile 再 link (Makefile)
- 這樣單獨檔案被跟改時,不會需要所以 source code 都被重新 compiler 一次
- 不同語言
- 通常語言會各自加自己的 header 在變數名稱(text session)前面
- => 不同語言不能直覺直接 link
- `nm`: 可以解析 object file 的指令(list symbols)
```cpp
#ifdef__cplusplus (only needs for a C++ compiler)
extern "C" { (declare that everything within the scope)
int b(); (should be treated as C symbols, not C++ )
}
#endif
```
- make, Makefile
- Ref: https://www.gnu.org/software/make/manual/make.html
- 簡化 build process
- 管理相依性與多平台選擇參數問題
- 參數
- `-C`: 修改工作目錄
- `-f`: 指定檔案 (就不一定要叫做 Makefile 了)
- `-j`: 同時編譯
- 內容
```Makefile
rulename: dependencies
rule
# comment
split to\
multi-lines
```
- 預設變數
- `$@`: target 名稱
- `$<`: **第一個** prerequisite
- `$?`: 比 target 新的 prerequisite
- `$^`: 所有 prerequisite (會移除重複)
- `$+`: `$^` 但不移除重複
```Makefile
haha: a b c a d
echo $@
echo $<
echo $?
echo $^
echo $+
# output:
# haha
# a
# a b c d
# a b c d
# a b c a d
```
- 注意,只要 rulename 內的任一檔案 timestemp 不同,make 都會把 rulename 內的全部 rule 再跑一次
- .PHONY: 不事先檢查檔案是否存在
- 在不指定開始的 rulename 時,會以第一個 rulename 開始執行 (不是 all)
- `.c.o:`: .c 檔轉成 .o 檔
- 舊式,新式改成 `%.o: %.c`
- `.c.o` [缺點](https://www.gnu.org/software/make/manual/make.html#Suffix-Rules)
- GDB
- `-g`: compile 時產生 debug synble,不用不影響 gdb 指令,但是會看不到 source code
- debug symble 不會存 souce code,他是 link 到 source file,所以如果 source file compile 後又被改過,會看到錯誤的資訊
- command:
- `list`: 列出 sourse code
- `run`: always run from begin
- `s/step`: 跳一行,但會跳進 function call (if esixt)
- `n/next`: 跳一行
- `ni/nexti`: next instruction (asm level)
- `p/print`: print source code
- `c/continue`: 跳到下一個 break point
- `b/break`
- `layout {ams|regs|src}`
- `bt/backtrace`: 查看 call stack
- `x/FMT <address>`: 用 MFT 方式查看記憶體位址
- `fin/finish`: 從 call stack 中跳回上一層
- Core dump file
- `ulimit`
- ...
- debug w/o asm
- stript
- ...
## File IO & Standard IO
- File IO
- file discriptor
- 所有開啟的檔案在 kernel 下都有一個 FD 來描述(紀錄)
- 非負整數 [0, OPEN_MAX-1]
- STDIN_FILENO(0), STDOUT_FILENO(1), STDERR_FILENO(2)
- Unbuffered IO
- user space 下是沒有 buffer 的
- 直接跟直接跟 kernel 溝通
- 通常只有這五種: open, read, write, lseek, and close
- 可以用 pipe 做重導向
- `a>b`: 將 FD a 重導向 FD b
- `> file`: 通常不寫就是將 FD 1(stdout) 重導向 file
- `|`: pipe()
- Standard I/O
- buffered IO
- user space 下有 buffer
- ex. fwrite() 事先寫入 buffer 才寫入 file
- buffer 填滿後才一起塞入 write => 減少 kernel 呼叫
- 透過 wrapper of FD 存取
- stdin, stdout, and stderr
- 透過 fileno() 可以看到 FD number
- ex.
- open(path, flag, mode)
- mode: permission
- rwxr-xr-x: 655
- flag:
- O_RDONLY, O_WRONLY, O_RDWR
- O_APPEND, O_CREAT, O_EXCL(exclusive?), O_TRUNC(把從在的檔案清空,讓看起來像 create 的), O_SYNC(去掉 device buffer 的影響)
- create(path, mode)
- close(fd)
- process 結束時會自動 close 所有的 open file
- lseek(fd, offset, whence)
- 用來移動 file pointer (device 要可以 random access)
- whence: SEEK_SET(最前), SEEK_CUR(目前), SEEK_END(最後)
- 多用 lseek64(fd, offset64, whence)
- Random access vs Sequential access
- file: Random access
- pipe: Sequential access
- `/var/spool/cron/FIFO`: Sequential access
- Large File Support (LFS)
- 2GB 以上
- 需要 define _LARGEFILE64_SOURCE, _LARGEFILE_SOURCE 與 _FILE_OFFSET_BITS 64 (在 include 任何 c library header 前)
- 開檔需要參數 O_LARGEFILE
- `gcc –D_LARGEFILE64_SOURCE –D_LARGEFILE_SOURCE –D_FILE_OFFSET_BITS=64`
- File hole: 一份檔案裡有部分空間沒有寫到,沒寫入的部分 FS 會自動填入 0
- 檔案大小是從第一個 pointer 到最後一個 pointer (最後接 EOF)
- 檔案實際大小(硬碟消耗空間)是 4KB * n > 寫入大小
- ex. 寫 10byte + 跳 16374 + 寫 10byte
- file size: 16394 B
- write disk size: 8 KB
- `ls -s`: 看 write byte
- `strip`: re-format block write,有機會整理壓縮寫入 block size
- 
- 
### File IO other issue
- IO Efficiency
- 如果 buffer size 太小,因為每次 read 都要 kernel call,效率會減低
- 如果 buffer size < block size
- 每次 read 進 DMA 後,來到 disk 會是同一個 block => DMA 需要重複讀取一樣的資料
- File sharing
- kernel 如何實作 file open
- process table
- table of file descriptors
- v-node
- 
- sharing 時
- 到 file pointer 時,都是 per process 的
- 然後將 file pointer 的 v-node 指向同一個 v-node
- fork(),因為 file table 存在 heap (per process),所以會 duplicate 一份
- process-table 會複製一份,file table 也會複製一份,vnode 是同一份
- Atomic Operation
- 如果開檔後,fork 一個新的 process,然後再把 file pointer 移到最後面,加入 10B
- 會產生非預期操作 (unexpected order)
- Atomic: 只用一個 x86 instruction 可以執行出一個 function call => 不用擔心 interrupt
- pread, pwrite
- pread: seek + read
- pwrite: seek + write
- open = check + create => interrupt
- open(pathname, O_CREAT | O_EXCL, mode)
- check + create 會在同時執行 (中間不會被中斷)
- dup, dup2 (duplicate)
- 
- dup2 的 target fd 要確保關閉了
- dup(n): 將 fd{n} 的 file table entry 複製一份到最新關閉的 fd (target 由 fd 自己維護)
- dup2(1, 3): 將 fd1 的 file table entry 複製一份到 fd3 (target 需要是 close 的)
- 
- sync, fsync, fdatasync
- 因為 IO 其實會先 queue 在 buffer 再一起寫入
- 跟 buffered IO 的 buffer(user space) 不同,是 kernel 裡的 buffer
- 對某個 fd
- fsync: filedata + metadata
- fdatasync: filedata only
- 所有檔案
- sync: filedata + metadata
- 如果想要強迫寫入,可以用 sync
- ioctl
- 所有 device 在 UNIX 下都是 file => 可以用 ioctl access
- dependency by driver
- linux cross reference
- /dev/fd
- virtual file system
- `/proc/[pid]/fd/` : 看 process 有維護的 fd
### Standard IO
最大的差別,stdio 有 **buffer**,他不會每次 call 都做 system call,而是只有在 buffer 被填滿時才 system call 寫入
- Buffering
- 3 mode
- fully buffered
- 整份檔案 call 進 buffer
- ex. fopen(..., "rb") 中的 `b`
- ex. stdio
- line buffered
- 一行一行吃,在 buffer 中讀到 `\n` 時,再去跑一次 kernel call
- ex. fopen(..., "rt") 中的 `t`,會讓這個 io 一行一行吃
- unbuffered
- ex. stderr
- fdopen(fd, mode)
- 將指定 fd 的檔案的 fp 抓出來回傳,好處是之後可以直接用 file pointer 來操作這個 檔案 / io / device / ...
- size_t fwrite(void *ptr, size_t obj_size, size_t nr_obj, FILE *fp);
- 回傳寫入了幾個 object
- 如果 size of obj 沒有寫完全,就是沒有寫入這個 obj
- ex. `fwrite(pt, sizeof(double), 2, fp)`
- 如果今天 pt 只有 2Byte,無法完全寫入一個 double 的長度,就會回傳 0
- fseek()
- 暫存檔
- 一種直接被覆寫也不會怎麼樣的檔案
- `char *tmpnam(char *ptr)`
- 會在 `/tmp/fileXXXXXX` 開出暫存檔
- 檔名相同時,會被 overwrite (危險)
- `tmpfile(void)`
- 推薦使用
## File & Dir
- linux 的 filename 除了 `/` 跟 `null` 外,都可以用
- 最多 255 character (PATH_MAX)
- 每個 process 都有自己的 working directory
- cwd (current working directory)
- chdir (cd)
- home dir 是使用者登入時的第一個 working dir
- 在 `/etc/passwd` 設定
- file information:
- api
- `int stat(const char *path, struct stat *buf);`
- `int fstat(int fd, struct stat *buf);`
- `int lstat(const char *path, struct stat *buf);`
- stat 與 lstat 幾乎一樣,lstat 不 follow symbolic link
### File information
- file type
- Regular file、Directory file、Block special file、Character special file、FIFO、Socket、Symbolic link
- block special file 與 character special file 差在 random access
- file permission (discentralized permission <-> SELinux)
- Real UID, Real GID
- 實際登入的人
- Effective UID, Effective GID
- 確認檔案權限
- SUID, SGID
- set UID
- exec 會儲存狀態
- 當 SUID enabled 時,RUID 還是會是執行本人,但 EUID 就會變成程式檔案的擁有者 (SUID)
- function: setuid(2), setgid(2)
- 9bit permission
- 
- access permission
- dir 要有 x 權限
- 刪除檔案要有 dir wx 權限
- 可以用 id 指令來查看 id / `/etc/passwd` / `/etc/groups`
- `int access(const char *path, int mode);`: 確認權限
- mode bit:
- R_OK, W_OK, X_OK: read, write, exec permission
- F_OK: file exist
- `mode_t umask(mode_t cmask);`: mask 掉權限
- 直接用 umask 指令可以看到 touch 檔案時的預設權限 (被 mask 掉的數字)
- 可以用 umask 指令改寫預設檔案權限 `umask 0` -> 預設所有權限都開
- 12-bit file permission
- suid,sgid,sticky,9-bit permission
- sticky bit
- executable
- cache 在 swap
- 減少 loading 時間
- dir
- delete or rename 的權限
- owner of file
- owner of dir
- superuser
- ex: /tmp
- file system
- 
- i-node
- file metadata
- type
- permission
- data blocks
- timestamps
- reference counts
- 通常為正整數
- 特別用途
- 0: reserved, or does not exist
- 1: list of bad/defective blocks
- 2: root directory of a partition
- 兩個 dir 可以指向同一個 i-node
- 
- create dir
- 
- 
- reference counts
- 指向 i-node 的 pointer 個數
- 增加
- link
- hard link (必須在同一個 partition)
- 減少
- unlink
- symbolic link (soft link)
- 大小為 target name 的長度
- 小心迴圈
- soft link 是可以指向不存在的檔案,但 hard link 不可
- 
- symlink
- 建 symbolic link
- target 和 link 可以在不同 file system
- readlink
- read target pathname
- 結合 open, read and close
- file time
- 
- 
- utime
- change access and modification time
- directory operation
- `void rewinddir(DIR *dir)`
- reset pointer 到 dir stream 的開始
- `off_t telldir(DIR *dir)`
- dir stream 現在的位置
- `void seekdir(DIR *dir, off_t offset)`
- device special file
- `dev_t` device number
- major
- device driver
- minor
- specific sub device
- `st_dev`
- `st_rdev`
- created by `mknod` or auto generated when dev register
- `hdaN?`、`sdaN?`、`scdN`、`ttyN`、`ttySN`、`pts/N`、`null`、`zero`、`random`
## sysinfo