---
title: 'Linux OS programming'
---
[TOC]
文件
===
可以通過這些函示進行 創建 打開 讀寫 光標:
```c
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
int creat(const char *pathname, mode_t mode);
ssize_t write(int fildes, const void *buf, size_t nbyte);
ssize_t read(int fd, void *buf, size_t count);
off_t lseek(int fd, off_t offset, int whence);
int close(int fd);
read/write 都會影響光標, 使用lseek來改變:
whence: SEEK_SET - head
SEEK_END - tail
SEEK_CUR - current position
```
>返回的數字fd都是文件描述符file descriptor, 而系統默認的有0, 1, 2 分別為:讀,寫,錯誤。
#include <unistd.h> 也有定義這3個宏(STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO)
開啟文件也可以用fopen, 搭配: fread, fwrite:
```c
FILE *fopen(const char *pathname, const char *mode);
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
int fclose(FILE *stream)
int fflush(FILE* stream)
```
## open與fopen的優缺點
open 主要用於內核態的設備文件調用,無緩存。
fopen 主要給用戶態讀寫文件,有緩存(大塊大塊的讀寫,少次數的切換內和態),需注意fclose或fflush確保文件的保存。
___
# 進程
程序是未被執行的executable(靜態), 進程是運行起來的executable(動態)
進程裡可以包含多個線程(Thread),好比工廠(process)裡面的工人(thread)。
## 進程狀態
就緒:獲取出CPU外的所有資源、只要處理器分配資源就可以馬上執行
運行:獲得處理器分配的資源,程序開始執行
阻塞:當程序條件不夠的時候,需要等待提交滿足的時候才能執行。
終止:進程結束,或是出現錯誤而被系統終止,進入終止狀態,無法再執行。
## 查看進程(process)
```bash
ps -aux // check all precesses
top // working manager
```
每個進程創建後都會有個名字 pid, 系統默認的有 調度進程(pid=0), 系統初始化,界面(pid=1)
## 程式存储空间分配

主要分為6個部分:
從高地址->低地址
| | | |
|-|-|-|
|高地址|命令行參數和環境變量 | argc, argv|
| |stack(棧or堆棧) |返回使用中(函數,局部變量)的地址,靜態內存分配|
| |heap (堆or堆積)| malloc 返回值,動態存儲分配|
| |未初始化『數據』bss(block started by symbol)|沒有 被賦值的變量|
| |初始化的『數據』|有 被賦值的變量|
|低地址 |正文 | 代碼段: (流程控制, 算法)|
## 子進程
```c
pid_t fork(void);
pid_t vfork(void);
```
On success, the PID of the child process is returned in the parent, and 0 is returned in the child.
On failure, -1 is returned in the parent, no child process is created, and errno is set appropriately.
||fork()|vfork|
|-|-|-|
|空間|與父進程不同空間;創新空間且copy-on-write pages|與父進程共享空間|
|順序|由系統調度決定|父進程 保證 子進程優先運行;直到子進程exit才還給父進程|
### 正常/異常退出
|正常退出|異常退出|
|-|-|-|
|main調用return|abort()放棄當前進程|
|exit()`標諄C庫`|當程式接收到類似關閉的訊號e.g Ctrl-c, kill -9|
|_exit() or _Exit(); `系統調用`|最後一個線程取消請求做出反應|
|進程中的最後一個線程調用thread_exit()||
### 獲取退出狀態
子進程使用`exit`, `_exit`, `_Exit` 回傳 `status`碼
||作用|
|-|-|
|_exit|最簡單的調用; 直接使程序停止, 清除其使用的記憶體空間 和 銷毀其在內核中的各種資料結構|
|exit|則是在_exit基礎上做了一些包裝, 如 `保存程序狀態於某個檔案`, `解開對某共享資料的鎖`|
> 當子進程退出,SIGCHLD會回傳給父進程,若不被收集的子進程 將變成 殭屍進程 Z+ , 造成`資源上的浪費`

