

Thread Model#
Thread Concept#
Process Model#
- Resource Grouping (資源分組):Process 是作業系統分配資源(記憶體、檔案等)的基本單位。
- Single Thread of Execution:傳統的 Process 中只有一個執行緒(Thread),指令是單一序列執行的。
Thread Model#

Thread Model
- 概念:在同一個 Process 的生命週期內,可以同時執行多個 Thread。
- 資源共享 (Resource Sharing):
因為屬於同一個 Process,Thread 之間共享大部分的資源:
- Global Data / Heap Memory
- Open Files Descriptor
- Code (Text Segment)
- Child Processes
- Pending Alarms / Signals
- 獨立擁有 (Per-Thread Resources):
為了能夠獨立執行並進行 Context Switch,每個 Thread 必須擁有自己獨立的:
- Stack (維護 Function call chain)
- Registers (包含 Context Switch 時需要保存的狀態)
- Program Counter (PC) (紀錄目前執行到哪一行指令)
- Scheduling Policy (排程優先級)
- Signal Mask (對訊號的遮罩設定)
- Thread specific data
Process v.s. Thread#
Process#
- 競爭關係:Process 之間通常處於資源競爭狀態。
- 資源隔離:預設情況下無法共享資源(基於安全性 Security 考量),若要溝通需透過 IPC (Inter-Process Communication)。
- 建立成本高:使用
fork()建立新 Process 是 System Call,需複製記憶體空間與 Page Table,開銷較大。
Thread#
- 合作關係:通常由單一使用者(Owner)啟動,Thread 之間是為了完成同一任務而合作。
- 無保護機制 (No Protection):Thread 之間直接共享 Memory,先天沒有隔離保護(假設同一 Process 內的 Thread 彼此信任)。
- 地位平等:雖然由 Main thread 建立其他 Thread,但在 OS 排程眼中,它們地位通常是平等的(Peer),無階層從屬關係。
- Context Switch 成本低:切換 Thread 時只需保存暫存器與 Stack 指標,不需要切換 Page Table(Memory Space 不變)。
- 非同步與併發 (Asynchronous & Concurrency):
- Thread 可以獨立運作。當 Process 收到 Signal 或需處理 I/O 時,可以只讓負責該工作的 Thread 暫停(Suspend)或處理,其他 Thread 繼續執行,不需整個 Process 停擺。
- 這種特性增加了程式的 Throughput。
- 回應時間 (Response Time):
- 透過多執行緒,可以讓負責 UI 或回應的 Thread 保持運作,將耗時的運算或 I/O 交給背景 Thread,大幅提升使用者的操作體驗。
- 除錯困難 (Debugging):
- 由於資源共享且執行順序不確定(Race Condition),除錯遠比單一 Process 複雜。
- 單核處理器 (Single Processor) 的效益:
- 在 Single processor(core) machine 上,Thread model 無法達到真正的「平行運算 (Parallelism)」,只能做到「併發 (Concurrency)」。
- 但並非沒用:它依然能透過重疊 I/O 等待時間與 CPU 運算時間,有效減少 Response time 並提升系統整體效率。

Thread
Implementing of Thread#
Implementing of Thread (User Space)#

Implementing of Thread 1
- Runtime System: 一個 Library 去控制所有 thread 出生死亡,用 function call
- Thread Table: Runtime System 內部 maintain TCB
- Process 要有 PCB (Process Control Block),同理 Thread 也有 TCB
- Kernel 並不知道 Process 裡面到底長什麼樣子,只是 schedule process 們
- Thread 是用 Runtime System 去控制,TCB 也是存在裡面的
Implementing of Thread (Kernel Space)#

Implementing of Thread 2
- Thread table 會在 kernel 內,可以做不同 process 間的交互處理
- kernel 知道是 thread 被 block 而不是 process 所以可以 schedule 其他 thread
Hybrid Implementation#

Hybrid Implementation
- 讓 kernel thread 各自做 user thread 的 context switch
Thread Control#
- 以前的 modle 都可以當作是 single thread 的 process
- 我們都講 POSIX 標準
Thread creation#
int pthread_create( pthread_t *tidp, pthread_attr_t *attr, void *(*start_rtn)(void *), void *arg )
tidp: thread idattr: attributevoid *(*start_rtn)(void *): 指向通用型態的 functionarg: 送進start_rtn
任何被 create 跟 create 別人的 thread 會根據 priority 來決定跑的順序,跟 fork() 很像
- 只分成 main thread 跟 spawned thread,而且並非主從關係
- Normal function call

