命名管道
管道是指進程直接進行交換數據的通道,可分爲普通管道(pipe)和命名管道(FIFO),他們都是通過內核緩衝區按先進先出的方式進行數據傳輸,管道一端順序地寫入數據,另一端順序地讀入數據,管道最大的一個特點就是半雙工性,數據只能向一個方向流動,如果需要進行雙方的通信時,則需要建立兩個管道。
管道和命名管道是最早的進程間的通信機制之一,其中管道用於相互關聯的進程之間進行通信,比如父子進程。爲了克服必須在具有親緣關係的進程之間才能通信,於是產生了命名管道——即有名字的管道。
命名管道使得沒有關係的進程之間也可以進行通信,命名管道提供一個與之關聯的路徑名,因此一個進程不需要與命名管道的創建具有相同的祖先,就可以通過該命名管道與之進行進程間的通信(只要可以訪問該路勁名),實際上,命名管道就是利用建立於文件系統的特殊文件,以FIFO的文件形式存在於文件系統中,永久保存相關信息,,因此不同的進程可以打開命名管道進行讀寫,從而實現通信。
對於命名管道的操作與文件操作非常相似,對文件操作中使用的函數read(),write(),close()等函數都可以是用來對管道進行操作。
命名管道的創建:
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int mkfifo(const char *pathname, mode_t mode);
//mode S_IRUSR S_IWUSR
//mode O_CREAT | O_EXCL
if (access(write_fifo_name,F_OK) == -1)
{
//mkfifo(write_fifo_name, S_IRUSR|S_IWUSR) == -1; //創建命名管道
}
該函數的第一個參數是一個普通的路徑名,也就是創建後FIFO的名字,第二個參數與打開普通的open()函數中的mode參數相同,如果mkfifo的第一個參數是一個已經存在的路徑名時,會返回EEXIST錯誤,所以一般典型的調用代碼首先會檢查是否返回該錯誤,如果確實返回該錯誤,那麼只要調用打開FIFO的函數就可以了。
管道僅需要創建而不需要打開,因爲使用它們的進程通過繼承獲得了管道的文件描述符,但命名管道則需要打開,因爲使用它們的進程可以沒有任何關係,對命名管道的打開通常使用文件打開函數open().
write_fd = open(write_fifo_name,O_WRONLY) == -1;
FIFO 常用的打開方式:
O_RDONLY:以只讀並且阻塞的方式打開,如果已經有相應進程爲寫而打開該FIFO,則打開操作將成功返回,否則,可能阻塞直到有相應進程爲寫而打開該FIFO。
O_WRONLY:以只寫並且阻塞的方式打開,如果已經有相應進程爲讀而打開該FIFO,則打開操作將成功返回,否則,可能阻塞直到有相應進程爲讀而打開該FIFO。
O_RDONLY|O_NONBLOCK:以只讀並且非阻塞得方式打開,無論是否已經具有相應進程爲寫進程而打開該FIFO,均立即成功返回。
O_WRONLY|O_NONBLOCK:以只寫並且非阻塞得方式打開,立即返回,如果已經有相應進程爲讀而打開該FIFO,則可以根據返回得文件描述符進行寫操作,如果沒有進程以只讀方式打開,則返回錯誤-1.
如果以O_RDONLY模式打開FIFO,且已經有相應進程以寫模式打開FIFO,那麼打開操作將成功返回,否則,打開該FIFO的進程將可能被阻塞直到有相應進程以寫模式打開FIFO(當前打開操作設置了阻塞標誌(O_NONBKOCK))或成功返回(當前打開操作沒有設置阻塞標誌)。
如果以O_WRONLY模式打開FIFO;……
如果有進程以寫模式打開了FIFO,但該FIFO中沒有數據,那麼讀FIFO的進程將被阻塞(打開操作設置了阻塞標誌),或返回-1(當前打開操作沒有設置阻塞標誌)
如果打開的FIFO設置了阻塞標誌,對於讀操作來說,造成阻塞的原因有兩種:當前FIFO內有數據,但有其他進程在讀這些數據,另外就是FIFO內沒有數據,解除阻塞的則是FIFO中有新的數據寫入(不管寫入的數據量有多少,也不管讀操作請求多少數據,都將解阻塞)
以讀模式打開的FIFO的阻塞標誌只對打開進程的第一個讀操作有效,如果該進程有多個讀操作序列,則在第一個讀操作被喚醒並完成讀操作後,其他將要執行的讀操作將不再阻塞,即使在執行讀操作時,FIFO中沒有數據也一樣不再阻塞(此時,讀操作返回0)
如果沒有進程以寫模式打開FIFO,則設置了阻塞標誌的讀操作會阻塞。
如果FIFO中有數據,則設置了阻塞標誌的讀操作不會因爲FIFO中的字節數小於請求讀的字節數而阻塞,此時,讀操作會返回FIFO中現有的數據量。
如果寫打開時設置了阻塞標誌,一個進程對FIFO的寫操作可能被阻塞,當要寫入FIFO的數據量 不大於PIPE_BUF時,Linux將保證寫入的原子性,當FIFO的空閒緩衝區不足以容納要寫入的字節數時,寫進程將被阻塞,直到緩衝區中能夠容納要寫入的字節數時才被喚醒寫入沒有寫入的數據字節,當要寫入FIFO的數據量大於PIPE_BUF時,Linux將不再保證寫入的原子性,FIFO緩衝區一有空閒區域,寫進程就會試圖向管道寫入數據,寫操作在寫完所有請求寫的數據後返回。
如果寫打開時沒有設置阻塞標誌,當要寫入FIFO的數據量大於PIPE_BUF時,Linux將不再保證寫入原子性,在寫滿所有FIFO空閒緩衝區後,寫操作返回,當要寫入FIFO的數據量不大於PIPE_BUF時,Linux將保證寫入的原子性,如果當前FIFO空想緩衝區能夠容納請求寫入的字節數,寫完成後成功返回,如果當前FIFO空閒緩衝區不能容納請求寫入的字節數,則返回錯誤。
信號
信號是由單個詞組成的消息,比如指示燈是一個信號,眼神是一個信號,裁判手勢也是一個信號,這些事物和事件本身不是消息,而他們的語意所表達的內容纔是消息。在Linux中,經常我們按鍵時內核會向當前正在運行的進程發送一箇中斷信號,這個信號和一個數字編碼相對應。
信號來源:
● 用戶——用戶通過輸入Ctrl+C等按鍵,請求內核產生信號
● 內核——當內核執行出現錯誤時,內核給進程發送一個信號,
● 進程——一個進程可以通過系統調用kill給另外一個進程發送信號,同時,一個進程也可以和另外一個進程通過信號進行通信。
信號的產生:
信號大概的來源可以分爲兩類,一是硬件原因,一是軟件原因。
最常用的4個發出信號系統函數是kill(),raise(),alarm(),setitimer()
其中 int kill(pid_t pid, int sig)中的pid說明爲:
pid > 0 將信號傳給進程號爲PID的進程
pid = 0 將信號傳給和目前進程相同進程組得所有進程
pid = -1 將信號廣播傳送給系統內所有得進程(不包括自己)
pid < 0 將信號傳給進程號爲pid絕對值得所有進程
信號操作:
操作信號最常用的方法就是信號屏蔽,信號屏蔽一般用到的函數爲:
sigset_t
信號集及信號集操作函數:信號集被定義爲一種數據類型:
typedef struct {
unsigned long sig[_NSIG_WORDS];
} sigset_t
#define _NSIG 64
#define _NSIG_BPW 32
#define _NSIG_WORDS (_NSIG / _NSIG_BPW)
信號集用來描述信號的集合,linux所支持的所有信號可以全部或部分的出現在信號集中,主要與信號阻塞相關函數配合使用。下面是爲信號集操作定義的相關函數:
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signum)
int sigdelset(sigset_t *set, int signum);
int sigismember(const sigset_t *set, int signum);
int sigaction( int sig, const struct sigaction *act,struct sigaction *oact );
頭文件
#include <signal.h>
sigemptyset(sigset_t *set)初始化由set指定的信號集,信號集裏面的所有信號被清空;
sigfillset(sigset_t *set)調用該函數後,set指向的信號集中將包含linux支持的64種信號;
sigaddset(sigset_t *set, int signum)在set指向的信號集中加入signum信號;
sigdelset(sigset_t *set, int signum)在set指向的信號集中刪除signum信號;
sigismember(const sigset_t *set, int signum)判定信號signum是否在set指向的信號集中。
int sigaction( int sig, const struct sigaction *act,struct sigaction *oact )檢查、修改和指定信號相關聯的信號響應。
#include<signal.h>.
Int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
此函數是用來檢測和改變信號行爲且功能強大的一個函數。
Struct sigaction
{
Void (* sa_handler) (int);
Sigset_t sa_mask;
Int sa_flags;
Void (* sa_reatorer)(void);
}
信號量
信號量(Semaphore)是一個特殊得整數值,主要用來控制多個進程對臨界資源的互斥訪問,進程根據信號量來判斷是否有可供訪問得資源,要注意與信號得區別,信號是一種處理異步事件的方法,而信號量是一種進程同步機制,信號與信號量是兩個不同得東西。
信號量是一個計數器,可用於同步多進程對共享數據對象得訪問,爲了獲得共享資源,進程需要執行以下操作:
1、測試控制該資源的信號量
2、若此信號量的值爲正,則進程可以使用該資源,進程將信號量值減1,表示它使用了一個資源單位
3、若此信號量的值爲0,則進程進入睡眠狀態,直至信號量值大於0。當進程被喚醒後,它返回至第1布。
當進程不再使用由一個信號量控制得共享資源時,該信號量值增1.如果有進程正在睡眠以等待此信號量,則喚醒他們,爲了正確地實現信號量,信號量值得測試及減1操作應當是原子操作,爲此,信號量通常是在內核中實現得。
常用的信號量一般初始值爲1,只控制單個資源,有時也稱互斥鎖,但是,信號量得初值可以是任意一正值,該值說明有多少個共享資源單位可供共享應用,信號量有以下3個特性:
1、信號量並非是一個非負值,而必須將信號量定義爲含有一個或多個信號量值得集合,當創建一個信號量時,要指定該集合中的各個值。
2、創建信號量對其賦初值分開,這是一個致命弱點,因爲不能原子地創建一個信號量集合,並且對該集合中的所有值賦初值。
3、即使沒有進程使用,但他們仍然存在,因此必須考慮在進程終止時有沒有釋放得信號量。
以上的三個特性就導致了信號使用的複雜性。