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