Thread creation 1
- Threaded function call

Thread creation 2
pthread_t pthread_self(void)
- thread id 早期是 integer 但有些系統用 pointer,
pthread_t可以 portable - 可以獲取自己的 id
int pthread_equal ( pthread_t tid1, pthread_t tid2 )
- 比較用 function
int pthread_attr_init(pthread_attr_t *attr)
int pthread_attr_destroy(pthread_attr_t *attr)
- 先把 variable 傳入,然後去 initalize 他
detachstate: 如果是的話,termenation state 不需要回收,直接清掉,在 TCB 內guardsize: 防止壓到別人,要有多少緩衝區stackaddrstacksize: stack 大小限制
- 必須要去 destroy 他,因為會佔用 heap
- 會把
pthread_attr_t設定為 invalid
- 會把
int pthread_attr_getdetachstate (pthread_attr_t *attr, int *detachstate)
int pthread_attr_setdetachstate (pthread_attr_t *attr, int detachstate)
- 可以設定或拿到他的
attr PTHREAD_CREATE_DETACHED,PTHREAD_CREATE_JOINABLE(default),可以設定
int pthread_detach(pthread_t tid)
- 進行 detach 設定,只要你有
tid就可以 pthread_detach(pthread_self())- 並不是殺掉,只是不回收而已
- 有三種情況會被 detached
pthread_detach()PTHREAD_CREATE_DETACHEDpthread_join(),被拿走了
Passing Argument#
一定要確定對面的 thread 不可以自己結束,不然會出問題
int- Pass
cint i = 42; pthread_create(..., my_func, (void *)&i); - Retrieving
cvoid *myfunc(void *vptr) { int value = *((int *)vptr); }
- Pass
- Passing
string- Pass
cchar *str = ”NTU”; pthread_create(..., my_func, (void *)str); - Retrieving
cvoid *myfunc(void *vptr) { char *str = (char *)vptr; }
- Pass
- Passing
array- Pass
cint arr[100]; pthread_create(..., my_func, (void *)arr); - Retrieving
cvoid *myfunc(void *vptr) { int *arr = (int *)vptr; }
- Pass
Thread Termination#
- 有些情況會把所有 thread 一次關掉
_exit()- signal default 是 terminate
return了main()(main thread)
void pthread_exit (void *rval_ptr)
- 呼叫後不會有任何的其他動作,只是關掉 thread
- 其他資源是 process 共享的,所以不會被關掉
- 在 main thread 裡面呼叫
pthread_exit()就是要 block 住 main thread 直到所有他 create 出來的 thread return
int pthread_join(pthread_t tid, void **rval_ptr)
- 也可以呼叫這個 function,意思是等到所有他自己 spawn 出來的 non-detach thread 結束,拿到結束碼
- thread 異常死亡叫做被他人 cancel,拿結束碼會拿到
PTHREAD_CANCELED - thread 被拿走結束碼後就會 detechable
- 任何出錯都要看 return value 不是一般 fucntion 的
errno - non-detechable thread 死掉沒被 join 就是 zombie thread
- threadID 發完了就不能繼續 create 了
- 呼叫 join 的 thread 被打斷了就可以用
pthread_timedjoin_np()returnETIMEDOUT
Thread Cancel#
任何 thread 都可以 cancel 其他人
int pthread_cancel(pthread_t tid)
- 相當於讓要被 cancel 的呼叫
pthread_exit()然後,然後傳參數給PTHREAD_CANCELED - 被 cancel 的人可以決定自己要不要被 cancel
PTHREAD_CANCEL_ENABLE(default)PTHREAD_CANCEL_DISABLE可以不讓別人 cancel- 在
PTHREAD_CANCEL_ENABLE之下 傳來 cancel 也只是 Only makes the request 而已,會運行直到 cancelation point
int pthread_setcanceltype(int type, int *oldtype)
- 在可以被 cancel 的情況下
PTHREAD_CANCEL_DEFERRED(default) 運行到 cancelation pointPTHREAD_CANCEL_ASYNCHRONOUS直接死
void pthread_testcancel(void)
- 設定成可以 cancel 的 cancel point,在我沒有任何 cancelation point 的情況下
void pthread_cleanup_push(void (*rtn)(void *), void *arg)
void pthread_cleanup_pop(int execute)
- 也是可以 clean up,用 reverse way
- 可以自行 push, pop
rtn裡面的 function 會被 call 出來的情況pthread_exit()pthread_cleanup_pop()- 被 cancel
- 如果呼叫
return ((void *) 2);就不會往後 pop 出所有東西 - 正常異常死亡都會呼叫出
rtn內的 function 們,用 reverse way
Thread Synchronization#
- Share memory 太常出現,所以我們必須要產生同步,不然會出現問題,因為一個高階語言指令可能是四五個低階語言指令

