在Linux,如果程式碼在背景執行(使用&,加在執行檔後面),就沒辦法使用ctrl+C,需要使用kill
使用ps
指令 ,可以查看執行程式的PID,只要kill PIDnum
,就可以把背景執行的程式刪除
gcc georgeMary.c -lpthread
: 跑應用程式,使用pthread動態函式庫
使用公用變數就會發生這種問題
race.c
#include <stdio.h>
#include <pthread.h>
#define LOOPS 100000000
int counter = 0;
void *inc()
{
for (int i=0; i<LOOPS; i++) {
counter = counter + 1;
}
return NULL;
}
void *dec()
{
for (int i=0; i<LOOPS; i++) {
counter = counter - 1;
}
return NULL;
}
int main()
{
pthread_t thread1, thread2;
pthread_create(&thread1, NULL, inc, NULL);
pthread_create(&thread2, NULL, dec, NULL);
pthread_join( thread1, NULL);
pthread_join( thread2, NULL);
printf("counter=%d\n", counter); // 出來的結果不會是0,會在1億和-1億之間
// 因為組合語言的關係,在程序互相交錯時,裡面的值就會錯亂
/*
R1 = C
R1 = R1 + 1
C = R1
*/
/*
R1 = C
R1 = R1 -1
C = R1
*/
}
需要使用
norace.c
#include <stdio.h>
#include <pthread.h>
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
#define LOOPS 100000
int counter = 0;
void *inc()
{
for (int i=0; i<LOOPS; i++) {
pthread_mutex_lock( &mutex1 ); // 採用互斥鎖的鎖定方式
counter = counter + 1;
pthread_mutex_unlock( &mutex1 );
}
return NULL;
}
void *dec()
{
for (int i=0; i<LOOPS; i++) {
pthread_mutex_lock( &mutex1 );
counter = counter - 1;
pthread_mutex_unlock( &mutex1 );
}
return NULL;
}
int main()
{
pthread_t thread1, thread2;
pthread_create(&thread1, NULL, inc, NULL);
pthread_create(&thread2, NULL, dec, NULL);
pthread_join( thread1, NULL);
pthread_join( thread2, NULL);
printf("counter=%d\n", counter); // 執行幾次都是 0
}
也稱有限緩衝問題(Bounded-buffer problem),如果解決方法不夠完善,則容易出現死結的情況
- 生產者的主要作用是生成一定量的數據放到緩衝區中,然後重複此過程。與此同時,消費者也在緩衝區消耗這些數據。該問題的關鍵就是要保證生產者不會在緩衝區滿時加入數據,消費者也不會在緩衝區中空時消耗數據。
需要哲學家同時拿起叉子才會發生問題(死結),這個代表很少發生的Bug,所以在偵測上也會比較麻煩。
- 做以下兩件事情之一:吃飯,或者思考。吃東西的時候,他們就停止思考,思考的時候也停止吃東西。餐桌上有五碗義大利麵,每位哲學家之間各有一支餐叉。因為用一支餐叉很難吃到義大利麵,所以假設哲學家必須用兩支餐叉吃東西。
semaphore
根據S,判斷可不可以進入程式(S>0),執行P(wait())會減少值(S),執行V(signal())會增加值(S)
thread
有點二進為的感覺,1可以進去,0不可以進去
fork函數可以讓一個應用程式產生很多行程(process),他有傳回值,假如return == 0 代表是新產生的process
- 可移植作業系統介面(Portable Operating System Interface of UNIX,縮寫為 POSIX ),是一種標準
行程,代表一個程式可以運行多次,執行一次多一個行程(有的也會出現多個行程),行程大多運行在背景,使用
ps
可以查看process
fork2.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h> // 在linux環境下才可以引入
int main() {
printf("%-5d : enter\n", getpid()); // getpid() 可以查看thread進行狀況
/* 還沒有fork(),只有一個process
4100 : enter
*/
fork(); // 使用unistd.h,一個行程分叉成父子兩個行程
printf("%-5d : after 1st fork\n", getpid());
/* fork()一次,兩個個process
4100 : after 1st fork
4101 : after 1st fork
*/
fork(); // 兩個行程又分別分叉出兩對父子,所以變成四個行程。
printf("%-5d : Hello world!\n", getpid());
/* fork()兩次,四個process
4100 : Hello world!
4102 : Hello world!
4101 : Hello world!
4103 : Hello world!
*/
}
現在作業系統,一開始會執行init,後面會使用fork分出一個shell,讓我們可以使用指令控制電腦做其他事情。有多工,在電腦運行應用程式當機的時候,只要切斷fork的分支就可以,不用重新開機
process的切換
有exec這種字的,都會切換到其他的執行碼,下面的內容就不會執行了
execvp1.c
#include <stdio.h>
#include <unistd.h>
int main() {
char *arg[] = {"ls", "-l", NULL };
printf("execvp():before\n");
execvp(arg[0], arg); // 使用unistd.h,可以把這個 process 換成上面的指令 ls
printf("execvp():after\n"); // 這行不會執行
}
mysystem1.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int mysystem(char *arg[]) {
if (fork()==0) { // let child process exec ls -l, parent keep going
execvp(arg[0], arg); // child : exec("ls -l")
}
int status;
wait(&status); // wait通常搭配fork來用,讓fork執行完才retrun
return status;
}
int main() {
char *arg[] = {"ls", "-l", NULL };
mysystem(arg);
printf("main end!\n");
}
system1.c
#include <stdio.h>
#include <stdlib.h>
int main() {
system("ls -l"); // c的原生函數,可以呼叫指令,不會結束到原生函數
printf("main end!\n");
}
父還沒結束,子就不會結束,記憶體會被占用。如果可以遠端讓別人的電腦使用fork指令,就會讓電腦爆炸(殭屍炸彈)
zombie.c
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main () {
pid_t child_pid;
/* Create a child process. */
child_pid = fork (); // 創造進程讓電腦產生垃圾
if (child_pid > 0) {
/* This is the parent process. Sleep for a minute. */
sleep (60);
} else {
/* This is the child process. Exit immediately. */
exit (0); // child pocess become zombie
}
return 0;
}
使用fork()和execvp()可以做出shell、bash....