进程基本概念和练习
進程/線程
1.?? ?linux多任務編程
?? ?(1)?? ?指的是用戶可以同一時間運行多個應用程序(任務)。
?? ??? ?我們的window和linux操作系統都是同時運行多個任務的。
?? ??? ??? ?windows----->任務管理器
?? ??? ??? ?linux ?----->ps -ef
?? ?
?? ??? ?int main()
?? ??? ?{
?? ??? ??? ?printf("Hello,Process!\n");
?? ??? ??? ?while(1)
?? ??? ??? ??? ?;
?? ??? ?}
?? ??? ?gcc main.c -o main
?? ??? ?main---->可執行程序 ?死的
?? ??? ?./main ---->進程?? ? 活的
?? ??? ?
?? ??? ?程序到底是什么?
?? ??? ?sum.c
?? ??? ?int main()
?? ??? ?{
?? ??? ??? ?int a = 5,b = 6;
?? ??? ??? ?int sum = a + b;
?? ??? ??? ?printf("sum = %d\n",sum);
?? ??? ?}
?? ??? ?程序 = 算法 + 數據結構
?? ??? ?程序 = 指令 + 數據
?? ??? ?用來表示人們思維對象的抽象概念的物理表現叫做數據,比如:a b sum
?? ??? ?我們把數據處理的規則叫做操作(指令),如:+
?? ??? ?對某一有限數據集合所施行的、目的在于解決某一問題的一組有限的指令的集合,
?? ??? ?稱之為一個計算(compute).
?? ??? ?計算機就是用指令來處理數據。所以我們可以稱:
?? ??? ??? ?程序是存放在磁盤上的可執行文件,是數據和指令的集合,一個程序的執行過程就是一個計算。
?? ??? ??? ?
?? ?(2)?? ?一個任務可以包含一個或者多個獨立功能的子任務
?? ??? ?這些獨立功能的子任務稱之為進程或線程,比如殺毒軟件....
?? ??? ?任務和進程、線程之間的關系:
?? ??? ??? ?
?? ??? ?現代操作系統為了提高CPU的利用率,特定的引入了并發的概念。
?? ??? ?
2.?? ?進程
?? ?進程是具有獨立功能的某個數據集合上的一次動態的執行過程,簡單的說,進程就是一個程序的
?? ?一次執行過程,程序一旦執行,就變成了進程。
?? ?
?? ?在Linux系統中用task_struct來描述和記錄進程的一切,這個結構體也稱之為進程控制塊(Process Control Block)
?? ??? ?該結構體的定義在如下文件中可以查看(637行左右)
?? ??? ?/usr/src/linux-headers-5.3.0-40/include/linux/sched.h
?? ?小結:
?? ??? ?當一個程序執行時,系統就會創建/啟動一個進程,這個進程用結構體來描述,其中記錄和描述了
?? ??? ?這個進程所需要的所有資源,隨著進程的運行,各種資源被分配和釋放,是一個動態的過程。
3.?? ?進程的組織形態
?? ?在系統中每一個進程都有一個唯一的ID,稱之為PID(Process ID)
?? ?PID是重要的系統資源,是區分其他進程的基本依據。其本質上就是一個非負的整數(>=0)
?? ?在主流的操作系統中,如果進程A啟動了進程B,就把進程A稱之為進程B的父進程,進程B就稱之為
?? ?進程A的子進程。
?? ?
?? ?linux中任何一個進程都有一個創建/啟動它的“父母”,除了0號進程。
?? ?
?? ?linux中進程0啟動了進程1和進程2,其它進程都是由進程1或進程2創建/啟動,從而形成樹狀結構。
?? ?
4.?? ?進程狀態
?? ?在單CPU的計算機中,所謂的“同時”運行多個任務(并發),并不是真正的并發,
?? ?系統把CPU的執行時間,切分為細小的單位,比如10ms,稱之為時間片。
?? ?在單個時間片內,時間片到,就執行下一個程序,如此循環。
?? ?
?? ?進程是程序的執行過程,有自己的生命周期,可分為如下狀態:
?? ??? ?就緒態:進程具備執行的一切條件,正在等待CPU的處理
?? ??? ?運行態:進程正在運行,占用CPU
?? ??? ?阻塞態:因等待某件事件發生而休眠,如果等待的資源分配到位,就會被喚醒進行就緒態。
?? ??? ?
?? ?那么我們剛提到要按某種規則挑選一個就緒的進程開始運行 ,怎么挑選呢?
?? ??? ?所有的就緒的進程會則組成一個“就緒隊列”(Ready Queue)
?? ??? ?
?? ??? ?那么我們會有一個“調度程序”負責確定下一個進入"Running"狀態的進程。
?? ??? ?
?? ??? ?“調度程序”是按某種“調度策略(調度算法)”來進行調度的,如:
?? ??? ??? ?分時系統:
?? ??? ??? ??? ?調度策略是以“時間片輪轉”為主要策略的系統
?? ??? ??? ??? ?“時間片輪轉”:分時,每一個進程執行一段時間(時間片)。
?? ??? ??? ??? ?如:
?? ??? ??? ??? ??? ?大部分的桌面系統,如:Linux,Android,Windows,UINX...
?? ??? ??? ?
?? ??? ??? ?實時系統:
?? ??? ??? ??? ?調度策略是以“實時策略”為主要策略的系統
?? ??? ??? ??? ?“實時策略”:每次調度都優先取優先級最高的那個進程開始執行,直到這個進程
?? ??? ??? ??? ?執行完畢或它主動放棄CPU或其他更高優先級的進程搶占。
?? ??? ??? ??? ?如:
?? ??? ??? ??? ??? ?UCOS,FreeRTOS...
?? ??? ??? ?無論是哪種系統都會有“搶占(插隊)”的情況發生。
5.?? ?linux進程地址空間布局
?? ?我們知道程序一旦開始執行就會進行系統資源的分配,那么進程做的第一件事就是申請一塊內存區域
?? ?來存儲程序的“數據”和“指令”,那么一個程序里面"數據"和“指令”他們的屬性是各不一致的,所以
?? ?要分段(區域)來存儲程序中的“數據”和“指令”。
?? ?
?? ?linux對進程的數據進行分段管理,不同的屬性的數據存儲在不同的“內存段”中。
?? ?不同的“內存段(內存區域)”的屬性以及管理方法各不一樣。
?? ?
?? ?.text 段
?? ??? ?主要用來存儲代碼。只讀并且共享的,這段內存在程序運行期間(進程存活期間)不會被釋放。
?? ??? ?“代碼段”是隨進程的持續性。
?? ?
?? ?.rodata段
?? ??? ?只讀,這段內存在進程運行期間,一直存在,隨進程的持續性。
?? ?
?? ?.data段:
?? ??? ?可讀可寫,這段內存在進程運行期間,一直存在,隨進程的持續性。
?? ??? ?
?? ?.bss段:
?? ??? ?可讀可寫,這段內存在進程運行期間,一直存在,隨進程的持續性。
?? ??? ?在這個段中的數據,有可能會被全部初始化為0
?? ?
?? ?堆(heap) 動態內存空間
?? ??? ?主要是malloc/realloc/calloc動態分配的空間
?? ??? ?可讀可寫的,這段內存在進行運行期間,一旦分配,就會一直存在,直到你手動free或進程消亡。
?? ??? ?防止“內存泄露/垃圾內存”
?? ?
?? ?棧(stack)空間
?? ??? ?可讀可寫的,這段空間會自動釋放,隨代碼塊的持續性。
?? ??? ?
6.?? ?Linux下進程相關的API函數
?? ?1)?? ?創建進程
?? ??? ?
?? ??? ?NAME
?? ??? ??? ?fork - create a child process
?? ??? ?SYNOPSIS
?? ??? ??? ?#include <sys/types.h>
?? ??? ??? ?#include <unistd.h>
?? ??? ??? ?pid_t fork(void);
?? ??? ??? ?通過復制調用進程來創建新的子進程,新的子進程幾乎與原有進程一致,但是它有自己的PID。
?? ??? ??? ?
?? ??? ??? ?返回值:
?? ??? ??? ??? ?當fork調用成功,就創建出一個新的進程,把新的子進程的PID返回給父進程,子進程自己
?? ??? ??? ?本身返回0.如果fork失敗,返回-1,同時errno被設置。
?? ?
?? ??? ??? ?注意:
?? ??? ??? ??? ?1.?? ?當創建子進程后,那么父進程和子進程到底誰先執行,不一定。
?? ??? ??? ??? ?2.?? ?如果父進程先執行,執行完畢之后,子進程還沒有結束的話,子進程會認其他進程
?? ??? ??? ??? ??? ?為新的父進程。
?? ??? ??? ??? ?3.?? ?新的進程是用復制創建出來的,擁有父進程的所有資源。新的進程從fork之后開始執行
?? ??? ??? ??? ??? ?即fork之前的代碼父進程執行一遍,fork之后的代碼,父子同時執行。
?? ??? ??? ?
?? ?2)?? ?獲取PID
?? ??? ?#include <sys/types.h>
?? ??? ?#include <unistd.h>
?? ??? ?pid_t getpid(void);?? ??? ?//獲取當前進程的ID
?? ??? ?pid_t getppid(void);?? ?//獲取當前進程的父進程的ID
?? ??? ?
?? ??? ?練習:
?? ??? ??? ?寫一個代碼,創建4個進程:
?? ?3)?? ?進程退出
?? ??? ?3.1?? ?正常退出(自殺)
?? ??? ??? ?a.?? ?在main函數中執行return
?? ??? ??? ?b.?? ?調用exit/_exit函數
?? ??? ??? ??? ?#include <stdlib.h>
?? ??? ??? ??? ?void exit(int status);
?? ??? ??? ??? ??? ?status:表示退出碼,表示退出狀態的。
?? ??? ??? ??? ??? ?退出碼的具體含義,由程序猿來解釋。
?? ??? ??? ??? ??? ?exit()正常退出,做一些清理工作(比如清理緩沖區)。
?? ??? ??? ??? ?
?? ??? ??? ??? ?#include <unistd.h>
?? ??? ??? ??? ?void _exit(int status);
?? ??? ??? ??? ??? ?status:表示退出碼,表示退出狀態的。
?? ??? ??? ??? ??? ?退出碼的具體含義,由程序猿來解釋。
?? ??? ??? ??? ??? ?_exit連夜坐火車走的,終止進程,來不及做清理工作。
?? ??? ??? ??? ??? ?
?? ??? ?3.2?? ?非正常退出(他殺)
?? ??? ??? ?在外力干涉下結束,如進程出現段錯誤,系統會自動結束這個進程。
?? ??? ??? ?
?? ?4)?? ?注冊終止函數
?? ??? ?即main函數正常結束后調用的函數。
?? ??? ?
?? ??? ?NAME
?? ??? ??? ?atexit - register a function to be called at normal process termination
?? ??? ?SYNOPSIS
?? ??? ??? ?#include <stdlib.h>
?? ??? ??? ?int atexit(void (*function)(void));
?? ??? ??? ?1.?? ?atexit()注冊的函數類型應為不接受任何參數的void類型
?? ??? ??? ?2.?? ?調用注冊函數的順序與他們注冊時候的順序是相反的,即先注冊后執行,后注冊先執行
?? ??? ??? ??? ?同一個函數若注冊多次,則也會被調用多次。
?? ??? ??? ?3.?? ?如果main函數是在非正常退出的情況下,注冊函數將不會被調用。
?? ??? ??? ??? ?
?? ?5)?? ?等待一個進程
?? ??? ?NAME
?? ??? ??? ?wait, waitpid, waitid - wait for process to change state
?? ??? ?SYNOPSIS
?? ??? ??? ?#include <sys/types.h>
?? ??? ??? ?#include <sys/wait.h>
?? ??? ??? ?pid_t wait(int *wstatus);
?? ??? ??? ?pid_t waitpid(pid_t pid, int *wstatus, int options);
?? ??? ??? ?
?? ??? ??? ?一個進程退出,操作系統會釋放這個退出進程的大部分資源,就是說有一小部分資源必須
?? ??? ??? ?留給他的父進程去釋放。如果一個進程退出了,但是它的父進程沒有去釋放這個子進程,
?? ??? ??? ?那么這個子進程就會變成一個僵尸進程。
?? ??? ??? ?僵尸進程:進程已經死掉了但是資源還沒有被完全釋放掉(還沒死透)。
?? ??? ??? ?那么僵尸進程的產生將會造成系統資源的浪費,我們應該要盡量的去避免這種情況的發生。
?? ??? ??? ?
?? ??? ??? ?通過wait/waitpid函數來釋放子進程留下來的資源。
?? ??? ??? ?
?? ??? ??? ?這兩個函數的作用是用來等待某個(些)子進程退出的,當子進程正常退出時,調用wait/waitpid
?? ??? ??? ?可以釋放子進程的資源啦。假如沒有調用wait/waitpid,那么子進程退出后,就會變成僵尸進程
?? ??? ??? ?
?? ??? ??? ?pid_t wait(int *wstatus);
?? ??? ??? ??? ?父進程一旦調用了wait,就立即阻塞自己,由wait自動分析當前進程的某個子進程是否
?? ??? ??? ??? ?已經退出,如果讓它找到一個已經變成僵尸進程的子進程,wait就是收集這個子進程的
?? ??? ??? ??? ?信息,并將子進程銷毀掉之后返回。
?? ??? ??? ??? ?如果沒有找到一個這樣的子進程,wait就會一直阻塞,直到有一個符合條件的進程
?? ??? ??? ??? ?出現為止。
?? ??? ??? ??? ?
?? ??? ??? ??? ?其中有一個參數
?? ??? ??? ??? ?wstatus:它是一個int類型的指針,那么這個參數是用來收集子進程的退出信息的,
?? ??? ??? ??? ?所以它指向的空間是用來保存子進程的退出信息(子進程是正常退出還是非正常退出、
?? ??? ??? ??? ?子進程的退出碼、who kill....)。如果我們并不在意子進程的退出信息的話,
?? ??? ??? ??? ?那么我們就可以設定這個參數為NULL。
?? ??? ??? ?
?? ??? ??? ?返回值:
?? ??? ??? ??? ?成功返回退出的那個子進程的進程ID,失敗返回-1,同時errno被設置。
?? ??? ?
?? ??? ??? ?需要注意的是:
?? ??? ??? ??? ?*wstatus是一個整數,用來保存退出的子進程的退出信息的,退出的信息比較多,
?? ??? ??? ??? ?這些退出信息全部保存在一個整數中。則意味著不同的退出信息要保存在不同的
?? ??? ??? ??? ?比特段中,比如:
?? ??? ??? ??? ??? ?我們的退出碼就存儲于第8bit到第15bit的段上。
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ?注意:
?? ??? ??? ??? ??? ?因為這退出碼是保存在wstatus指向的空間的第8bit到第15bit上,所以
?? ??? ??? ??? ??? ?退出碼的范圍不應該超過一個unsigned char的取值范圍(<=255)。
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ?如果我們要獲取到進程的退出信息的話則必須要對status進行解析,解析通過如下宏:
?? ??? ??? ??? ??? ?WIFEXITED(wstatus)
?? ??? ??? ??? ??? ?如果子進程是正常退出的,則這個宏將會返回一個非零。
?? ??? ??? ??? ??? ?WEXITSTATUS(wstatus)
?? ??? ??? ??? ??? ?當WIFEXITED()返回一個非零值時,我們就可以利用這個宏來提取子進程的退出碼啦。
?? ??? ??? ??? ??? ?例子:
?? ??? ??? ??? ??? ??? ?exit(4) ? ?WEXITSTATUS(wstatus)-->4
?? ??? ??? ??? ??? ??? ?exit(257) ?WEXITSTATUS(wstatus)-->1
?? ??? ??? ??? ??? ?如果WIFEXITED返回一個0,則這個宏毫無意義,因為WIFEXITED返回0表示
?? ??? ??? ??? ??? ?進程是非正常退出的,非正常退出的進程是沒有退出碼的。
?? ??? ??? ??? ??? ?
?? ??? ??? ?pid_t waitpid(pid_t pid, int *wstatus, int options);?? ?
?? ??? ??? ??? ?pid:指定要等待的進程或進程組
?? ??? ??? ??? ??? ?pid == -1?? ??? ?表示等待任意的子進程退出
?? ??? ??? ??? ??? ?pid == 0 ?? ??? ?表示等待與調用進程同組的任意子進程。
?? ??? ??? ??? ??? ??? ?“進程組”:就是一組進程。
?? ??? ??? ??? ??? ??? ?每一個進程必須會屬于某一個進程組。并且每個進程組,都會有一個組長進程,
?? ??? ??? ??? ??? ??? ?一般來說,創建這個進程組的進程為組長,進程組有一個組ID,這個組ID就是
?? ??? ??? ??? ??? ??? ?組長進程的ID。
?? ??? ??? ??? ??? ?pid < -1?? ??? ?表示等待組ID等于pid絕對值的那個組的任意子進程
?? ??? ??? ??? ??? ??? ??? ??? ??? ?如:?? ?
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?pid == -2398
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?等待進程組ID為2398的那個組內的任意子進程
?? ??? ??? ??? ??? ?pid > 0?? ??? ??? ?表示等待指定的子進程(其進程ID為pid的那個子進程)
?? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ?wstatus:同上。
?? ??? ??? ??? ?
?? ??? ??? ??? ?options:等待選項。
?? ??? ??? ??? ??? ?0:表示阻塞等待
?? ??? ??? ??? ??? ?WNOHANG:非阻塞,假如沒有子進程退出,則立即返回。
?? ??? ??? ??? ??? ?
?? ??? ??? ?返回值:?? ?
?? ??? ??? ??? ?成功返回退出的那個子進程的進程ID,失敗返回-1,同時errno被設置。
?? ??? ??? ?wait(&status) <----> waitpid(-1,&status,0);?? ??? ??? ??? ?
?? ??? ??? ??? ?
?? ??? ?作業:
?? ??? ??? ?假設程序名為a.out,問程序執行后,總共出現過多少個a.out進程?
?? ??? ??? ??? ?int main()
?? ??? ??? ??? ?{
?? ??? ??? ??? ??? ?fork();
?? ??? ??? ??? ??? ?fork() && fork() || fork();
?? ??? ??? ??? ??? ?fork();
?? ??? ??? ??? ??? ?printf("Hello,World!\n");
?? ??? ??? ??? ?}
?? ??? ??? ??? ?
?? ?6)?? ?啟動外部程序
?? ??? ?我們通過exec函數族來實現:
?? ??? ??? ?#include <unistd.h>
?? ??? ??? ?extern char **environ;
?? ??? ??? ?int execl(const char *path, const char *arg, .../* (char ?*) NULL */);
?? ??? ??? ?int execlp(const char *file, const char *arg, .../* (char ?*) NULL */);
?? ??? ??? ?int execle(const char *path, const char *arg, .../*, (char *) NULL, char * const envp[] */);
?? ??? ??? ?int execv(const char *path, char *const argv[]);
?? ??? ??? ?int execvp(const char *file, char *const argv[]);
?? ??? ??? ?int execvpe(const char *file, char *const argv[],char *const envp[]);
?? ??? ??? ?
?? ??? ??? ?exec函數族是讓一個進程去執行另外一個程序文件。
?? ??? ??? ?
?? ??? ??? ?a.?? ?int execl(const char *path, const char *arg, .../* (char ?*) NULL */);
?? ??? ??? ??? ?
?? ??? ??? ??? ?讓進程去執行參數指定的程序文件
?? ??? ??? ??? ?
?? ??? ??? ??? ?參數:
?? ??? ??? ??? ??? ?path:程序文件的文件名(需要指定這個程序的路徑的)
?? ??? ??? ??? ??? ?arg...:新程序執行需要的參數,參數的個數是可變的。
?? ??? ??? ??? ??? ??? ?注意:
?? ??? ??? ??? ??? ??? ??? ?參數的個數可以有很多,但最后一定要以NULL結尾。
?? ??? ??? ??? ??? ??? ??? ?參數列表中的第一個參數應該是可執行文件(程序)的名字。
?? ??? ??? ??? ?
?? ??? ??? ??? ?l:(list)的意思就是參數是以列表的形式存在的。
?? ??? ??? ??? ?
?? ??? ??? ?b.?? ?int execv(const char *path, char *const argv[]);
?? ??? ??? ??? ?
?? ??? ??? ??? ?execv與execl作用、功能、返回值都是一樣的。
?? ??? ??? ??? ?唯一的區別在于,指定的程序文件的帶參數的方式不一樣。
?? ??? ??? ??? ??? ?path:程序文件的文件名(需要指定這個程序的路徑的)
?? ??? ??? ??? ??? ?argv:指定程序運行的參數,程序運行的第一個參數是程序名,最后一個為NULL
?? ??? ??? ??? ??? ?表示參數結束了。
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ?帶V(vector向量)的意思就是參數是以數組的形式存在的。
?? ??? ??? ??? ?
?? ??? ??? ?c.?? ?int execlp(const char *file, const char *arg, .../* (char ?*) NULL */);?? ?
?? ??? ??? ??? ?int execvp(const char *file, char *const argv[]);
?? ??? ??? ??? ?帶p(PATH):意思就是指定的程序文件在標準的命令搜索路徑(PATH)下
?? ??? ??? ??? ??? ?file:可執行文件的程序名(可以不帶路徑)
?? ??? ??? ??? ??? ?其他參數同上
?? ??? ??? ??? ??? ?
?? ??? ??? ?d.?? ?int execle(const char *path, const char *arg, .../*, (char *) NULL, char * const envp[] */);
?? ??? ??? ??? ?int execvpe(const char *file, char *const argv[],char *const envp[]);
?? ??? ??? ??? ?
?? ??? ??? ??? ?帶e(environment):使用環境變量數組設置新執行的程序運行的環境,不使用
?? ??? ??? ??? ?進程原有的環境變量
?? ??? ??? ??? ?
?? ??? ??? ??? ?環境變量:
?? ??? ??? ??? ??? ?PATH
?? ??? ??? ??? ??? ?HOME
?? ??? ??? ??? ??? ?LD_LIBRARY_PATH
?? ??? ??? ??? ??? ?....
?? ??? ??? ??? ??? ?環境變量中包含用戶的主目錄,命令的搜索路徑,當前目錄.....,他們包含了
?? ??? ??? ??? ??? ?用戶的工作環境,所以我們稱之為環境變量。
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ?我們可以通過一個名稱 env 的指令去查看系統環境變量。
?? ??? ??? ??? ?
?? ??? ??? ??? ?系統同時也預定義了一個environ全局變量顯示各個環境變量的值。
?? ??? ??? ??? ?extern char **environ;
?? ??? ??? ??? ?
?? ??? ??? ??? ?我們可以通過execvpe函數修改程序所運行的環境:
?? ??? ??? ??? ??? ?
?? ?7)?? ?system
?? ??? ?
?? ??? ?NAME
?? ??? ??? ?system - execute a shell command
?? ??? ?SYNOPSIS
?? ??? ??? ?#include <stdlib.h>
?? ??? ??? ?int system(const char *command);
?? ??? ??? ??? ?
?? ??? ??? ??? ?system用來執行command指定的命令或程序或sh腳本。
?? ??? ??? ??? ?
?? ??? ??? ??? ??? ?system("ls -l");--->fork--->exec("ls -l")
?? ??? ??? ??? ?
?? ??? ??? ??? ?system會等待命令或程序執行完畢,system實際上是新創建了一個進程去執行指定的
?? ??? ??? ??? ?命令或程序。
?? ??? ??? ??? ?
?? ??? ??? ??? ?返回值:
?? ??? ??? ??? ??? ?成功返回狀態碼,失敗返回-1.
?? ??? ??? ??? ??? ?
?? ??? ?
?? ??? ?在linux下有一條mplayer或者madplay。
?? ??? ?
?? ?作業:
?? ??? ?寫一個程序,創建一個子進程,播放一個目錄下的所有的mp3/mp4文件,實現自動循環/手動播放。
?? ??? ??? ?
?? ??? ??? ?mplayer :是一個開源的多媒體播放器,此款軟件可在各主流作業系統上使用,因為
?? ??? ??? ?linux下都是命令行的操作方式,所以對mplayer的各個操作都是用命令來實現的,
?? ??? ??? ?此次項目主要是用到了mplayer它的slave(從模式)工作方式。
?? ??? ??? ?
?? ??? ??? ?默認mplayer是從鍵盤上獲取控制信息,mplayer另外提供了一種更為靈活的控制方式,
?? ??? ??? ?用來進行播放控制---slave模式。在slave模式,mplayer為后臺運行其他程序,不再
?? ??? ??? ?截獲鍵盤事件,mplayer會從標準輸入讀取一個換行符分隔開的命令。
?? ??? ??? ?
?? ??? ??? ?查看mplayer所支持的所有slave模式的命令:
?? ??? ??? ??? ?mplayer -input cmdlist
?? ??? ??? ??? ?
?? ??? ??? ?根據命令去操作mplayer的方式有兩種:
?? ??? ??? ?1.?? ?從控制臺輸入控制命令(在終端上)
?? ??? ??? ??? ?在終端上輸入指令:
?? ??? ??? ??? ??? ?mplayer -slave -quiet xxx.mp4
?? ??? ??? ??? ??? ?-slave ? 啟動從模式
?? ??? ??? ??? ??? ?-quiet?? ? 不輸出冗余的信息
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ?啟動mplayer之后,我們就可以在終端上輸入指令來控制mplayer的運行啦:
?? ??? ??? ??? ??? ?loadfile string ?? ??? ?//參數string為播放文件的名字
?? ??? ??? ??? ??? ?volume 100 1?? ??? ??? ?//設置音量,中間的值為音量的大小
?? ??? ??? ??? ??? ?mute 1/0?? ??? ??? ??? ?//靜音設置,1:靜音 ? 0:開啟聲音
?? ??? ??? ??? ??? ?pause ?? ??? ??? ??? ??? ?//暫停/取消暫停
?? ??? ??? ??? ??? ?get_time_length ?? ??? ?//返回值是播放文件的長度,以秒為單位
?? ??? ??? ??? ??? ?seek value ?? ??? ??? ??? ?//快進/后退,參數value為秒數
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?//value為正,快進value秒
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?//value為負,后退value秒
?? ??? ??? ??? ??? ?seek value 1?? ??? ??? ?//意味著跳轉到百分之value處?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?get_percent_pos ?? ??? ?//按百分比輸出當前的播放進度
?? ??? ??? ??? ??? ?get_time_pos ?? ??? ??? ?//把文件當前的播放的秒鐘數以浮點型打印出來
?? ??? ??? ??? ??? ?get_file_name ?? ??? ??? ?//打印出當前文件的名字
?? ??? ??? ??? ??? ?quit ?? ??? ??? ??? ??? ?//退出播放
?? ??? ??? ?
?? ??? ??? ?2.?? ?從有名管道輸入控制命令(在代碼中使用)
?? ??? ??? ??? ?我們可以用指令指定mplayer從有名管道中獲取指令,那么我們要做的就是在代碼中
?? ??? ??? ??? ?往管道中寫入指令就同樣的可以控制mplayer。
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?指定mplayer從哪一個管道中獲取指令:
?? ??? ??? ??? ??? ??? ?mplayer -slave -quiet -input file=xxx.fifo
?? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ?write(xxx.fifo,"pause\n",sizeof("pause\n"));
?? ??? ??? ??? ?
?? ??? ??? ??? ??? ?另外,我們還可以指定mplayer的播放界面的大小:
?? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ?mplayer -zoom -x 800 -y 480 xx.mp4
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?so,我們最終的mplayer的完整命令為:
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?mplayer -slave -quiet -input file=xxx.fifo -zoom -x 800 -y 480 1.mp4
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?int c = getchar();
?? ??? ??? ??? ??? ?switch(c)
?? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ?case 'a':
?? ??? ??? ??? ??? ??? ??? ?write(xxx.fifo,"seek -5\n",sizeof("seek -5\n"));
?? ??? ??? ??? ??? ??? ??? ?break;
?? ??? ??? ??? ??? ??? ?case 'p':
?? ??? ??? ??? ??? ??? ??? ?write(xxx.fifo,"pause\n",sizeof("pause\n"));
?? ??? ??? ??? ??? ??? ??? ?break;
?? ??? ??? ??? ??? ??? ??? ?......
?? ??? ??? ??? ??? ?}
?? ??? ??? ?//搜索所有的MP3/MP4文件--->鏈表
?? ??? ??? ?while(1)
?? ??? ??? ?{
?? ??? ??? ??? ?//取下一首
?? ??? ??? ??? ?pid_t pid = fork();
?? ??? ??? ??? ?if(兒子)
?? ??? ??? ??? ?{
?? ??? ??? ??? ??? ?exec--->讓兒子去播放視頻
?? ??? ??? ??? ?}
?? ??? ??? ??? ?else if(老子)
?? ??? ??? ??? ?{
?? ??? ??? ??? ??? ?wait(兒子);
?? ??? ??? ??? ?}?? ?
?? ??? ??? ?}
7.?? ?IPC:Internal Process Communication
?? ?進程間通信,實質是:信息(數據)的交換
?? ?
?? ?如果兩個進程之間要進行通信,就必須要把數據放在一個大家都能夠訪問到的地方。
?? ?
?? ?文件可以嗎?當然可以,因為文件在文件系統中,大家都可以訪問。
?? ?
?? ?但是這種方式有一個缺點,速度太慢了。
?? ?
?? ?我們講內存的讀寫速度比文件塊很多,那能不能在內存中創建一塊空間,這塊內存空間通信進程
?? ?都是能夠訪問的,以此來解決問題呢?
?? ?
?? ?IPC方式:
?? ??? ?管道?? ?:?? ?pipe?? ?無名管道
?? ??? ??? ??? ??? ?fifo?? ?有名管道
?? ??? ??? ??? ?
?? ??? ?信號?? ?:?? ?signal
?? ?
?? ??? ?消息隊列:?? ?System V消息隊列 / POSIX消息隊列---->淘汰啦
?? ??? ?信號量?? ?:?? ?System V信號量 / POSIX信號量
?? ??? ?共享內存:?? ?System V共享內存 / POSIX共享內存
?? ??? ?Socket ?? ?:?? ?Unix域協議
?? ??? ?
?? ??? ?
?? ?在很久很久以前,其實我們進程間通信就是通過文件。?? ?
?? ?
?? ?但是這種方式,有一個缺點,就是效率太低了。
?? ?
?? ?但是這種方式又有一個天大的好處就是簡單,不需要額外提供API函數(直接利用文件系統的API函數)
?? ?
?? ?有人想,能不能改進一下?
?? ??? ?問題是文件內容是在外設(硬盤)上--->訪問速度太低了。
?? ??? ?能不能把文件的內容放到內存上去呢?
?? ??? ?
?? ??? ?管道:管道文件,但是內容是在內存或內核中的。
?? ??? ?
?? ?(1)?? ?無名管道 pipe
?? ??? ?
?? ??? ?我們說無名管道雖然是一個文件,但是它在文件系統中沒有名字(沒有inode),他的內容在內核中,
?? ??? ?訪問pipe的方式還是通過文件系統的API(open/read/write...),但是他又不能用open(因為沒有名字)
?? ??? ?問題是read/write又需要用一個文件描述符才能對文件進行操作,所以在創建一個pipe的時候,
?? ??? ?就必須要返回文件描述符。
?? ??? ?
?? ??? ?pipe在創建時,在內核中開辟一塊緩沖區,作為pipe文件內容的存儲空間,同時返回兩個
?? ??? ?文件描述符(一個用來讀,一個用來寫)
?? ??? ?
?? ??? ?并且有如下特點:
?? ??? ??? ?1.?? ?pipe有兩端,一端是用來寫,一端是用來讀。
?? ??? ??? ?2.?? ?按順序讀,不支持lseek。
?? ??? ??? ?3.?? ?內容讀走了,就沒啦。
?? ??? ??? ?4.?? ?pipe(無名管道)隨內核的持續性。
?? ??? ??? ?
?? ??? ?函數接口:
?? ??? ??? ?NAME
?? ??? ??? ??? ?pipe - create pipe
?? ??? ??? ?SYNOPSIS
?? ??? ??? ??? ?#include <unistd.h>
?? ??? ??? ??? ?int pipe(int pipefd[2]);
?? ??? ??? ??? ??? ?pipe用來在內核中創建一個無名管道的。
?? ??? ??? ??? ??? ?pipefd:數組
?? ??? ??? ??? ??? ??? ?pipefd[0]?? ?保存讀的文件描述符
?? ??? ??? ??? ??? ??? ?pipefd[1]?? ?保存寫的文件描述符
?? ??? ?
?? ??? ??? ??? ??? ?返回值:成功返回0,失敗返回-1,同時errno被設置。
?? ??? ?
?? ??? ??? ??? ?pipe創建的管道,默認是“阻塞方式”。
?? ??? ??? ??? ?意思就是:讀一個管道,如果管道中沒有數據,則阻塞,直到有數據可讀為止
?? ??? ??? ??? ??? ??? ? ?往一個管道中寫數據,如果管道不可寫,則阻塞,直到文件可寫為止。
?? ??? ??? ??? ??? ??? ? ?
?? ??? ??? ??? ?原則上來講:只要兩個進程能獲取到同一個pipe的文件描述符,就可以用pipe進行通信。
?? ??? ??? ??? ?一般情況下無名管道用于有親緣關系的進程間通信。
?? ??? ??? ??? ?
?? ??? ??? ??? ?pipe本身是一個全雙工通信,但是兩個進程用一個管道去實現全雙工的哦通信可能會有
?? ??? ??? ??? ?問題,問題就是自己很有可能會讀到自己寫的數據,所以我們人為的把pipe當成
?? ??? ??? ??? ?半雙工使用。
?? ??? ??? ??? ?
?? ?(2)?? ?有名管道
?? ??? ?pipe(無名管道)只能用于有親緣關系的進程間通信,為什么會有這么一個限制呢?
?? ??? ?原因就是pipe沒有名字
?? ??? ?
?? ??? ?假設他在文件系統中有一個名字(inode),它就可以用于任意進程間的通信。
?? ??? ?那么這個就是我們的有名管道fifo。
?? ??? ?
?? ??? ?fifo是在pipe的基礎上,給fifo在文件系統中創建一個inode(它會在文件系統中有一個名字)
?? ??? ?但是fifo文件內容卻是在內核中。
?? ??? ?
?? ??? ?fifo的文件名隨文件系統的持續性。
?? ??? ?
?? ??? ?fifo同pipe一樣,文件內容是存在于內核中的,隨內核的持續性。
?? ??? ?
?? ??? ?那么操作fifo的一個步驟就是:
?? ??? ??? ?open ---> read/wirte ---> close
?? ??? ??? ?
?? ??? ?fifo的創建為:
?? ??? ??? ?mkfifo
?? ??? ??? ?
?? ??? ??? ?#include <sys/types.h>
?? ??? ??? ?#include <sys/stat.h>
?? ??? ??? ?int mkfifo(const char *pathname, mode_t mode);
?? ??? ??? ??? ?mkfifo用來在文件系統中創建一個fifo(有名管道)
?? ??? ??? ??? ?
?? ??? ??? ??? ?pathname:要創建的有名管道在文件系統中的名字
?? ??? ??? ??? ?mode:創建的有名管道的權限,有兩種方式指定:
?? ??? ??? ??? ??? ?a.?? ?S_IRUSR....
?? ??? ??? ??? ??? ?b.?? ?0660
?? ??? ??? ?
?? ??? ??? ??? ?函數返回值:
?? ??? ??? ??? ??? ?成功返回0,失敗返回-1,同時errno被設置。
?? ??? ??? ?
?? ??? ??? ?fifo的創建有兩種不同的形式:
?? ??? ??? ?1.?? ?通過指令mkfifo
?? ??? ??? ??? ?mkfifo ~/test.fifo
?? ??? ??? ??? ?
?? ??? ??? ??? ?創建到linux的目錄下去,不能創建到共享文件夾,因為window不支持fifo。
?? ??? ??? ??? ?
?? ??? ??? ?2.?? ?通過函數
?? ??? ??? ??? ? ? ? ??
?? ??? ??? ??? ??? ? ??
?? ??? ??? ?關于fifo的一段描述:?? ??? ? ??
?? ??? ??? ?A ?FIFO special file (a named pipe) is similar to a pipe, except that it is accessed
?? ??? ? ? as part of the filesystem. ?It can be opened by multiple processes ?for ?reading ?or
?? ??? ? ? writing. ? When ?processes ?are ?exchanging data via the FIFO, the kernel passes all
?? ??? ? ? data internally without writing it to the filesystem. ?Thus, the FIFO ?special ?file
?? ??? ? ? has no contents on the filesystem; the filesystem entry merely serves as a reference
?? ??? ? ? point so that processes can access the pipe using a name in the filesystem.
?? ??? ??? ?
?? ??? ??? ?FIFO(有名管道)和PIPE(無名管道)類似,除了它在文件系統中有一個名字,它可以被多個進程打開
?? ??? ??? ?或者讀/寫,當進程用FIFO來交換數據時,內核根本沒有把數據寫入到文件系統中去,
?? ??? ??? ?而是保存在內核的內部,它僅作為文件系統的一個引用入口,提供一個文件名,給其他進程去
?? ??? ??? ?open它。
?? ??? ??? ?
?? ??? ??? ?在數據交換前,FIFO的兩端(read/write)必須都被打開。
?? ??? ??? ?
?? ??? ??? ?通常情況下,你打開FIFO的一端,會阻塞,直到另外一端也被打開。
?? ??? ??? ?
?? ??? ??? ?一個進程也可能以“非阻塞”(O_NONBLOCK)的方式打開
?? ??? ??? ?在這種情況下,只讀打開總會成功,即使是寫端沒有被打開
?? ??? ??? ?只寫打開總會失敗,并且errno == EENXIO,除非讀端已經被打開。
?? ??? ??? ?
?? ??? ?阻塞方式:阻塞的讀或寫
?? ??? ??? ?讀的時候,如果沒有數據,則read會阻塞
?? ??? ??? ?寫的時候,如果沒有空間,則write會阻塞
?? ??? ??? ?
?? ??? ?非阻塞方式:以非阻塞讀或寫
?? ??? ??? ?讀的時候,如果沒有數據,立即返回,設置相應的錯誤碼
?? ??? ??? ?寫的時候,如果沒有空間,立即返回,設置相應的錯誤碼
?? ??? ??? ?
?? ?(3)?? ?信號
?? ??? ?信號是進程間通信的一種方式,這種方式沒有傳輸數據。只是在內核中傳遞了一個信號(整數),
?? ??? ?信號表示的就是一個整數。
?? ??? ?
?? ??? ?比如如果我們有一個進程正在執行,突然之間它收到了一個信號2(SIGINT),在我們linux下面,
?? ??? ?一個進程收到信號2默認的動作就是被終止了(Terminate)。
?? ??? ?不同的信號值,代表不同的含義,當然用戶也可以自定義信號。
?? ??? ?
?? ??? ?那么自定義的信號的含義和值由程序猿來定義和解釋。
?? ??? ?
?? ??? ?那么信號的本質其實是軟中斷。
?? ??? ?信號會中斷正在執行的某一個任務/某一段程序,進而去處理中斷(處理函數),處理完中斷后,
?? ??? ?再回來繼續執行原有的程序。
?? ??? ?
?? ??? ?信號是異步的。
?? ??? ?
?? ??? ?注意:?? ?
?? ??? ??? ?如果用戶沒有顯示的處理信號,系統的默認處理方式大多是終止進程。
?? ??? ??? ?
?? ??? ?查看信號可以在終端上輸入:
?? ??? ??? ?man 7 signal
?? ??? ??? ?
?? ??? ??? ?
?? ? ? Signal ? ? Value ? ? Action ? Comment
? ? ? ?──────────────────────────────────────────────────────────────────────
? ? ? ?SIGHUP ? ? ? ?1 ? ? ? Term ? ?Hangup detected on controlling terminal
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?or death of controlling process
?? ??? ??? ??? ??? ??? ??? ??? ??? ??
? ? ? ?SIGINT ? ? ? ?2 ? ? ? Term ? ?Interrupt from keyboard
?? ??? ??? ??? ??? ??? ??? ??? ??? ? 從鍵盤上收到的中斷信號,按下ctrl + c就會產生這個信號
?? ? ??
? ? ? ?SIGQUIT ? ? ? 3 ? ? ? Core ? ?Quit from keyboard
?? ??? ??? ??? ??? ??? ??? ??? ??? ? 默認處理是輸出信息然后終止進程,按下ctrl + /就會產生這個
?? ??? ??? ??? ??? ??? ??? ??? ??? ? 信號
?? ? ??
? ? ? ?SIGILL ? ? ? ?4 ? ? ? Core ? ?Illegal Instruction
?? ? ? ??
? ? ? ?SIGABRT ? ? ? 6 ? ? ? Core ? ?Abort signal from abort(3)
?? ? ??
? ? ? ?SIGFPE ? ? ? ?8 ? ? ? Core ? ?Floating-point exception
?? ??? ??? ??? ??? ??? ??? ??? ??? ? 浮點型異常
?? ? ??
? ? ? ?SIGKILL ? ? ? 9 ? ? ? Term ? ?Kill signal
?? ??? ??? ??? ??? ??? ??? ??? ??? ? 殺死進程,不可被捕捉和忽略
?? ??? ?
? ? ? ?SIGSEGV ? ? ?11 ? ? ? Core ? ?Invalid memory reference
?? ??? ??? ??? ??? ??? ??? ??? ??? ? 段錯誤
?? ??? ??? ??? ??? ??? ??? ??? ??? ? 非法內存引用時,會收到SIGSEGV。
?? ??? ??? ??? ??? ??? ??? ??? ??? ? 此時輸出信息,然后終止發生段錯誤的進程
?? ? ??
? ? ? ?SIGPIPE ? ? ?13 ? ? ? Term ? ?Broken pipe: write to pipe with no
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?readers; see pipe(7)
?? ??? ??? ??? ??? ??? ??? ??? ??? ? 管道破裂。
?? ??? ??? ??? ??? ??? ??? ??? ??? ? 當你往一個管道寫數據時,沒有讀端進程時就會產生這個信號。
?? ??? ??? ??? ??? ??? ??? ??? ??? ??
? ? ? ?SIGALRM ? ? ?14 ? ? ? Term ? ?Timer signal from alarm(2)
?? ??? ??? ??? ??? ??? ??? ??? ? ? ? 定時信號,在進程中調用alarm時,會在超時的時候,
?? ??? ??? ??? ??? ??? ??? ??? ??? ? 產生這個信號。
?? ? ??
? ? ? ?SIGTERM ? ? ?15 ? ? ? Term ? ?Termination signal
?? ? ??
? ? ? ?SIGUSR1 ? 30,10,16 ? ?Term ? ?User-defined signal 1
? ? ? ?SIGUSR2 ? 31,12,17 ? ?Term ? ?User-defined signal 2
?? ??? ??? ??? ??? ??? ??? ??? ??? ? 用戶自定義信號,這個信號什么含義有用戶自己解釋。
?? ? ??
? ? ? ?SIGCHLD ? 20,17,18 ? ?Ign ? ? Child stopped or terminated
?? ??? ??? ??? ??? ??? ??? ??? ??? ? 當子進程停止或終止時,父進程會收到SIGCHLD這個信號
?? ??? ??? ??? ??? ??? ??? ??? ??? ? 父進程收到這個信號之后,并不會怎么樣,因為這個信號默認是忽略
?? ? ??
? ? ? ?SIGCONT ? 19,18,25 ? ?Cont ? ?Continue if stopped
? ? ? ?SIGSTOP ? 17,19,23 ? ?Stop ? ?Stop process
?? ??? ??? ??? ??? ??? ??? ??? ??? ? 停止進程,不可被捕捉和忽略
?? ? ??
? ? ? ?SIGTSTP ? 18,20,24 ? ?Stop ? ?Stop typed at terminal
? ? ? ?SIGTTIN ? 21,21,26 ? ?Stop ? ?Terminal input for background process
? ? ? ?SIGTTOU ? 22,22,27 ? ?Stop ? ?Terminal output for background process
?? ?linux下信號相關的API函數
?? ??? ?1)?? ?發送信號 ?? ?
?? ??? ??? ?NAME
?? ??? ??? ??? ?kill - send signal to a process
?? ??? ??? ?SYNOPSIS
?? ??? ??? ??? ?#include <sys/types.h>
?? ??? ??? ??? ?#include <signal.h>
?? ??? ??? ??? ?int kill(pid_t pid, int sig);
?? ??? ??? ??? ?kill用來把一個信號發送到一個指定的進程或多個進程
?? ??? ??? ??? ?
?? ??? ??? ??? ?kill不僅是一個函數,也是一條指令。
?? ??? ??? ??? ??? ?pid:指定信號的接受者(可能是多個進程)
?? ??? ??? ??? ??? ??? ?pid > 0 ?? ?pid表示指定的那一個接收者進程
?? ??? ??? ??? ??? ??? ?pid = 0 ?? ?發送信號給與調用進程同組的所有進程
?? ??? ??? ??? ??? ??? ?pid = -1?? ?發送信號給系統所有進程(有權限發送的所有進程)
?? ??? ??? ??? ??? ??? ?pid < -1?? ?發送信號給組ID等于pid的絕對值的所有進程
?? ??? ??? ??? ??? ?sig:要發送的信號,參考上面的宏
?? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?返回值:?? ?
?? ??? ??? ??? ??? ??? ?成功(至少有一個進程成功接收到了信號)返回0,失敗返回-1,同時errno被設置。
?? ??? ??? ??? ??? ?
?? ??? ?2)?? ?raise ?
?? ??? ??? ?NAME
?? ??? ??? ??? ?raise - send a signal to the caller
?? ??? ??? ?SYNOPSIS
?? ??? ??? ??? ?#include <signal.h>
?? ??? ??? ??? ?int raise(int sig);
?? ??? ??? ??? ??? ?發送信號給自己
?? ??? ??? ??? ??? ?raise(sig) <---> kill(getpid(),sig)
?? ??? ?
?? ??? ?3)?? ?alarm
?? ??? ??? ?NAME
?? ??? ??? ??? ?alarm - set an alarm clock for delivery of a signal
?? ??? ??? ?SYNOPSIS
?? ??? ??? ??? ?#include <unistd.h>
?? ??? ??? ??? ?unsigned int alarm(unsigned int seconds);
?? ??? ??? ??? ?alarm定時發送一個鬧鐘信號(SIGALRM)給本進程
?? ??? ??? ??? ?
?? ??? ??? ??? ?“鬧鐘”:每一個進程都有屬于自己的一個“鬧鐘”,“鬧鐘”時間到了,進程就會收到一個
?? ??? ??? ??? ?SIGALRM的信號,但是同一時刻只有一個“鬧鐘”生效。
?? ??? ??? ??? ?
?? ??? ??? ??? ??? ?seconds:多少秒之后,發送一個“鬧鐘信號”。
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?返回值:返回上一個鬧鐘的剩余時間。
?? ??? ??? ??? ?
?? ??? ??? ??? ??? ?例子:
?? ??? ??? ??? ??? ??? ?alarm(5);?? ??? ??? ?//5秒鐘之后收到一個“鬧鐘信號”
?? ??? ??? ??? ??? ??? ?...?? ??? ??? ??? ??? ?//執行這個代碼花了2秒
?? ??? ??? ??? ??? ??? ?int r = alarm(10);?? ?//鬧鐘時間被重新設置為10秒,即前面的那個5秒的鬧鐘被
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?//替換掉了
?? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ?alarm(0);?? ??? ??? ?//表示取消鬧鐘
?? ??? ??? ??? ??? ?
?? ??? ?4)?? ?捕捉信號:改變信號的處理方式
?? ??? ??? ?NAME
?? ??? ??? ??? ?signal - ANSI C signal handling
?? ??? ??? ?SYNOPSIS
?? ??? ??? ??? ?#include <signal.h>
?? ??? ??? ??? ?typedef void (*sighandler_t)(int);
?? ??? ??? ??? ??? ?sighandler_t:是一個新的類型,這種類型可以用來定義一個函數指針的變量
?? ??? ??? ??? ??? ??? ?這個變量指向一個無返回值,有一個int類型的參數的函數。
?? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ?sighandler_t signal(int signum, sighandler_t handler);
?? ??? ??? ??? ??? ?signum:要捕捉的那個信號的信號值
?? ??? ??? ??? ??? ?handler:這是一個函數指針,指向一個函數。
?? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ?信號的處理的方式有如下三種:
?? ??? ??? ??? ??? ??? ??? ?a.?? ?自定義的處理函數
?? ??? ??? ??? ??? ??? ??? ??? ?這個處理函數為無返回值,但是帶一個int的參數(用來保存收到的
?? ??? ??? ??? ??? ??? ??? ??? ?那個信號的信號值)的函數。
?? ??? ??? ??? ??? ??? ??? ??? ?大概格式如下:
?? ??? ??? ??? ??? ??? ??? ??? ??? ?void my_sig_handler(int sig)
?? ??? ??? ??? ??? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?//函數的功能用戶自己實現
?? ??? ??? ??? ??? ??? ??? ??? ??? ?}
?? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ??? ?b.?? ?默認
?? ??? ??? ??? ??? ??? ??? ??? ?SIG_DFL:default,采用操作系統默認的處理方式
?? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ??? ?c. ?? ?忽略
?? ??? ??? ??? ??? ??? ??? ??? ?SIG_IGN:ignore,忽略該信號
?? ??? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ?練習:
?? ??? ??? ??? ?我們的程序,通??梢员绘I盤上的ctrl + c干掉
?? ??? ??? ??? ?能不能寫一個新的程序,ctrl + c不能干掉你呢?
?? ??? ??? ??? ?兩種方式:
?? ??? ??? ??? ??? ?一種直接忽略掉ctrl + c發過來的信號:
?? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?第二種方式:
?? ??? ??? ??? ??? ??? ?自定義的處理函數
?? ??? ??? ?
?? ??? ?5)?? ?pause
?? ??? ??? ?NAME
?? ??? ??? ??? ?pause - wait for signal
?? ??? ??? ?SYNOPSIS
?? ??? ??? ??? ?#include <unistd.h>
?? ??? ??? ??? ?int pause(void);
?? ??? ??? ??? ?讓進程停在那里,等待某個信號的到來,直到收到信號。
?? ??? ??? ??? ?當一個信號被捕獲,且信號處理函數返回后才返回,這種情況下返回-1。
?? ??? ??? ??? ?
?? ?(4)?? ?共享內存
?? ??? ?為了提高進程間通信的效率,有人就提出能不能讓多個進程共享一段內存,這段內存既是你的
?? ??? ?也是我的,你往這段內存空寫數據,實際上就是往我的內存中寫數據。
?? ??? ?那么我們就把這種進程間的通信方式稱之為共享內存。
?? ??? ?有點類似于我們的“共享文件夾”。
?? ??? ?
?? ??? ?這種方式比其他的IPC方式(像PIPE,FIFO,Message...)至少少copy了兩次,共享內存的效率最高。
?? ??? ?
?? ??? ?共享內存的實現方式:
?? ??? ??? ?在內核中開辟一塊共享內存,其他要通信的進程通過“映射”方式獲取這段共享內存的引用(指針)。
?? ??? ??? ?
?? ??? ?進程1可以映射這段內存,同時其他的進程(如P2)也可以映射這段內存,p1往這段內存中寫數據
?? ??? ?實際上就是往p2的進程地址空間中寫數據,反之亦然。
?? ??? ?System V IPC(msg/shm/sem)操作流程:
?? ??? ??? ?通過ftok()函數獲取 ----> System V IPC對象的key ---> 通過key創建或打開這個IPC
?? ??? ??? ?的設施(msg/shm/sem)----> 通過System V IPC提供的讀/寫函數接口交換數據 ---> 關閉設施
?? ??? ??? ?
?? ??? ??? ?
?? ??? ??? ?通過某個軟件訂房 ---> 獲取房卡 ---> ?進入房間 ---> 交換數據 ---> 打掃房間溜啦。。
?? ??? ??? ?
?? ??? ?System V共享內存的API函數:
?? ??? ??? ?a.?? ?ftok 用來創建一個System V IPC對象的key
?? ??? ??? ??? ?NAME
?? ??? ??? ??? ??? ?ftok - convert a pathname and a project identifier to a System V IPC key
?? ??? ??? ??? ?SYNOPSIS
?? ??? ??? ??? ??? ?#include <sys/types.h>
?? ??? ??? ??? ??? ?#include <sys/ipc.h>
?? ??? ??? ??? ??? ?key_t ftok(const char *pathname, int proj_id);
?? ??? ??? ??? ??? ??? ?pathname:一個文件系統中的路徑名(必須是真實存在的并且有權限訪問的)。
?? ??? ??? ??? ??? ??? ?proj_id:整數。這個參數存在的意義在于讓一個文件也能生成多個IPC key值。
?? ??? ??? ??? ??? ??? ??? ?ftok利用同一個文件最多可得到的IPC key鍵值為256個。
?? ??? ??? ??? ??? ??? ??? ?因為ftok只取proj_id值二進制的后8bits,即16進制的后兩位與文件信息
?? ??? ??? ??? ??? ??? ??? ?合成IPC Key鍵值。
?? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ?也就是說。ftok("/home",257) <--->ftok("/home",1);?? ?
?? ??? ??? ??? ??? ??? ??? ?生成的key值是一樣的。
?? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ?返回值:
?? ??? ??? ??? ??? ??? ??? ?成功生成一個唯一的System V IPC的key(類型為key_t --> int)
?? ??? ??? ??? ??? ??? ??? ?失敗返回-1,同時errno被設置。
?? ??? ??? ?
?? ??? ??? ??? ??? ?注意:?? ?
?? ??? ??? ??? ??? ??? ?key值其實就是一個32位的int,key的生成是根據pathname和proj_id來的
?? ??? ??? ??? ??? ??? ?具體其實是按照pathname指定的文件(目錄)的屬性,也就是說struct state
?? ??? ??? ??? ??? ??? ?這個結構體中的st_dev的低八位和st_ino成員變量的低十六位和proj_id的低百位
?? ??? ??? ??? ??? ??? ?組合而成的。
?? ??? ??? ??? ??? ??? ?
?? ??? ??? ?b.?? ?shmget
?? ??? ??? ??? ?NAME
?? ??? ??? ??? ??? ?shmget - allocates a System V shared memory segment
?? ??? ??? ??? ?SYNOPSIS
?? ??? ??? ??? ??? ?#include <sys/ipc.h>
?? ??? ??? ??? ??? ?#include <sys/shm.h>
?? ??? ??? ??? ??? ?int shmget(key_t key, size_t size, int shmflg);
?? ??? ??? ??? ??? ?shmget用來在內核中創建或打開一個System V共享內存
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ?key:System V IPC對象的key,一般由ftok的返回值獲得。
?? ??? ??? ??? ??? ??? ?size:以字節為單位指定共享內存區的大小
?? ??? ??? ??? ??? ??? ??? ?當實際操作為創建一個新的共享內存區時,必須指定一個不為0的size值
?? ??? ??? ??? ??? ??? ??? ?(PAGE_SIZE的整數倍,PAGE_SIZE:4K),如果實際操作為訪問一個已經存在
?? ??? ??? ??? ??? ??? ??? ?的共享內存區,那么size因為0
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ?shmflg:標志位
?? ??? ??? ??? ??? ??? ??? ?如果是創建共享內存:IPC_CREAT | 權限位(0666)
?? ??? ??? ??? ??? ??? ??? ?如果是打開共享內存: 0
?? ??? ??? ??? ??? ??? ??? ?注意:
?? ??? ??? ??? ??? ??? ??? ??? ?共享內存一旦創建,是隨內核的持續性的。其實你完全可以按照創建
?? ??? ??? ??? ??? ??? ??? ??? ?的方式去指定也可以,因為發現key對應的共享內存已經存在了,
?? ??? ??? ??? ??? ??? ??? ??? ?那么它就會自動忽略后面的選擇而直接打開。
?? ??? ??? ??? ??? ??? ??? ??? ?即使使用(創建)它的進程結束了 這塊共享內存也存在,除非顯示的去釋放它。
?? ??? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ?c.?? ?映射
?? ??? ??? ??? ?shmat
?? ??? ??? ??? ?NAME
?? ??? ??? ??? ??? ?shmat, shmdt - System V shared memory operations
?? ??? ??? ??? ?SYNOPSIS
?? ??? ??? ??? ??? ?#include <sys/types.h>
?? ??? ??? ??? ??? ?#include <sys/shm.h>
?? ??? ??? ??? ??? ?void *shmat(int shmid, const void *shmaddr, int shmflg);
?? ??? ??? ??? ??? ??? ?把內核的共享內存映射到進程空間中去
?? ??? ??? ??? ??? ??? ?shmid:要映射的共享內存區域的id(通過shmget獲取)
?? ??? ??? ??? ??? ??? ?shmaddr:指向要映射到進程的哪一個地址上去
?? ??? ??? ??? ??? ??? ??? ??? ?一般為NULL,表示由操作系統自行分配
?? ??? ??? ??? ??? ??? ?shmflg:SHM_RDONLY ? 只讀
?? ??? ??? ??? ??? ??? ??? ??? ?0?? ??? ??? ? 讀寫
?? ??? ??? ??? ??? ??? ?返回值:成功返回映射后的首地址,失敗返回NULL,同時errno被設置。
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?
?? ??? ??? ?d.?? ?解映射
?? ??? ??? ??? ?int shmdt(const void *shmaddr);
?? ??? ??? ??? ?shmdt用來解映射一端system v共享內存的。
?? ??? ??? ??? ??? ?shmaddr:要解映射的那個地址
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?返回值:成功返回0,失敗返回-1,同時errno被設置。
?? ??? ??? ??? ?
?? ??? ??? ?e.?? ?共享內存的刪除操作
?? ??? ??? ??? ?shmctl
?? ??? ??? ?
?? ??? ??? ??? ?NAME
?? ??? ??? ??? ??? ?shmctl - System V shared memory control
?? ??? ??? ??? ?SYNOPSIS
?? ??? ??? ??? ??? ?#include <sys/ipc.h>
?? ??? ??? ??? ??? ?#include <sys/shm.h>
?? ??? ??? ??? ??? ?int shmctl(int shmid, int cmd, struct shmid_ds *buf);
?? ??? ??? ??? ??? ??? ?shmid:要刪除的共享內存的ID
?? ??? ??? ??? ??? ??? ?cmd:IPC_RMID--->刪除一塊共享內存
?? ??? ??? ??? ??? ??? ?buf:NULL
?? ?
?? ?5)?? ?信號量
?? ??? ?如果有兩個或兩個以上的任務(進程/線程),去訪問同一個共享資源(硬件上的/軟件上的),那么
?? ??? ?我們必須要保證這個共享資源有序訪問,否則會產生不可預知的后果。
?? ??? ?
?? ??? ?怎么去解決這個問題呢?
?? ??? ??? ?造成這個問題的原因是主要是進程對同一個資源訪問不是有序的,就是說一個進程訪問一個
?? ??? ?資源時,其他進程同時也在訪問,那么他們之間就形成這種競爭的關系。
?? ??? ?
?? ??? ?所以我們需要對這個共享資源進行某種方式的保護,以使他被有序的訪問,以避免競爭。
?? ??? ?
?? ??? ?我們是因為并發--->競爭--->共享資源的非法訪問--->程序行為異常
?? ??? ?
?? ??? ?那么我們應該在保存并發的前提下,避免競爭,也就是說在多個進程訪問一個共享資源的時候,
?? ??? ?嚴格串行,那么這種機制我們就稱之為信號量機制。
?? ??? ?
?? ??? ?1.?? ?信號量機制
?? ??? ??? ?信號量(semaphore)是一種用于提供不同進程間或一個進程內部不同的線程間同步的一種機制。
?? ??? ??? ??? ?我們把進程/線程/任務稱之為叫并發的實體。
?? ??? ??? ??? ?同步:并發的實體間互相等待,相互制約,有序的,有條件的訪問共享資源。
?? ??? ??? ??? ?
?? ??? ??? ?所以信號量就是為了保護共享資源,讓共享資源有序訪問的一種機制。
?? ??? ??? ?
?? ??? ??? ?信號量是我們程序界最高尚的一種東西,因為他不是為了自己而存在的,而是為了別人
?? ??? ??? ?(它保護的對象->共享資源)而存在的,它相當于是一個保鏢。
?? ??? ??? ?
?? ??? ??? ?信號量不是用來傳輸數據的,而是用來協調各進程/線程工作的。
?? ??? ??? ?什么時候需要使用信號量呢?
?? ??? ??? ??? ?有保護的對象的時候,才需要信號量。
?? ??? ??? ?
?? ??? ?2.?? ?如何進行保護呢?
?? ??? ??? ?“保護”:讓這個被保護的對象(共享資源)有序的訪問,如:“互斥”
?? ??? ??? ?“互斥”:我在訪問的時候你不能訪問
?? ??? ??? ?
?? ??? ??? ?信號量機制其實是程序猿之間的一個約定,用來保護共享資源的
?? ??? ??? ?
?? ??? ??? ?比如說進程A和進程B,都要訪問一個互斥設備,那么我們可以用一個信號量來表示
?? ??? ??? ?能不能訪問該設備,然后每一個進程訪問該設備時,先去訪問信號量,如果能訪問
?? ??? ??? ?設備則先把信號量設為“NO”,然后再去訪問該互斥設備,訪問完互斥設備,
?? ??? ??? ?再把信號量設為“YES”。
?? ??? ??? ?那么我們把多個進程/線程可能同時訪問的資源稱之為共享資源
?? ??? ??? ?也稱之為臨界資源。
?? ??? ??? ?把訪問臨界資源的代碼,稱之為臨界區。
?? ??? ?3.?? ?信號量是如何實現的?
?? ??? ??? ?信號量本質上是一個整數,用來表示資源的數量
?? ??? ??? ?信號量-->0?? ??? ?表示資源不可用
?? ??? ??? ?信號量>0?? ??? ?表示資源可用
?? ??? ?
?? ??? ??? ?一個進程或線程可以在某個信號量上執行如下三個操作:
?? ??? ??? ?a.?? ?創建一個信號量
?? ??? ??? ??? ?在創建一個信號量的同時,我們還要求創建者指定信號量的初始值。
?? ??? ??? ??? ?初始值表示該信號量保護的共享資源可以同時被多少個任務所訪問。
?? ??? ??? ??? ?sem --> 5 表示可以有5個線程/進程同時訪問它所保護的共享資源
?? ??? ??? ??? ?sem --> 1 表示只有一個進程/線程可以同時訪問它所保護的共享資源
?? ??? ??? ??? ??? ?
?? ??? ??? ?b.?? ?等待一個信號量
?? ??? ??? ??? ?該操作會判斷這個信號量的值,如果其值<=0,那么就會等待(阻塞),一旦
?? ??? ??? ??? ?其值>0,這個時候,將它-1,接著訪問臨界資源。
?? ??? ??? ??? ?也就是程序在進入臨界區之前,必須對資源進行申請,這個操作稱之為P操作
?? ??? ??? ??? ?申請成功,資源數量(信號量)減少,申請失敗,要么等,要么走。
?? ??? ??? ??? ?
?? ??? ??? ?c.?? ?釋放一個信號量
?? ??? ??? ??? ?該操作將信號量的值+1。
?? ??? ??? ??? ?程序離開臨界區之后,必須釋放相應的資源,這個操作稱之為V操作
?? ??? ??? ??? ?釋放資源,資源的數量(信號量)增加。
?? ??? ??? ??? ?
?? ??? ??? ?總結:
?? ??? ??? ??? ?P操作為資源減操作,V操作為資源加操作。
?? ??? ??? ??? ?
?? ??? ??? ?兩者都是屬于原子操作:
?? ??? ??? ??? ?是指不會被調度機制打斷的操作,這種操作一旦開始,就一直運行到結束。
?? ??? ??? ??? ?也就是原子操作是不可被分隔的。
?? ??? ??? ??? ?
?? ??? ?思考:
?? ??? ??? ?1.?? ?現在有五個資源A.B.C.D.E需要保護,設計師決定用一個信號量S來同時保護這五個資源?
?? ??? ??? ??? ?請問這種設計有沒有什么問題?
?? ??? ??? ??? ?
?? ??? ??? ??? ?這種設計可以達到互斥訪問的目的,但是降低了并發度,因為A B C D E五個資源
?? ??? ??? ??? ?是可以同時訪問的。
?? ??? ??? ?2.?? ?現在有五個資源A.B.C.D.E需要保護,設計師決定用5個信號量s1,s2,s3,s4,s5來
?? ??? ??? ??? ?分別保護這五個資源。
?? ??? ??? ?3.?? ?有這樣一種情況:
?? ??? ??? ??? ?現在有一個進程p1需要同時訪問A和B,那么在訪問A和B之前,p1會先對s1和s2進行判斷
?? ??? ??? ??? ?能不能訪問,能訪問的話就進行P操作,如:P(s1) P(s2)。
?? ??? ??? ??? ?那會不會會有另外一個進程p2也要訪問A和B,那么訪問之前p2也要判斷,判斷完
?? ??? ??? ??? ?就訪問就P操作,如果進程2進行P操作的流程的P(s2) P(s1)
?? ??? ??? ??? ?就會造成死鎖的現象發生。
?? ??? ??? ??? ??
?? ??? ??? ??? ?解決死鎖的問題應該是進程p1和p2一旦獲取資源應該要同時對s1和s2進行P操作
?? ??? ??? ??? ?因為P操作是原子操作,所以要么s1.s2兩個都沒被上鎖,要么s1.s2都被上鎖。
?? ??? ??? ??? ?不會出現只上鎖一個的情況出現。
?? ??? ??? ??? ?
?? ??? ?4.?? ?信號量相關的API函數
?? ??? ??? ?信號量的使用流程:
?? ??? ??? ??? ?a.?? ?生成key--->ftok()
?? ??? ??? ??? ?b.?? ?創建或獲取信號量
?? ??? ??? ??? ?c.?? ?信號量值的初始化
?? ??? ??? ??? ?d.?? ?PV操作
?? ??? ??? ??? ?e.?? ?用戶刪除
?? ??? ??? ??? ?
?? ??? ??? ?信號量也分為兩種:
?? ??? ??? ??? ?System V 信號量和POSIX信號量
?? ??? ??? ?
?? ??? ??? ?4.1) System V 信號量(semaphore)
?? ??? ??? ??? ? System V 信號量分為兩種:
?? ??? ??? ??? ??? ?互斥信號量:
?? ??? ??? ??? ??? ??? ?該信號量的值要么是1,要么是0
?? ??? ??? ??? ??? ??? ?它所保護的共享資源同一時刻只允許一個任務去訪問它。
?? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?計數信號量:
?? ??? ??? ??? ??? ??? ?該信號量的值可以是>=1的值,它所保護的共享資源時允許多個
?? ??? ??? ??? ??? ??? ?任務同時訪問它。
?? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ?如果計數信號量的計數值為1,0,那么我們就可以把其看成是互斥信號量
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ?System V采用的是計數信號量集:
?? ??? ??? ??? ??? ?計數信號量集是由一個或者多個計數信號量組成的集合。
?? ??? ??? ??? ??? ?計數信號量集其實就是一個計數信號量的數組。
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ?而POSIX信號量采用的是計數信號量。
?? ??? ??? ??? ?
?? ??? ??? ??? ?1.?? ?semget()-->用來創建或打開一個System V信號量集
?? ??? ??? ??? ??? ?NAME
?? ??? ??? ??? ??? ??? ?semget - get a System V semaphore set identifier
?? ??? ??? ??? ??? ?SYNOPSIS
?? ??? ??? ??? ??? ??? ?#include <sys/types.h>
?? ??? ??? ??? ??? ??? ?#include <sys/ipc.h>
?? ??? ??? ??? ??? ??? ?#include <sys/sem.h>
?? ??? ??? ??? ??? ??? ?int semget(key_t key, int nsems, int semflg);
?? ??? ??? ??? ??? ??? ??? ?key:System V IPC對象的key(一般由ftok返回)
?? ??? ??? ??? ??? ??? ??? ?nsems:你要創建的信號量集中的信號量的個數
?? ??? ??? ??? ??? ??? ??? ??? ?如果我們不是去創建而是去打開一個已經存在的信號量集的話。
?? ??? ??? ??? ??? ??? ??? ??? ?此處的參數應該要指定為0.一旦創建完一個信號量集的話,
?? ??? ??? ??? ??? ??? ??? ??? ?其信號量的個數就不能改變啦。
?? ??? ??? ??? ??? ??? ??? ?semflg:
?? ??? ??? ??? ??? ??? ??? ??? ?(1) 創建 ---> IPC_CREAT | 0660?? ?
?? ??? ??? ??? ??? ??? ??? ??? ?(2)?? ?打開 ---> 0
?? ??? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ??? ?返回值:?? ?
?? ??? ??? ??? ??? ??? ??? ??? ?成功返回System V信號量集的ID,失敗返回-1,同時errno被設置。
?? ??? ??? ??? ??? ?注意:在一個新創建的信號量集中,信號量的值是不確定的
?? ??? ??? ??? ??? ?所以,我們在新創建一個信號量集后,需要馬上去指定信號量的初始值!
?? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ?2.?? ?semctl:控制操作(設置或獲取信號量集中某個或某些信號量的值)
?? ??? ??? ??? ??? ?NAME
?? ??? ??? ??? ??? ??? ?semctl - System V semaphore control operations
?? ??? ??? ??? ??? ?SYNOPSIS
?? ??? ??? ??? ??? ??? ?#include <sys/types.h>
?? ??? ??? ??? ??? ??? ?#include <sys/ipc.h>
?? ??? ??? ??? ??? ??? ?#include <sys/sem.h>
?? ??? ??? ??? ??? ??? ?int semctl(int semid, int semnum, int cmd, ...);
?? ??? ??? ??? ??? ??? ??? ?semid:要操作的信號量集的ID(semget的返回值)
?? ??? ??? ??? ??? ??? ??? ?semnum:要操作的信號量集中的哪個信號量,就是信號量數組的下標
?? ??? ??? ??? ??? ??? ??? ??? ?從0開始的,0,1,2,3,4,5......nsems-1。
?? ??? ??? ??? ??? ??? ??? ?cmd:命令號,常用的有:
?? ??? ??? ??? ??? ??? ??? ??? ?GETVAL:獲取第semnum個信號的值
?? ??? ??? ??? ??? ??? ??? ??? ?SETVAL:設置第semnum個信號的值
?? ??? ??? ??? ??? ??? ??? ??? ?GETALL:獲取這個信號量集中所有的信號量的值
?? ??? ??? ??? ??? ??? ??? ??? ?SETALL:設置這個信號量集中所有的信號量的值
?? ??? ??? ??? ??? ??? ??? ??? ?IPC_RMID:刪除這個信號量集
?? ??? ??? ??? ??? ??? ??? ??? ?....
?? ??? ??? ??? ??? ??? ??? ?針對第四個參數:
?? ??? ??? ??? ??? ??? ??? ??? ?cmd == IPC_RMID,第四個參數不要
?? ??? ??? ??? ??? ??? ??? ??? ??? ?如:
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?semctl(semid,0,IPC_RMID);
?? ??? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ??? ??? ?cmd == GETVAL
?? ??? ??? ??? ??? ??? ??? ??? ??? ?第四個參數也不要
?? ??? ??? ??? ??? ??? ??? ??? ??? ?函數返回值就是表示那個信號量的值
?? ??? ??? ??? ??? ??? ??? ??? ??? ?如:
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?int val = semctl(semid,1,GETVAL);
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?用來獲取semid指定的信號量集中第2個信號量的值
?? ??? ??? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ??? ??? ?cmd == SETVAL,第四個參數應該為一個int
?? ??? ??? ??? ??? ??? ??? ??? ??? ?表示要設置信號量的值。
?? ??? ??? ??? ??? ??? ??? ??? ??? ?如:
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?int sem_val = 1;
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?int r = semctl(semid,1,SETVAL,sem_val);
?? ??? ??? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ??? ??? ?cmd == GETALL
?? ??? ??? ??? ??? ??? ??? ??? ??? ?第四個參數,應該為一個unsigned short vals[]
?? ??? ??? ??? ??? ??? ??? ??? ??? ?這個數組的作用是用來保存獲取的每個信號量的值的
?? ??? ??? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ??? ??? ??? ?unsigned short vals[10];
?? ??? ??? ??? ??? ??? ??? ??? ??? ?semctl(semid,0,GETALL,vals);
?? ??? ??? ??? ??? ??? ??? ??? ??? ?//vals[0] 保存的就是信號量集中第一個信號量的值
?? ??? ??? ??? ??? ??? ??? ??? ??? ?.....
?? ??? ??? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ??? ??? ?cmd == SETALL
?? ??? ??? ??? ??? ??? ??? ??? ??? ?第四個參數應該為unsigned short vals[]
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?這個數組是用來設置每個信號量的值
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ??? ??? ??? ?如:
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?unsigned short vals[10] = {1,1,1,1,1,1,1,1,1,1};
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?int r = semctl(semid,0,SETALL,vals);
?? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ??? ?返回值
?? ??? ??? ??? ??? ??? ??? ??? ?根據不同的命令,semctl返回值的含義不一樣。
?? ??? ??? ??? ??? ??? ??? ??? ??? ?一般情況下,0-->成功?
?? ??? ??? ??? ??? ??? ??? ??? ??? ?-1---->失敗
?? ??? ??? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ?3.?? ?semop:System V 信號量P/V操作
?? ??? ??? ??? ??? ?NAME
?? ??? ??? ??? ??? ??? ?semop, semtimedop - System V semaphore operations
?? ??? ??? ??? ??? ?SYNOPSIS
?? ??? ??? ??? ??? ??? ?#include <sys/types.h>
?? ??? ??? ??? ??? ??? ?#include <sys/ipc.h>
?? ??? ??? ??? ??? ??? ?#include <sys/sem.h>
?? ??? ??? ??? ??? ??? ?int semop(int semid, struct sembuf *sops, size_t nsops);
?? ??? ??? ??? ??? ??? ??? ?semid:要操作的是哪個信號量集
?? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ??? ?在System V的信號量P/V操作,用一個結構體struct sembuf來描述
?? ??? ??? ??? ??? ??? ??? ?P/V操作的。這個結構體可以在man semop中看到
?? ??? ??? ??? ??? ??? ??? ??? ?struct sembuf
?? ??? ??? ??? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ??? ??? ??? ?unsigned short sem_num; ?/* semaphore number */
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?要進行P/V操作的信號量在信號量集中的編號(下標)?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ??? ??? ??? ?short sem_op; ? /* semaphore operation */
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?> 0 ?--> V操作 unlock
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?= 0 ?--> 試一試,看是否會阻塞(是否能訪問)
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?< 0 ?--> P操作 lock
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?semval(信號量的值) = 原semval + sem_op;
?? ??? ??? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ??? ??? ??? ?short sem_flg; ?/* operation flags */
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?0?? ?: 默認,如果P操作獲取不了,則會阻塞
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?IPC_NOWAIT : 非阻塞,不等待
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?如果是P操作,能獲取則獲取,不能獲取則走人(返回-1)
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?SEM_UNDO:撤銷。
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?為了防止進程帶鎖退出的。
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?如果進程突然中止,自動釋放資源。
?? ??? ??? ??? ??? ??? ??? ??? ?};
?? ??? ??? ??? ??? ??? ??? ?sops:struct sembuf的數組?? ?
?? ??? ??? ??? ??? ??? ??? ?nsops:第二個參數sops數組中的元素個數
?? ??? ??? ??? ??? ??? ??? ??? ?或者說信號量集中要進行PV操作的信號量的個數
?? ??? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ??? ?返回值:成功返回0,失敗返回-1,同時errno被設置。
?? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ?注意:?? ?
?? ??? ??? ??? ??? ??? ??? ?semop可能會阻塞當前進程/線程,如果是P操作,獲取不了的時候
?? ??? ??? ??? ??? ??? ??? ?且IPC_NOWAIT沒有設置的時候,會等待。
?? ??? ??? ??? ??? ??? ?int semtimedop(int semid, struct sembuf *sops, size_t nsops,
?? ??? ??? ??? ??? ??? ??? ??? ? ?const struct timespec *timeout);
?? ??? ??? ??? ??? ??? ??? ?限時等待。
?? ??? ??? ??? ??? ??? ??? ?第四個參數:
?? ??? ??? ??? ??? ??? ??? ??? ?timeout:描述“限時”?
?? ??? ??? ??? ??? ??? ??? ??? ??? ?如果在這段時間內,沒有等到,也返回。
?? ??? ??? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ??? ??? ?struct timespec {
?? ??? ??? ??? ??? ??? ??? ??? ??? ?long ? ?tv_sec; ? ? ? ? /* seconds */
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?//秒數
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ??? ??? ??? ?long ? ?tv_nsec; ? ? ? ?/* nanoseconds */
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?//納秒 ? ? ?
?? ??? ??? ??? ??? ??? ??? ??? ?};
?? ??? ??? ??? ??? ??? ??? ??? ?struct timespec tv;
?? ??? ??? ??? ??? ??? ??? ??? ?tv.tv_sec = 5;
?? ??? ??? ??? ??? ??? ??? ??? ?tv.tv_nsec = 0;
?? ??? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ?4.2)POSIX信號量--->單個信號量,不像System V的信號量集
?? ??? ??? ??? ?POSIX信號量原理和System V信號量是一樣的。
?? ??? ??? ??? ?POSIX信號量分為兩種:
?? ??? ??? ??? ??? ?1.?? ?有名信號量
?? ??? ??? ??? ??? ??? ?在文件系統中有一個名字,即有一個對應的inode結點,但是信號量的
?? ??? ??? ??? ??? ??? ?文件內容(整數值)卻是在內核中的。
?? ??? ??? ??? ??? ??? ?一般可以用于任意線程和進程之間。
?? ??? ??? ??? ??? ??? ?一般有名信號量的操作由:
?? ??? ??? ??? ??? ??? ??? ?sem_open
?? ??? ??? ??? ??? ??? ??? ?sem_wait
?? ??? ??? ??? ??? ??? ??? ?sem_post
?? ??? ??? ??? ??? ??? ??? ?sem_close
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?2.?? ?無名信號量
?? ??? ??? ??? ??? ??? ?沒有名字,無名信號量存在于內存中
?? ??? ??? ??? ??? ??? ?一般用于線程間和有親緣關系的進程間。
?? ??? ??? ??? ??? ??? ?無名信號量的操作一般有:
?? ??? ??? ??? ??? ??? ??? ?sem_init
?? ??? ??? ??? ??? ??? ??? ?sem_wait
?? ??? ??? ??? ??? ??? ??? ?sem_post
?? ??? ??? ??? ??? ??? ??? ?sem_destroy
?? ??? ??? ??? ?操作流程:
?? ??? ??? ??? ??? ?1.?? ?初始化
?? ??? ??? ??? ??? ?2.?? ?PV操作
?? ??? ??? ??? ??? ?3.?? ?銷毀?? ?
?? ??? ??? ??? ?相關的API函數?? ??? ?
?? ??? ??? ??? ?a.?? ?創建或打開一個POSIX信號量
?? ??? ??? ??? ??? ?有名信號量:
?? ??? ??? ??? ??? ?NAME
?? ??? ??? ??? ??? ??? ?sem_open - initialize and open a named semaphore
?? ??? ??? ??? ??? ?SYNOPSIS
?? ??? ??? ??? ??? ??? ?#include <fcntl.h> ? ? ? ? ? /* For O_* constants */
?? ??? ??? ??? ??? ??? ?#include <sys/stat.h> ? ? ? ?/* For mode constants */
?? ??? ??? ??? ??? ??? ?#include <semaphore.h>
?? ??? ??? ??? ??? ??? ?sem_t *sem_open(const char *name, int oflag);
?? ??? ??? ??? ??? ??? ?sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);
?? ??? ??? ??? ??? ??? ?Link with -pthread.
?? ??? ??? ??? ??? ??? ?sem_t *sem_open(const char *name, int oflag);
?? ??? ??? ??? ??? ??? ?name : 要創建或打開的POSIX有名信號量在文件系統中的路徑名。
?? ??? ??? ??? ??? ??? ??? ?要求以“/”開頭的路徑名(路徑名中只能有一個/)
?? ??? ??? ??? ??? ??? ??? ??? ?如:
?? ??? ??? ??? ??? ??? ??? ??? ??? ?"/test.sem"
?? ??? ??? ??? ??? ??? ??? ??? ?也就是說你的有名信號量必須放在根目錄下。
?? ??? ??? ??? ??? ??? ?
?? ?
?? ??? ??? ??? ??? ??? ?oflag: (1)?? ?打開 0
?? ??? ??? ??? ??? ??? ??? ??? ?(2) 創建:O_CREAT
?? ??? ??? ??? ??? ??? ??? ??? ?如果你是打開一個有名信號量的話,則只需要兩個參數
?? ??? ??? ??? ??? ??? ??? ??? ?如果你是創建一個有名信號量的話,則需要四個參數
?? ??? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ?mode:?? ?創建的權限位,有兩種方式指定:
?? ??? ??? ??? ??? ??? ??? ??? ?(1)?? ?S_IRUSR....
?? ??? ??? ??? ??? ??? ??? ??? ?(2) 0664...
?? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ?value: 指定創建的有名信號量的初始值
?? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ?返回值:成功返回一個sem_t的指針,指向POSIX有名信號量
?? ??? ??? ??? ??? ??? ??? ??? ?失敗返回一個SEM_FAILED,同時errno被設置。
?? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?無名信號量:
?? ??? ??? ??? ??? ?NAME
?? ??? ??? ??? ??? ??? ?sem_init - initialize an unnamed semaphore
?? ??? ??? ??? ??? ?SYNOPSIS
?? ??? ??? ??? ??? ??? ?#include <semaphore.h>
?? ??? ??? ??? ??? ??? ?int sem_init(sem_t *sem, int pshared, unsigned int value);
?? ??? ??? ??? ??? ??? ?Link with -pthread.
?? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ?sem:指向要初始化的無名信號量
?? ??? ??? ??? ??? ??? ??? ?一般我們會先定義或分配一個無名信號量sem_t
?? ??? ??? ??? ??? ??? ??? ??? ?sem_t *psem = malloc(sizeof(sem_t));
?? ??? ??? ??? ??? ??? ?pshared:
?? ??? ??? ??? ??? ??? ??? ?該無名信號量的共享方式
?? ??? ??? ??? ??? ??? ??? ?0:進程內部的線程共享
?? ??? ??? ??? ??? ??? ??? ??? ?也就是說sem指向的地址是進程內部的地址
?? ??? ??? ??? ??? ??? ??? ??? ?如malloc開辟出來的內存
?? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ??? ?1:不同進程間共享
?? ??? ??? ??? ??? ??? ??? ??? ?也就是說sem指向的地址是內核共享內存區域
?? ??? ??? ??? ??? ??? ??? ?一般設置為0。?? ?
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ?value:指定無名信號量的初始值
?? ??? ??? ?
?? ??? ??? ??? ??? ??? ?一般情況下,無名信號量我們用于線程間或有親緣關系的進程間。
?? ??? ??? ?
?? ??? ??? ?b.?? ?POSIX信號量PV操作
?? ??? ??? ??? ?P操作?
?? ??? ??? ??? ?NAME
?? ??? ??? ??? ??? ?sem_wait, sem_timedwait, sem_trywait - lock a semaphore
?? ??? ??? ??? ?SYNOPSIS
?? ??? ??? ??? ??? ?#include <semaphore.h>
?? ??? ??? ??? ??? ?int sem_wait(sem_t *sem);
?? ??? ??? ??? ??? ??? ?死等-->此函數會阻塞等待
?? ??? ??? ??? ??? ??? ?返回值:返回0獲取了該信號量,返回-1,出錯了,同時errno被設置。
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?int sem_trywait(sem_t *sem);
?? ??? ??? ??? ??? ??? ?非阻塞版本--->能獲取則獲取,不能獲取則走人(返回-1)
?? ??? ??? ??? ??? ??? ?返回值:
?? ??? ??? ??? ??? ??? ??? ?返回0表示獲取了該信號量,返回-1,出錯,同時errbo被設置。
?? ??? ??? ??? ??? ?int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
?? ??? ??? ??? ??? ??? ?限時等待--->等一段時間后還是獲取不了的話,則返回-1
?? ??? ??? ??? ??? ??? ??? ?struct timespec {
?? ??? ??? ??? ??? ??? ??? ??? ?long ? ?tv_sec; ? ? ? ? /* seconds */
?? ??? ??? ??? ??? ??? ??? ??? ??? ?//秒數
?? ??? ??? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ??? ??? ?long ? ?tv_nsec; ? ? ? ?/* nanoseconds */
?? ??? ??? ??? ??? ??? ??? ??? ??? ?//納秒 ? ? ?
?? ??? ??? ??? ??? ??? ??? ?};
?? ??? ??? ??? ??? ??? ?abs_timeout:指定等待超時的絕對時間。
?? ??? ??? ??? ??? ??? ??? ?絕對時間:
?? ??? ??? ??? ??? ??? ??? ?相對時間:
?? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ?當前的時間獲取:
?? ??? ??? ??? ??? ??? ??? ?int clock_gettime(clockid_t clk_id, struct timespec *tp);
?? ??? ??? ??? ??? ??? ??? ??? ?例子:?? ?
?? ??? ??? ??? ??? ??? ??? ??? ??? ?//獲取當前時間保存起來
?? ??? ??? ??? ??? ??? ??? ??? ??? ?struct timespec ts;
?? ??? ??? ??? ??? ??? ??? ??? ??? ?clock_gettime(CLOCK_REALTIME,&ts);
?? ??? ??? ??? ??? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ??? ??? ??? ?//假設我現在想等待15s:30ms
?? ??? ??? ??? ??? ??? ??? ??? ??? ?ts.tv_sec += 15;?? ??? ?//秒鐘數加15?? ?
?? ??? ??? ??? ??? ??? ??? ??? ??? ?ts.tv_nsec += 30000000;?? ?//納秒數加30000000即30ms
?? ??? ??? ??? ??? ??? ??? ??? ??? ?if(ts.tv_nsec >= 1000000000)//納秒有可能會超過1s
?? ??? ??? ??? ??? ??? ??? ??? ??? ?{
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?ts.tv_sec += 1;
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?ts.tv_nsec -= 1000000000;
?? ??? ??? ??? ??? ??? ??? ??? ??? ?}
?? ??? ??? ??? ??? ??? ??? ??? ??? ?int ret = sem_timedwait(sem,&ts);
?? ??? ??? ??? ??? ?Link with -pthread.
?? ??? ??? ?
?? ??? ??? ??? ?V操作
?? ??? ??? ??? ?NAME
?? ??? ??? ??? ??? ?sem_post - unlock a semaphore
?? ??? ??? ??? ?SYNOPSIS
?? ??? ??? ??? ??? ?#include <semaphore.h>
?? ??? ??? ??? ??? ?int sem_post(sem_t *sem);
?? ??? ??? ??? ??? ?Link with -pthread.
?? ??? ??? ??? ??? ?sem_post用來釋放sem指定的POSIX信號量。
?? ??? ??? ?
?? ??? ??? ?c.?? ?POSIX信號量的關閉和刪除操作
?? ??? ??? ??? ?有名信號量的關閉操作:
?? ??? ??? ??? ??? ?sem_close:用來關閉一個POSIX有名信號量
?? ??? ??? ??? ??? ?sem_unlink:用來刪除一個POSIX有名信號量
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ?NAME
?? ??? ??? ??? ??? ??? ?sem_close - close a named semaphore
?? ??? ??? ??? ??? ?SYNOPSIS
?? ??? ??? ??? ??? ??? ?#include <semaphore.h>
?? ??? ??? ??? ??? ??? ?int sem_close(sem_t *sem);
?? ??? ??? ??? ??? ??? ?Link with -pthread.
?? ??? ??? ??? ??? ?NAME
?? ??? ??? ??? ??? ??? ?sem_unlink - remove a named semaphore
?? ??? ??? ??? ??? ?SYNOPSIS
?? ??? ??? ??? ??? ??? ?#include <semaphore.h>
?? ??? ??? ??? ??? ??? ?int sem_unlink(const char *name);
?? ??? ??? ??? ??? ??? ?Link with -pthread.
?? ??? ??? ??? ??? ??? ?name:要刪除的POSIX有名信號量的路徑名
?? ??? ??? ??? ??? ??? ?返回值:成功返回0,失敗返回-1,同時errno被設置。
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ?無名信號量的銷毀操作:?? ?
?? ??? ??? ??? ??? ?NAME
?? ??? ??? ??? ??? ??? ?sem_destroy - destroy an unnamed semaphore
?? ??? ??? ??? ??? ?SYNOPSIS
?? ??? ??? ??? ??? ??? ?#include <semaphore.h>
?? ??? ??? ??? ??? ??? ?int sem_destroy(sem_t *sem);
?? ??? ??? ??? ??? ??? ?Link with -pthread.
?? ??? ??? ??? ??? ?
?? ??? ??? ??? ??? ??? ?
?? ??? ??? ?NAME
?? ??? ??? ??? ?sem_getvalue - get the value of a semaphore
?? ??? ??? ?SYNOPSIS
?? ??? ??? ??? ?#include <semaphore.h>
?? ??? ??? ??? ?int sem_getvalue(sem_t *sem, int *sval);
?? ??? ??? ??? ?Link with -pthread.
?? ??? ??? ??? ?
?? ??? ??? ??? ?sem :sem_t指針,表示要獲取哪個信號量的值
?? ??? ??? ??? ?sval:int*,用來保存獲取到的信號量的值
?? ??? ??? ??? ?返回值:成功返回0,失敗返回-1,同時errno被設置。
?? ??? ??? ??? ??? ?
?? ??? ?信號量一般情況下用來保護共享資源,但是信號量可以有其他的意外作用?? ?
?? ??? ??? ?如:我們fork一個子進程,想讓子進程先執行。
?? ??? ??? ?
?? ??? ??? ??? ??? ?
?? ??? ?作業:
?? ??? ??? ?使用信號量和共享內存,實現文件的傳輸
?? ??? ??? ?
總結