Thread Synchronization
- 兩個 thread 要讀/寫入同一個資料是不同的 code 會競爭到同一個 resource
- 即便有一個 resource 只有一個 function 可以 access,兩個 thread call 同一個 function 還是會有 competition,還是需要 Synchronization
Mutexes#
- 全名 Mutual-exclusion interfaces
- 是一種 binary advisory lock 但是是 for memory resource 而非先前的 file lock
- A thread 用完 resource unlock 後,BCD 三個 pending lock 就會變成 runable,也就是 unlock 後剩下的人才會被送到 ready queue
- 再由 system 根據 priority 去決定是誰獲得 mutex lock
- 其他沒拿到 lock 的 thread 又會被 suspend
- 只有搶到 lock 才會 return
read(),write()前都需要先進行 mutexes check- programmer 要自己去 check,如果跳過這關也不會被阻止,因為 thread 在 OS view 來看是互相 trustable 的
int pthread_mutex_init ( pthread_mutex_t *mutex, pthread_mutexattr_t *attr )
int pthread_mutex_destroy ( pthread_mutex_t *mutex )
- 宣告一個
pthread_mutex_t要做 initialize- 也可以
做靜態宣告
cpthread_mutex_t mlock = PTHREAD_MUTEX_INITIALIZER; - 也可以動態宣告,用
pthread_mutex_init()+pthread_mutex_destroy()
- 也可以
NULL就是 default- attribute 也要做 initialize
- Process-share attribute
PTHREAD_PROCESS_SHARED可否接受多個 process share Mutex 的 memory(非 file)- compile time 用
_POSIX_THREAD_PROCESS_SHARED看 system 有沒有這個功能 - runtime check
_SC_THREAD_PROCESS_SHARED看 system 有沒有這個功能 pthread_mutexattr_getpshared(),pthread_mutexattr_setpshared()
- Type attribute
PTHREAD_MUTEX_NORMAL: 不做 error check 也不做 deadlock checkPTHREAD_MUTEX_ERRORCHECKPTHREAD_MUTEX_RECURSIVE: relock 幾次都不出事,lockcount會記錄被鎖幾次,解鎖也要相對次數- PTHREAD_MUTEX_DEFAULT: 會是以上三種的其中一種,每個 system 不一樣
int pthread_mutex_lock(pthread_mutex_t *mutex)
int pthread_mutex_trylock(pthread_mutex_t *mutex)
int pthread_mutex_unlock(pthread_mutex_t *mutex)
pthread_mutex_lock()是 block modepthread_mutex_trylock()是 non-block mode 如果出錯會 returnEBUSY為錯誤碼
Share memory#
Memory-mapped I/O#
void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);

Memory-mapped I/O 1
- 先把 memory map 到 virtual memory 上,再利用 system call 把它 mapping 回去
- Virtual 跟 Physical memory 都會切成一個一個 page,兩端 page size 必須相同,必須得 align
- 對 system call 一個
malloc()會返回一個可以任意轉換 type 的 address,但對於 align 方法有要求

Memory-mapped I/O 2
- 不像一般 IO 需要各種搬移
- Unbuffered I/O: Kernel − standard I/O buffer − our buffer
- Buffered I/O: Kernel − our buffer
- 理論上
len要是 page size 整數倍,但 system 有彈性,會幫你把後面的空(亂)資料也一起 mapping 做 align start_addr是NULL讓系統決定,要知道 heap, stack, page size 才能自己設定
addr對於 system 只是 hint 而已,不是真實值,除非使用MAP_FIXEDflagMAP_SHARED會寫回 physical memory,而MAP_PRIVATE會做 copy on write,延遲讀寫- mapping 多大就只能寫多大的檔案,不能動態增生

