一、Linux系統呼叫主要函式
一、Linux系統呼叫主要函式
首先,認識一下Linux下系統呼叫的主要函式,為後面程式與通訊等做好鋪墊。
以下是 Linux 系統呼叫的一個列表,包含了大部分常用系統呼叫和由系統呼叫派生出的函式。
fork | 建立一個新程式 |
clone | 按指定條件建立子程式 |
execve | 執行可執行檔案 |
exit | 中止程式 |
_exit | 立即中止當前程式 |
sleep(n) | 睡眠(等待/阻塞),n 為秒的單位 |
getpid | 獲取程式標識號 |
getppid | 獲取父程式標識號 |
pause | 掛起程式,等待訊號 |
wait(引數) | 等待子程式終止 |
waitpid | 等待指定子程式終止 |
kill | 向程式或程式組發訊號 |
pipe | 建立管道 |
二、建立程式
接下來這部分相當於程式設計,通過系統呼叫建立程式,然後根據執行順序進行判斷,理解主程式和子程式的關係。
1、建立子程式系統呼叫fork()
#include <unistd.h> #include <sys/types.h> #include<stdio.h> int main () { pid_t pid; /* pid_t 是 short 型別 */ pid=fork(); if (pid < 0) printf("error in fork!"); else if (pid == 0) printf("i am the child process, my process id is %d\n",getpid()); else printf("i am the parent process, my process id is %d\n",getpid()); return 0; }
這個程式很好理解:
fork()返回值0,進入子程式,返回值1,進入父程式,-1則是建立失敗。
主要理解父子程式執行順序是否與程式結構(判斷語句前後)有關,所以再次進行以下測試,修改了判斷語句順序:
#include <unistd.h> #include <sys/types.h> #include<stdio.h> int main () { pid_t pid; /* pid_t 是 short 型別 */ pid=fork(); if (pid < 0) printf("error in fork!"); else if (pid > 0) printf("i am the parent process, my process id is %d\n",getpid()); else printf("i am the child process, my process id is %d\n",getpid()); return 0; }
根據實驗結果如圖,對比之後發現執行結果相同,說明父子程式執行順序與判斷語句次序無關,始終是父程式優先執行。
2、驗證fork()建立子程式效果
首先,我們再次對以上程式進行修改,第一次,測試列印語句放於fork()之前,觀察結果:
#include <unistd.h> #include <sys/types.h> #include<stdio.h> int main () { pid_t pid; /* pid_t 是 short 型別 */ printf("see the print times\n"); pid=fork(); if (pid < 0) printf("error in fork!"); else if (pid > 0) printf("i am the parent process, my process id is %d\n",getpid()); else printf("i am the child process, my process id is %d\n",getpid()); return 0; }
第二次,將列印測試放在fork()之後,結果:
由此可見得,在fork()建立子程式之後,有兩個程式進行執行操作,所以執行了兩次測試列印。
3、系統呼叫fork()與掛起系統呼叫wait()
實現程式碼如下:
#include <unistd.h> #include <sys/types.h> #include<sys/wait.h> #include<stdio.h> #include<stdlib.h> int i, st; pid_t pid1, pid2; int sub_proc1(int id); int sub_proc2(int id); int main() { printf("\n\n\n"); if ((pid1 = fork()) == 0) { printf("The first child process id is %d\n", getpid()); sub_proc1(1);} else printf("i am the parent process id=%d \n",getpid()); if ((pid2 = fork()) == 0) { printf("The second child process id is %d\n", getpid()); sub_proc2(2);} else printf("i am the parent process id=%d \n",getpid()); i = wait(&st); i = wait(&st); printf("\n all finished!!!!!!!!!!!!!!!!!!\n"); exit(0); return 0; } int sub_proc1(int id) { printf("sub_proc%d is running !\n",id); printf("sub_proc%d finish \n",id); exit(id); } int sub_proc2(int id) { printf("sub_proc%d is running !\n",id); printf("sub_proc%d finish \n",id); exit(id); }
解析:初始化建立了兩個主程式,系統呼叫wait()更直觀看出,程式先執行第一個主程式,再執行其子程式;然後執行第二個主程式,再執行其子程式。按照這個執行順序,感知父子程式直接的關係,以及兩個程式之間的執行順序。證明結果:
三、模擬程式管道通訊
我們知道,管道是程式之間的通訊方法之一, 接下來通過程式的建立、管道的設定,實現程式間通過管道進行通訊,然後認識在 Linux下程式間通過管道通訊的程式設計。
原理:pipe() 把 p[ ] 與兩個檔案描述符緊密聯絡起來:
p[0] 用於從管道讀
p[1]用於向管道寫
使用到系統呼叫函式pipe() 和 fork()
#include <stdio.h> #include<stdlib.h> #include <sys/types.h> #include <unistd.h> #include <sys/wait.h> #define MSGSIZE 16 int pipe(int p[2]); char *msg1="hello, world #1"; char *msg2="hello, world #2"; char *msg3="hello, world #3"; int main() { char inbuf[MSGSIZE]; int p[2], i, pid; if (pipe(p) < 0) /* 開啟管道 */ { perror("pipe error!"); /* 無法開啟一個管道錯*/ exit(1); } if ( (pid = fork()) < 0) { perror("fork"); exit(2); } if (pid > 0) /* 父程式 */ { close(p[0]); /* 關閉讀端(連線) */ write(p[1], msg1, MSGSIZE); write(p[1], msg2, MSGSIZE); write(p[1], msg3, MSGSIZE); wait((int *)0); } if (pid == 0) /* 子程式 */ { close(p[1]); /* 關閉寫端 */ for (i=0; i < 3; i++) { read(p[0], inbuf, MSGSIZE); printf("%s\n", inbuf); } } return 0; }
結果實現不同進行之間的通訊,通過不同管道進行讀寫操作:
程式管道通訊需要注意下面幾個問題:
(1)關閉了一個方向
(2)管道的大小(Size)
每個管道都有一個大小限制,通常大約為 5k (5120) 個位元組
(3)寫問題:
write() 將被掛起(阻塞),如果管道中無法存放正文(容量不夠),可能在寫的過程中發生阻塞;
如果向一個沒有讀連線的管道寫,將返回 -1, 且 errno 被置成 EPIPE。errno 是一個全域性變數。
(4)讀問題:
read() 將被阻塞,如果管道空,且有一個 寫連線開放,read() 將返回 0 。
如果為空,且沒有任何寫連線,就此, 讀程式可以知道通訊結束。
四、pipe()下生產者與消費者問題
經典同步互斥問題,現在初始化有一個生產者,兩個消費者,只有生產者有生產物資後,消費者才能使用,否則進入等待。
#include<sys/types.h> #include<sys/file.h> #include<string.h> #include<sys/wait.h> #include<unistd.h> #include<stdlib.h> #include <stdio.h> char r_buf[4]; //讀緩衝 char w_buf[4]; //寫緩衝 int pipe_fd[2]; pid_t pid1, pid2; int producer(int id); int consumer(int id); int i,pid,status; /* status 用來表示程式退出的狀態 */ int main() { if(pipe(pipe_fd)<0) { printf("pipe create error \n"); exit(-1); } else { printf("pipe is created successfully!\n"); if((pid1=fork())==0) producer(1); if((pid2=fork())==0) consumer(2); } close(pipe_fd[0]);//否則會有讀者或者寫者永遠等待 close(pipe_fd[1]); for(i=0;i<2;i++) pid=wait(&status); /* 等待子程式完成(取返回狀態資訊),主程式後 面未利用子程式的什麼結果,直接在子程式完成後退出 */ exit(0); } int producer(int id) { int i; printf("producer %d is running!\n",id); close(pipe_fd[0]); for(i=1;i<10;i++) { sleep(3); strcpy(w_buf,"aaa\0"); if(write(pipe_fd[1],w_buf,4)==-1) printf("write to pipe error\n"); } close(pipe_fd[1]); printf("producer %d is over!\n",id); exit(id); } int consumer(int id) { close(pipe_fd[1]); printf("consumer %d is running!\n",id); strcpy(w_buf,"bbb\0"); while(1) { sleep(1); strcpy(r_buf,"eee\0"); if(read(pipe_fd[0],r_buf,4)==0) break; printf("consumer %d get %s, while the w_buf is %s\n",id,r_buf,w_buf); } close(pipe_fd[0]); printf("consumer %d is over!\n", id); exit(id); }
程式執行如下:
總結
學習之後的一篇總結,玩轉Linux程式設計,更加深刻地去體驗其中的原理,掌握基本的知識和操作。這篇記錄總結學習,可以認識到程式與程式的區別,還有程式間通訊機制,程式的執行順序和關係。Linux系統呼叫函式的認識和使用更進一步掌握相關Linux知識。
另外,程式間的通訊機制還有其他方法,程式訊號中斷通訊內容,互斥同步問題,銀行家演算法等期待後續的學習總結記錄,學習分享記錄每一步,希望也能夠幫助到有需要的朋友,大佬也多多指正!
我的CSDN部落格:https://blog.csdn.net/Charzous/article/details/108287075
我的部落格園:https://www.cnblogs.com/chenzhenhong/p/13580356.html
版權宣告:本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連結和本宣告。
本文連結:https://blog.csdn.net/Charzous/article/details/108287075