天天看點

命名管道與信号sigset_t

命名管道

管道是指程序直接進行交換資料的通道,可分為普通管道(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、即使沒有程序使用,但他們仍然存在,是以必須考慮在程序終止時有沒有釋放得信号量。

      以上的三個特性就導緻了信号使用的複雜性。