Memory-mapped I/O 3
int
main(int argc, char *argv[])
{
int fdin, fdout;
void *src, *dst;
struct stat statbuf;
if (argc != 3)
err_quit("usage: %s <fromfile> <tofile>", argv[0]);
if ((fdin = open(argv[1], O_RDONLY)) < 0)
err_sys("can't open %s for reading", argv[1]);
if ((fdout = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, FILE_MODE)) < 0)
err_sys("can't creat %s for writing", argv[2]);
if (fstat(fdin, &statbuf) < 0) /* need size of input file */
err_sys("fstat error");
/* set size of output file */
if (lseek(fdout, statbuf.st_size - 1, SEEK_SET) == -1)
err_sys("lseek error");
if (write(fdout, "", 1) != 1)
err_sys("write error");
if ((src = mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, fdin, 0)) == MAP_FAILED)
err_sys("mmap error for input");
if ((dst = mmap(0, statbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fdout, 0)) == MAP_FAILED)
err_sys("mmap error for output");
memcpy(dst, src, statbuf.st_size); /* does the file copy */
exit(0);
}c- 理論上這樣不對,因為做了 mapping 就得做 msycn, unmapping,所以應該要在
exit()前做這些事情(不寫等於給系統做) - 權限會繼承,因此檔案本身要有對的的讀寫權限
typedef struct {
pthread_mutex_t mlock;
char buf[ BUFSIZE ];
} buftype;
int main() {
int fd;
buftype *bufptr;
pthread_mutexattr_t mattr;
fd = open("/dev/zero", O_RDWR);
bufptr = (buftype *) mmap(NULL, sizeof(buftype), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
// bufptr = (buftype *) mmap(NULL, sizeof(buftype), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
pthread_mutexattr_init(&mattr);
pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(&bufptr->mlock, &mattr);
if (fork() == 0) {
pthread_mutex_lock(&bufptr->mlock);
// ...
pthread_mutex_unlock(&bufptr->mlock);
// call munmap(), pthread_attr_destroy(), close()
} else {
pthread_mutex_lock(&bufptr->mlock);
// ...
pthread_mutex_unlock(&bufptr->mlock);
// call wait(), munmap(), pthread_attr_destroy(), close()
}
return 0;
}c- 用
MAP_ANONYMOUS創造出一個 virtual file 來創造 share memory 的感覺

Memory-mapped I/O 4
- 兩個 share 到同一份 resource 同時要改,可以上 mutex lock,並且 lock 也要放在 share memory
- init 要在
fork()前就上好 - 在 process 裡面才去爭 lock
- init 要在

Memory-mapped I/O 5
Deadlock#
Resource Order
- 上各種 lock 都可能會發生 deadlock
- 解決方法是對 resource 排 order
A B C D ✔️ ✔️ ✔️ ✔️ ✔️ - 比如說在這個情況下我可以排 order 是要拿 B 必須先拿 A
- 要拿 B 必須先拿 A lock 這樣 , 就不會 cycle deadlock
- 如果上的 lock granular 好管理,但並不平行化
pthread_mutex_trylock()
- 也可用
pthread_mutex_trylock()non-blocking mode
ReaderWriter Locks#
- 照理來說 read lock 可以 share 但 mutex 是全部鎖死
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr)
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock)
- 跟 mutex 一樣需要 destroy 因為會動到 heap
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock)
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock)
Thread Pool#
- A master thread: 負責排工作,給 ID,把工作分配好後排進 link list
- A pool of worker threads: 真正做事的 thread,定期去 link list 上看一下有沒有自己的工作,然後改一下 list,自己去做事