父進程使用wait或waitpid來獲取返回值。
|wait|waitpid|
|-|-|
|阻塞直至某个子进程终止(沒有指定)
|waitpid则可以通过设置一个选项来设置为非阻塞,另外waitpid并不是等待第一个结束的进程而是等待参数中pid指定的进程|
wait 定義的macro請查看[man page](https://man7.org/linux/man-pages/man2/waitid.2.html)

:::spoiler wait的demo
```c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
void print_status(int status);
int main() {
int status;
pid_t pid;
// child process with "normal" exit
if((pid = fork()) == 0) { exit(7); }
wait(&status);
print_status(status);
// child process with "abort" exit: /* generated SIGABRT */
if((pid = fork()) == 0) { abort(); }
wait(&status);
print_status(status);
// child process: divide status with 0 generated /* SIGFPE */
if((pid = fork()) == 0) { status /= 0; }
wait(&status);
print_status(status);
return 0;
}
void print_status(int status)
{
if(WIFEXITED(status))
printf("normal termination, exit status: %d\n", WEXITSTATUS(status));
if(WIFSIGNALED(status))
printf("abnormal termination, signal number: %d%s\n", WTERMSIG(status),
#ifdef WCOREDUMP
WCOREDUMP(status) ? " (core file generated)" : "");
#else
"");
#endif
else if(WIFSTOPPED(status))
printf("child stopped, singal number = %d\n", WSTOPSIG(status));
}
```
:::
[补充: 处理SIGCHLD信号](https://blog.csdn.net/u012877472/article/details/50165083)
### 替換進程影像
在shell命令執行ls,實質上是調用fork()創建了一個子進程然後利用exec系統調用將新產生的子進程替換成ls進程。 並且新進程的PID與原來的進程PID相同。 [參考](https://www.cntofu.com/book/46/linux_system/linuxxi_tong_bian_cheng_zhi_jin_cheng_ff08_wu_ff09.md)
以下是exec系列的函數:
```c
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
```
> 主要兩種傳遞方式:
> l系列: 可變動參數, 以空指針結尾
> v系列: vecter 為參數。
> e系列: 將環境變亮傳遞給需要替換的進程
示範```ls -l```
* l系列:
```c
execl("/bin/ls","ls","-l",NULL); // absolute path
execlp("ls","ls","-l",NULL); // 找env中找第一個出現的ls, 不安全
```
* v系列:
```c
char *argv[] = {"ls","-l",NULL};
execv("/bin/ls",argv);
char *argv[] = {"ls","-l",NULL};
execvp("ls",argv);
```
* e系列:
execle.c
```c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
char * const envp[] = {"AA=11", "BB=22", NULL};
printf("Entering main ...\n");
int ret;
//ret =execl("./hello", "hello", NULL);
ret =execle("./hello", "hello", NULL, envp);
if(ret == -1)
perror("execl error");
printf("Exiting main ...\n");
return 0;
}
```
hello.c
```c
#include <unistd.h>
#include <stdio.h>
extern char** environ;
int main(void)
{
printf("hello pid=%d\n", getpid());
int i;
for (i=0; environ[i]!=NULL; ++i)
{
printf("%s\n", environ[i]);
}
return 0;
}
```
結果:

## System()
基本上是fork、execve、waitpid的再次封裝。
```c
int system(const char *command);
```
system()函數調用"/bin/sh -c command"執行特定的命令,阻塞當前進程直到command命令執行完畢
返回值:
如果無法啟動shell運行命令,system將返回127;出現不能執行system調用的其他錯誤時返回-1。如果system能夠順利執行,返回那個命令的退出碼。
system原碼:
```c=
int system(const char * cmdstring)
{
pid_t pid;
int status;
if(cmdstring == NULL){
return (1);
}
if((pid = fork())<0){
status = -1;
}
else if(pid == 0){
execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
_exit(127); //子進程正常執行則不會執行此語句
}
else{
while(waitpid(pid, &status, 0) < 0){
if(errno != EINTER){
status = -1;
break;
}
}
}
return status;
}
```
注意 最後有返回 "狀態碼" 可以做更進一步的檢查程式的運行狀態。
範例:
```c=
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define EXIT_ERR(m) \
do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}\
while (0);\
int main(void)
{
int status ;
status = system("ls -l|wc -l");
if(status == -1){
EXIT_ERR("system error");
}
else{
if(WIFEXITED(status))
{
if(WEXITSTATUS(status) == 0)
printf("run command successful\n");
else
printf("run command fail and exit code is %d\n",WEXITSTATUS(status));
}
else
printf("exit status = %d\n",WEXITSTATUS(status));
}
return 0;
}
```
結果:

## popen()
上面那些都是運行指令 但無法返回結果; 使用了popen函數就可以做到獲取運行結果了。 [man page](https://man7.org/linux/man-pages/man3/popen.3.html)
```c
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
```
範例:
```c=
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main() {
FILE *fd = popen("ls", "r");
char tmp[1024] = {'\0'};
// read the output to tmp array
fread(tmp, sizeof(tmp), 1, openfd);
printf("%s\n", tmp);
fclose(fd);
return 0;
}
```

# 信號
```man 7 signal```
系統中的信號有: ```kill -l```

處理方式:
* 忽略
* 大部份可被忽略,唯有SIGKILL,SIGSTOP不能
* 捕捉
* 某信好發生時,由內核來調用的至定義函數
* 默認
* 每個信號系統都對應了一個處理動作
* 結束
* kill -9 , kill -SIGKILL 都可以結束一個進程
## basic - 接收信號 調用函數
函數:
```c
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
```
```c=
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
void handler(int signum)
{
printf("signum: %d\n", signum);
}
int main(int argc, char **argv)
{
printf("pid: %d\n", getpid());
//signal(SIGINT, SIG_IGN); // ignore, ctl+c signal
signal(SIGINT, handler);
/*
// use system to send cmd to kill pid
int signum = atoi(argv[1]);
int pid = atoi(argv[2]);
char cmd[128] = {0};
sprintf(cmd, "kill -%d %d", signum, pid);
system(cmd);
*/
while(1);
return 0;
}
```
## 進階版
函數:
發送/接收: `sigqueue`, `sigaction`
```c
int sigqueue(pid_t pid, int sig, const union sigval值);
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
```
```c
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
```
注意:hand:若要使用 `void (*sa_sigaction)(int, siginfo_t *, void *);` 的話 `sa_flag` 要聲明: `SA_SIGINFO`
send
```c
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int signum = atoi(argv[1]);
pid_t pid = atoi(argv[2]);
union sigval value;
value.sival_int = 100;
sigqueue(pid, signum, value);
printf("Pid: %d Finish.\n", getpid());
sleep(10);
return 0;
}
```
recieve
```c
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
void handler(int sig, siginfo_t *info, void *ucontext)
{
if(ucontext != NULL)
{
printf("PID from: %d\n", info->si_pid);
printf("Signum: %d\n", sig);
printf("Data: %d\n", info->si_int); // one way to get data
printf("Data: %d\n", info->si_value.sival_int); // another way to get data
perror("WHY");
}
}
int main()
{
printf("pid %d\n", getpid());
struct sigaction act;
act.sa_sigaction = handler;
act.sa_flags = SA_SIGINFO;
sigaction(SIGUSR1, &act, NULL);
while(1);
return 0;
}
```
# 線程(執行序)
現成狀態:
就緒:指線程具備運行的所有條件,邏輯上可以運行,在等待處理機
運行:指線程占用處理機正在運行
阻塞:線程在等待一個事件,邏輯上不可執行
* 地址空間
* 要分配給進程(代碼段,樹鋸斷(初始化,為初始化),堆,棧,命令行參環境變量)
* 而現成共享和read on write
* 效率
* 線程間的切換上的時間遠遠小於進程間切換的時間
* 總體開銷約一個進程的30倍左右
* 通信
* 進程很不方便 - 耗費內存 CPU, 需要用(IPC - 管道,消息隊列,共享內存,信號,信號量)
* 解決數據共享問題
* 條件變量
* 互斥鎖
## 創建/刪除/回傳參數
```c
#include <pthread.h>
int pthread_create(pthread_t *restrict thread,
const pthread_attr_t *restrict attr,
void *(*start_routine)(void *),
void *restrict arg);
int pthread_join(pthread_t thread, void **retval);
void pthread_exit(void* retval);
```
範例: 創建/刪除/戴回傳參數
```c
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *func1(void *param)
{
printf("Number: %d\n", *((int *)param));
/* // pase int
static int ret = 666;
pthread_exit((void*)&ret);
*/
static char *ret = "hello linux";
pthread_exit((void *) ret);
}
int main()
{
pthread_t t1;
int param = 100;
pthread_create((pthread_t *)&t1, NULL, func1, ¶m);
/* //recieve int
int *ret;
pthread_join(t1, (void **) &ret);
printf("%d\n", ret);
*/
// recieve char*
char *ret;
pthread_join(t1, (void **) &ret);
printf("%s\n", ret);
return 0;
}
```
> 使用pthread_exit回傳參數為`整數`和`字符串`的差別是: 字串不需要再多加上地址(本身就是個指針)。
## 同步問題
下面示範了: 創建兩個thread訪問addOne()資源進行各1次萬次的調用,但執行的結果和我們期望的不同,counter未滿2千萬。 為甚麼呢?因為`同步問題`
> 這個問題只有多和處理器會發生呦
```c=
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
int counter;
void* addOne() {
for(int i=0; i<10000000; i++)
counter++;
}
int main()
{
if(_POSIX_THREADS)
perror("support posix_thread");
pthread_t t1;
pthread_t t2;
pthread_create(&t1, NULL, &addOne, NULL);
pthread_create(&t2, NULL, &addOne, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
printf("total: %d\n", total);
return 0;
}
```

## 互斥鎖 (mutex lock)
我們這時就需要有鎖的機制了。就好比說共享資源是一間廁所,你不會想要在你使用的時候歡迎別人一起共用吧:smile: 那就使用鎖的機制 用完再打開下一個人就可以使用了。
count++ 在cpu指令裡: 基本上可看成1.讀取 2.加+1 3.寫入 只要有打亂的步驟就會得到錯誤的結果喔
```c
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
```
下面是剛剛的範例下上鎖的版本:
```c=
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
int counter;
pthread_mutex_t mutex;
void* addOne() {
pthread_mutex_lock(&mutex);
for(int i=0; i<10000000; i++)
counter++;
pthread_mutex_unlock(&mutex);
}
int main()
{
if(_POSIX_THREADS)
perror("support posix_thread");
pthread_mutex_init(&mutex, NULL);
pthread_t t1;
pthread_t t2;
pthread_create(&t1, NULL, &addOne, NULL);
pthread_create(&t2, NULL, &addOne, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
printf("total: %d\n", total);
pthread_mutex_destroy(&mutex);
return 0;
}
```
得到了我們所希望的結果了:smile:

## 死鎖(dead lock)
死鎖: 請求的資源未得到釋放而沒有進展的等待。
一把鎖的情況: 在同一個執行續中 使用一把鎖, 鎖兩次。 (在第一次請求資源 得到資源後未釋放, 又接著再次請求 但此時是沒有資源可取得 所以一直等待 而產生死鎖)
>* 解決同一把鎖多次的使用 可以配置 mutexAttributex的state: recurssive來解決,但上鎖的次數必須對應解鎖的次數、如果享用不對稱的鎖就要使用到semaphore了:smile:
兩把鎖的情況: 兩個執行續 各有: 鎖A和B, 1.各自鎖上自己的鎖之後 2.又想要鎖上對方的鎖。 (各自都需要兩種資源來完成他們的任務所以就互相的卡死對方。
>預防死結:
>* Mutual exclusion:對不可共用的資源類型而言,互斥一定成立,而可共用的資源類型,因為可以同時讀取相同檔案,所以一定不會產生。
>* Hold and Wait:process必須保證一個行程在要求一項資源時,不可以佔用任何其它的資源。
>* No preemption:只要某個處理元要不到所要求的資源時,便把它已經擁有的資源釋放,然後再重新要求所要資源。
>* Circular Wait:確保循環式等候的條件不成立,我們對所有的資源型式強迫安排一個線性的順序。
>* [資料來源](https://sls.weco.net/node/21327)
以下是deadlock的小範例:
```c=
#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
void* func1() {
pthread_mutex_lock(&mutex1);
printf("fun1 lock mutex1\n");
sleep(1);
pthread_mutex_lock(&mutex2);
printf("fun2 lock mutex1\n");
printf("text from func1\n");
pthread_mutex_unlock(&mutex2);
pthread_mutex_unlock(&mutex1);
}
void* func2() {
pthread_mutex_lock(&mutex2);
printf("fun2 lock mutex2\n");
sleep(1);
pthread_mutex_lock(&mutex1);
printf("fun1 lock mutex1\n");
printf("text from func2\n");
pthread_mutex_unlock(&mutex1);
pthread_mutex_unlock(&mutex2);
}
int main() {
pthread_t t1,t2;
pthread_create(&t1, NULL, func1, NULL);
pthread_create(&t1, NULL, func2, NULL);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
pthread_mutex_destroy(&mutex1);
pthread_mutex_destroy(&mutex2);
return 0;
}
```

## 資源同步 條件控制
我們可以使用的函數:
```c
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
```
範例: 模擬媽媽們去買(搶)衣服
:::spoiler code
```c=
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int MAX_MOM = 6;
int t_shirt_per_day = 6;
int t_shirt;
int isWorking;
void* shopping(void *param) {
pthread_mutex_lock(&mutex);
while(t_shirt < 1 && isWorking) {
printf("\t\t\t\tNo.%d is waiting...\n", *(int*)param);
pthread_cond_wait(&cond, &mutex);
}
if(t_shirt > 0) {
t_shirt--;
printf("\t\t\t\tNo.%d bought one, %d left\n", *(int*)param, t_shirt);
usleep(100000);
} else {
printf("\t\t\t\tNo. %d missed.\n", *(int*)param);
}
pthread_mutex_unlock(&mutex);
}
void makeT_shirt() {
isWorking = 1;
for(int i=0; i < t_shirt_per_day; i++) {
pthread_mutex_lock(&mutex);
t_shirt += 2;
printf("Made %d new T-shirt. total: %d\n", i, t_shirt);
if(i == t_shirt_per_day-1)
isWorking = 0;
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&cond);
//pthread_cond_broadcast(&cond);
usleep(300000);
}
printf("worker: job off\n");
}
int main()
{
pthread_t worker;
pthread_t moms[MAX_MOM];
pthread_create(&worker, NULL, (void*) makeT_shirt, NULL);
for(int i=0; i<MAX_MOM; i++) {
int *pi = malloc(sizeof(int));
*pi = i;
pthread_create(&moms[i], NULL, &shopping, pi);
}
for(int i=0; i<MAX_MOM; i++) {
pthread_join(moms[i], NULL);
}
pthread_join(worker,NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}
```
:::
> 我們這裡使用的是`pthread_cond_singal`方式隨機喚起一個使用`pthread_cond_wait`在等待的媽媽,所以worker做了1件衣服就會銷售掉,這個情況就很完美。 但如果工人一次製造2件衣服的話呢? 如果依然使用cond_signal還就只會呼叫1個媽媽來買然後剩下一件,顯然呼叫所有的媽媽來搶購才是我們想要的,這樣就可以一次賣出現有的庫存。 這時我們就可以使用`pthread_cond_broadcast`來代替`pthread_cond_signal`來呼叫所有的媽媽來搶購了。 這樣就會很公平是不是 醬子就很棒 哈哈 :satisfied:

## 線程同步(代碼)
我們透過了barrier來保證 在運行某行指令時有足夠多的threads, 實現代碼上的同步。
下面的例子 創建了rolldice和calculated的barrier, 各自等待9個threads(8個threads, 加上main 總共就是9個), 來保證8個threads的statusVal值生成 後運行calculate status, 和 保證8個threads的status計算完畢後運行result的列印。

使用的函數:
```c
#include <pthread.h>
int pthread_barrier_destroy(pthread_barrier_t *barrier);
int pthread_barrier_init(pthread_barrier_t *restrict barrier,
const pthread_barrierattr_t *restrict attr, unsigned count);
```
:::spoiler
```c=
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
int NUM_THREADS = 8;
int status[8] = {0};
int statusVal[8] = {0};
pthread_barrier_t barrierRollDice;
pthread_barrier_t barrierCalculated;
void* rolldices(void *arg) {
int index = *(int*)arg;
while(1) {
// roll dices
for(int i=0; i<NUM_THREADS; i++) {
statusVal[i] = rand() %6 + 1;
}
pthread_barrier_wait(&barrierRollDice);
pthread_barrier_wait(&barrierCalculated); // guaranteed status are calculated
if(status[index] == 1)
printf("%d rolled %d, won\n", index, statusVal[index]);
else
printf("%d rolled %d, lost\n", index, statusVal[index]);
}
}
int main() {
srand(time(NULL));
pthread_t threads[NUM_THREADS];
pthread_barrier_init(&barrierRollDice, NULL, NUM_THREADS+1);
pthread_barrier_init(&barrierCalculated, NULL, NUM_THREADS+1);
// create threads
for(int i=0; i < NUM_THREADS; i++) {
int *index = malloc(sizeof(int));
*index = i;
pthread_create(&threads[i], NULL, rolldices, index);
}
while(1) {
pthread_barrier_wait(&barrierRollDice); // guaranteed dice rolled are calculated
printf("\nNEW ROUND:\n");
// calculate
int max;
for(int i=0; i<NUM_THREADS; i++) {
if(statusVal[i] > max)
max = statusVal[i];
}
for(int i=0; i<NUM_THREADS; i++) {
if(statusVal[i] == max)
status[i] = 1;
else
status[i] = 0;
}
sleep(3);
pthread_barrier_wait(&barrierCalculated); // guaranteed status are calculated
}
// clean up
for(int i=0; i < NUM_THREADS; i++) {
pthread_join(threads[i], NULL);
}
pthread_barrier_destroy(&barrierRollDice);
pthread_barrier_destroy(&barrierCalculated);
return 0;
}
```
:::

## detach
以前都是`pthread_join`等thread運行完釋放資源,現在又學新的一招讓他自己完成後釋放資源, 用`pthread_detch`就搞定了。
```c=
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
void* f() {
sleep(3);
printf("hello linux\n");
}
int main(){
pthread_t thread;
pthread_create(&thread, NULL, f, NULL);
pthread_detach(thread);
pthread_exit(0);
//return 0;
}
```
但實質上`pthread_detch`是把一個不是non-detch的thread狀態改為detach的狀態、所以在創建thread和更改狀態上有個空隙 是有可能導致狀態沒改上 thread就結束的狀況(資源就得不到釋放)。 因此是時候讓不知道有甚麼用的attribute登場了:satisfied:
```c=
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
void f() {
sleep(3);
printf("hello linux\n");
}
int main(){
pthread_t thread;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&thread, &attr, (void*)f, NULL);
pthread_detach(thread);
pthread_exit(0);
//return 0;
}
```
## factory and consumer (with semaphore)

[picture source](https://www.ktustudents.in/2017/10/program-for-producer-consumer-problem-in-c-cs331-lab.html)
一、V operation:V()會將semaphore的值加1,signal函數或是sem_post()。
二、P operation:P()會將semaphore的值減1,wait函數或是sem_wait()。
[Semaphore原理與操作說明](https://www.syscom.com.tw/ePaper_Content_EPArticledetail.aspx?id=213&EPID=176&j=5&HeaderName=NonStop%E5%B0%88%E6%AC%84)
我們使用2個threads在buffer[10]的數組做讀寫的動作, 並且也對臨界資源做了保護, 此時會看到 執行續雖然跑得很快 搶到了進入了讀寫的機會 但有很多時候都是無效的讀寫 因為裡面沒有數據, 這些都是cpu的浪費。
因此就可以使用semaphore來做資源量的管理、可以看到我們創建了2個semaphore分別是semEmpty, semFull。 我們對semEmpty 做10的初始化表示10個空箱子可以讓producer來裝箱、和semFull初始化0表示0個為填滿的箱子。 在producer做裝箱前使用sem_wait(&empty)來對empty的信號量-1,表示取了一個空箱子裝箱, 裝完箱子後使用sem_post(&semFull)對full的信號量+1,表示放入一個裝滿貨物的箱子, 反之亦然 在consumer的作法也就是反過來做囉。 可以看到輸出的結果一點資源都沒有浪費, 我們就可以和自己說 醬就好棒棒呀 :smile_cat: :+1:
:::spoiler 第一種
```c=
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
int THREAD_TOTAL = 2;
int storage[10];
int count = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void producer(void *args) {
while(1) {
pthread_mutex_lock(&mutex);
if(count < 10) {
storage[count] = rand() % 100;
printf("count: %d, val: %d\n", count, storage[count]);
count++;
} else {
printf("produce skipped\n");
}
pthread_mutex_unlock(&mutex);
}
}
void comsumer(void *args) {
int val;
while(1) {
pthread_mutex_lock(&mutex);
if(count > 0) {
val = storage[count-1];
count--;
} else {
printf("\t\t\t\t\tcomsum failed\n");
}
printf("\t\t\t\t\tcount: %d, got val: %d\n", count, val);
pthread_mutex_unlock(&mutex);
}
}
int main() {
srand(time(NULL));
pthread_t thread[THREAD_TOTAL];
for(int i = 0; i < THREAD_TOTAL; i++) {
if(i % 2 == 0)
pthread_create(&thread[i], NULL, (void*) producer, NULL);
else
pthread_create(&thread[i], NULL, (void*) comsumer, NULL);
}
for(int i = 0; i < THREAD_TOTAL; i++) {
pthread_join(thread[i], NULL);
}
pthread_mutex_destroy(&mutex);
return 0;
}
```
:::

:::spoiler better version...
```c=
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
int THREAD_TOTAL = 2;
int storage[10];
int count = 0;
sem_t semEmpty, semFull;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void producer(void *args) {
while(1) {
sem_wait(&semEmpty);
pthread_mutex_lock(&mutex);
if(count < 10) {
storage[count] = rand() % 100;
printf("count: %d, val: %d\n", count, storage[count]);
count++;
} else {
printf("produce skipped\n");
}
pthread_mutex_unlock(&mutex);
sem_post(&semFull);
}
}
void comsumer(void *args) {
int val;
while(1) {
sem_wait(&semFull);
pthread_mutex_lock(&mutex);
if(count > 0) {
val = storage[count-1];
count--;
} else {
printf("\t\t\t\t\tcomsum failed\n");
}
printf("\t\t\t\t\tcount: %d, got val: %d\n", count, val);
pthread_mutex_unlock(&mutex);
sem_post(&semEmpty);
}
}
int main() {
srand(time(NULL));
sem_init(&semEmpty, 0, 10);
sem_init(&semFull, 0, 0);
pthread_t thread[THREAD_TOTAL];
for(int i = 0; i < THREAD_TOTAL; i++) {
if(i % 2 == 0)
pthread_create(&thread[i], NULL, (void*) producer, NULL);
else
pthread_create(&thread[i], NULL, (void*) comsumer, NULL);
}
for(int i = 0; i < THREAD_TOTAL; i++) {
pthread_join(thread[i], NULL);
}
sem_destroy(&semEmpty);
sem_destroy(&semFull);
pthread_mutex_destroy(&mutex);
return 0;
}
```
:::

## threadpool
```c=
#include <stdlib.h>
#include <pthread.h>
#include <stdio.h>
typedef struct Task {
int a,b;
}Task;
pthread_mutex_t mutexQueue = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t condQueue = PTHREAD_COND_INITIALIZER;
int MAX_THREADS = 1;
int taskcount = 0;
Task taskqueue[256];
void executeTask(Task *task) {
int sum = task->a + task->b;
printf("result: %d\n", sum);
}
void submitTask(Task task) {
pthread_mutex_lock(&mutexQueue);
taskqueue[taskcount] = task;
taskcount++;
pthread_mutex_unlock(&mutexQueue);
pthread_cond_signal(&condQueue);
}
void startThread() {
while(1) {
pthread_mutex_lock(&mutexQueue);
while(taskcount < 0)
pthread_cond_wait(&condQueue, &mutexQueue);
Task task = taskqueue[0];
taskcount--;
for(int i=0; i<taskcount; i++) {
taskqueue[i] = taskqueue[i+1];
}
pthread_mutex_unlock(&mutexQueue);
executeTask(&task);
}
}
int main() {
pthread_t threadpool[MAX_THREADS];
int i;
for(i=0; i<MAX_THREADS; i++) {
pthread_create(&threadpool[i], NULL, (void*)startThread, NULL);
}
srand(time(NULL));
for(i=0; i<100; i++) {
Task task = {
.a = rand() % 100,
.b = rand() % 100
};
submitTask(task);
}
for(i=0; i<MAX_THREADS; i++) {
pthread_join(threadpool[i], NULL);
}
pthread_mutex_destroy(&mutexQueue);
pthread_cond_destroy(&condQueue);
return 0;
}
```
## threadpool with taskptr
```c=
#include <stdlib.h>
#include <pthread.h>
#include <stdio.h>
typedef struct Task {
void (*ptr)();
int a,b;
}Task;
pthread_mutex_t mutexQueue = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t condQueue = PTHREAD_COND_INITIALIZER;
int MAX_THREADS = 1;
int taskcount = 0;
Task taskqueue[256];
void sum(int a, int b) {
int total = a + b;
printf("sum of %d and %d is %d\n", a, b, total);
}
void mult(int a, int b) {
int total = a * b;
printf("multi of %d and %d is %d\n", a, b, total);
}
void executeTask(Task *task) {
task->ptr(task->a, task->b);
// int sum = task->a + task->b;
// printf("result: %d\n", sum);
}
void submitTask(Task task) {
pthread_mutex_lock(&mutexQueue);
taskqueue[taskcount] = task;
taskcount++;
pthread_mutex_unlock(&mutexQueue);
pthread_cond_signal(&condQueue);
}
void startThread() {
while(1) {
pthread_mutex_lock(&mutexQueue);
while(taskcount < 0)
pthread_cond_wait(&condQueue, &mutexQueue);
Task task = taskqueue[0];
taskcount--;
for(int i=0; i<taskcount; i++) {
taskqueue[i] = taskqueue[i+1];
}
pthread_mutex_unlock(&mutexQueue);
executeTask(&task);
}
}
int main() {
pthread_t threadpool[MAX_THREADS];
int i;
for(i=0; i<MAX_THREADS; i++) {
pthread_create(&threadpool[i], NULL, (void*)startThread, NULL);
}
srand(time(NULL));
for(i=0; i<100; i++) {
Task task = {
task.ptr = (i%2==0)? sum:mult,
.a = rand() % 100,
.b = rand() % 100
};
submitTask(task);
}
for(i=0; i<MAX_THREADS; i++) {
pthread_join(threadpool[i], NULL);
}
pthread_mutex_destroy(&mutexQueue);
pthread_cond_destroy(&condQueue);
return 0;
}
```
## 而外補充:
主題分享: 淺談 Semaphore 與 Mutex
* `mutex 是給行程獨立的鎖 而semephore是給多少量的行程可通行`
{%youtube JEXwO_EoyZo%}
:::info
[更多用法可以參考Beej的網站](https://beej.us/guide/bgc/html/#file-inputoutput)
[CodeVault](https://www.youtube.com/playlist?list=PLfqABt5AS4FmuQf70psXrsMLEDQXNkLq2)
:::
###### tags: `Linux`