Thread Pool
void
job_append(struct queue *qp, struct job *jp)
{
pthread_rwlock_wrlock(&qp->q_lock);
jp->j_next = NULL;
jp->j_prev = qp->q_tail;
if (qp->q_tail != NULL)
qp->q_tail->j_next = jp;
else
qp->q_head = jp; /* list was empty */
qp->q_tail = jp;
pthread_rwlock_unlock(&qp->q_lock);
}cstruct job *
job_find(struct queue *qp, pthread_t id)
{
struct job *jp;
if (pthread_rwlock_rdlock(&qp->q_lock) != 0)
return(NULL);
for (jp = qp->q_head; jp != NULL; jp = jp->j_next)
if (pthread_equal(jp->j_id, id))
break;
pthread_rwlock_unlock(&qp->q_lock);
return(jp);
}c- 有可能有 busy waiting
Condition Variable#
int pthread_cond_init(pthread_cond_t *restrict cond, pthread_condattr_t *restrict attr)
int pthread_cond_destroy(pthread_cond_t *cond)
- 一定會帶優個 mutex 的 lock
- 會有兩個 operation
- wait
- signal: 發送告訴 thread 你有事了(並非 ch7 的 signal)
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
- 先拿到 mutex lock
- Check condition,看看是否為 true
- 不是的話 Wait until a condition become true.
- Sleep until signaled
- Release the mutex just before sleep
- 直到這裡都會是 atomic operation
- 醒過來前要拿到 mutex 的 lock 不然要繼續回去睡
- 因此有兩種情形,有可能 signal 沒來的睡,跟 signal 來了的睡
int pthread_cond_signal(pthread_cond_t *cond)
- 多個人等的話,讓 system 去選一個人去叫醒
int pthread_cond_broadcast(pthread_cond_t *cond)
- 直接把所有 wait 的人全都叫醒
int done = 0;
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t c = PTHREAD_COND_INITIALIZER;
void *spawned(void *arg) {
// spawned thread goes first
pthread_mutex_lock(&m);
done = 1;
pthread_cond_signal(&c);
pthread_mutex_unlock(&m);
return NULL;
}
int main(int argc, char *argv[]) {
pthread_t p;
pthread_create(&p, NULL, spawned, NULL);
pthread_mutex_lock(&m);
while (done == 0)
pthread_cond_wait(&c, &m);
pthread_mutex_unlock(&m);
// main thread continues
}c- 對 main thread:要確認 condition 所以要上 mutex
- 發現 false 所以要進去等
- 等到 spawned thread 說
done = 1 - main thread 回來發現自己還沒拿到 mutex
- 會去跑 spawned thread unlock mutex
- 最後 main 才會跑
Thread and fork()#
如果只是一個 process 擁有很多個 thread,fork() 出來的 process 只會有一個 thread,就是呼叫 fork() 的那個
- Child 會繼承 Parent 的 virtual memory
- 假設有一個 B thread own 一個 mutex lock,parent 不會出事,B thread 會在結束的時候 unlock
- 但是若 A thread 在 unlock 前
fork(),那 child 會看到一個被 lock 住的 process,並且沒有任何一個 thread 知道要把 lock 放掉,產生 deadlock
Sloution:
- call
exec()in child(替換掉所有環境變數) - 先呼叫一個 fork handler
int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void))
- 只是註冊 function
- 可以多次註冊,執行順序:
prepare: reverse orderparent,child: correct order
- 在呼叫
fork()後- 先幫你 call
prparefunction 通常把 parent 的 lock 全部上上去 - 生 child process
- parent return 前 call
parentfunction - child return 前 call
childfunction,通常把所有 lock 都 unlock
- 先幫你 call
Thread and Signals#
每個 thread 也可以有自己的 signal mask
- signal disposition 是 process share 的
- 如果動到了 process level 的 signal 所有 thread 都會受影響
- 可能可以把一個 thread unblock 其他全都 block,這樣就會在這個 thread deliver
Synchronous & Asynchronous#
| How the signal was generated | What generated the signal | Effective target of the signal | How the signal-processing thread is selected |
|---|---|---|---|
| Synchronously | The system, because of an exception | A specific thread | Always the offended thread |
| Synchronously | An internal thread using pthread_kill | A specific thread | Always the targeted thread |
| Asynchronously | An external process using kill | The process as a whole | Per-thread signal masks of all threads in the process |
- 有些 Sychronous 的 signal 擁有非常明顯的 target 可以直接送往
- 但 Asychronous 的 signal 我們必須檢查 per-thread 的 signal mask 去看誰 unblock
Dedicate signal handling threads#
- 某些 thread 專門用來執行 signal handler
- 利用 signal mask 來指定那個 thread 要 handle 哪些 signal
- 通常會設計成一個 loop,一次執行一個 signal,同一個 handler 可以 handle 多個 signal
Inherent of signal mask#
- 一個新生的 child process 的第一個 thread 的 signal mask 會繼承 parent process 中 call
fork()的 thread 的 signal mask - 若 A thread 要 create B thread,B 會 inherit A create 他的時間點的那個 signal mask
int pthread_sigmask(int how, const sigset_t *restrict set, sigset_t *restrict oset)
- 用這個 system call 去做
sigpromask()的動作
int sigwait(const sigset_t *restrict set, int *restrict signop)
- 跟
sigsuspend()一樣,製作出 critical region - 雖然一樣,但是裡面的參數,你要等誰你就要在
set裡放誰,signal 來的時候我們就把它 unblock - 這個 function 被 return 就代表 signal 被 deliver 了
Sychronous handling signal#
- Signal 在 process 處理的時候是 asychronous,因為你不知道他何時來,有可能產生 re-entrance function 等一系列問題
- 在 thread 的情況,有很多 dedicate signal handling,先天就是平行的 model,本來就會保護,沒有在任意地方被中斷的問題,所以可以 sychronous 的處理,自己會獨立出去執行一個 thread
- 如果同時註冊了 process handler 跟 thread handler,system 會根據自己的 implementation 去做選擇(不建議這樣寫)
pthread_t stats_thread;
pthread_mutex_t stats_lock = PTHREAD_MUTEX_INITIALIZER;
extern int
main(void)
{
...
sigset_t sigs_to_block;
...
/* Set main thread's signal mask to block SIGUSR1.
All other threads will inherit mask and have it blocked too
*/
sigemptyset(&sigs_to_block);
sigaddset(&sigs_to_block, SIGUSR1);
// block first in here
pthread_sigmask(SIG_BLOCK, &sigs_to_block, NULL);
...
pthread_create(&stats_thread, NULL, report_stats, NULL);
...
}cvoid * report_stats(void *p)
{
sigset_t sigs_to_catch;
int caught;
sigemptyset(&sigs_to_catch);
sigaddset(&sigs_to_catch, SIGUSR1);
for (;;) {
sigwait(&sigs_to_catch, &caught);
/* Proceed to lock mutex and display statistics */
pthread_mutex_lock(&stats_lock);
display_stats();
pthread_mutex_unlock(&stats_lock);
}
return NULL;
}c- 上面那個區間,也就是
thread_create()到sigwait()之間要做成 critical region,不然 signal 來的話會 get lost,並且 block 的動作要在 create 以前就先做了,不然會有 race condition - 在
sigwait()unblock 完畢又 block 住後,會把直到下一次sigwait()unblock 前,又會形成下一次的 critical region
Thread-safe / Atomic IO#
- 在 thread 裡面,所有 file descriptor 跟 file stream 都是 share entry 的,所以必須要是 atomic operation 不然 offset 會跑掉
pread()/pwrite()用這兩個 system call 來做改動
ssize_t pread(int filedes, void *buf, size_t nbytes, off_t offset);
ssize_t pwrite(int filedes, const void *buf, size_t nbytes, off_t offset);
- 讀寫完畢並不會改變 open fd table 裡面的 current file
offset(lseek()的值)
Thread safe#
有些 function 在 thread 裡面呼叫是被禁止的
- Async-signal safe: 在傳統的 signal handler 裡面做 reentrant 是沒問題的 system call 或是 function call
- Thread-safe: 可以同時被很多 thread 呼叫,multiple threads reentrant
- 用
_POSIX_THREAD_SAFE_FUNCTIONS,_SC_THREAD_SAFE_FUNCTIONS去 check - 一個 function thread safe 不一定 Async-signal safe,但在大部分情況下(不惡搞)Async-signal safe implies
- 以下這些 function 並不保證是 thread-safe(但其實也可以找到 thread safe 的版本)

Thread safe 1
Thread-Safe FILE Objects#
void flockfile(FILE *fp)
int ftrylockfile(FILE *fp)
void funlockfile(FILE *fp)
- call
flockfile()就會用 block mode 做 lock ftrylockfile()是 non-block mode- 最後要
funlockfile()做 unlock - 是一個 recussive lock(lock again without deadlocking)
- 所有 Standard I/O 都會隱性幫你做 lock,雖然安全,但有時候做一些小的 I/O 會有效率問題(lock 上的太頻繁 granularity)
- 可以用 Unlocked version 的 character-based buffer I/O 做一次的 lock,兼具效率跟安全
- 用
getchar_unlocked(),getc_unlocked()做最後的 unlock
Back to the content
NTU PJ System Programming
2025 Fall
← Back to the content