進程間IPC機制

Linux進程間通信類型:

1.信號機制   發送信號

2.管道機制   讀者-寫者

3.消息隊列   消息緩衝隊列通信機制,傳送小量數據

4 .共享內存   傳送大量數據,鎖機制

5.信號量     PV操作,進程間同步互斥,生產者-消費者,哲學家進餐等。

 

 

 

 

現在linux使用的進程間通信方式:

(1)      管道(pipe)和有名管道(FIFO 讀者-寫者

(2)      信號(signal                  發送信號

(3)      消息隊列                      傳送小量數據,消息緩衝隊列通信機制

(4)      共享內存                      傳送大量數據,鎖機制

(5)      信號量                        最重要

(6)      套接字(socket

 

 

管道通信

普通的Linux shell都允許重定向,而重定向使用的就是管道。例如:ps | grep vsftpd

管道是單向的、先進先出的、無結構的、固定大小的字節流,它把一個進程的標準輸出和另一個進程的標準輸入連接在一起。寫進程在管道的尾端寫入數據,讀進程在管道的首端讀出數據。數據讀出後將從管道中移走,其它讀進程都不能再讀到這些數據。管道提供了簡單的流控制機制。進程試圖讀空管道時,在有數據寫入管道前,進程將一直阻塞。同樣,管道已經滿時,進程再試圖寫管道,在其它進程從管道中移走數據之前,寫進程將一直阻塞。

管道主要用於不同進程間通信。

1.管道創建與關閉

創建一個簡單的管道,可以使用系統調用pipe( )。它接受一個參數,也就是一個包括兩個整數的數組。如果系統調用成功,此數組將包括管道使用的兩個文件描述符。創建一個管道之後,一般情況下進程將產生一個新的進程。

系統調用:pipe( )

原型:int pipe( int fd[2] )

返回值:如果系統調用成功,返回0。如果系統調用失敗返回- 1

            errno = EMFILE (沒有空閒的文件描述符)

                         EMFILE (系統文件表已滿)

                         EFAULT (fd數組無效)

 

注意:fd[0]用於讀取管道,fd[1]用於寫入管道。

1 linux中管道與文件描述符的關係

 

#include <unistd.h>

#include <errno.h>

#include <stdio.h>

#include <stdlib.h>

int main()

{

        int pipe_fd[2];

        if(pipe(pipe_fd)<0)

{

            printf("pipe create error/n");

            return -1;

        }

        else

                  printf("pipe create success/n");

        close(pipe_fd[0]);          /*  */

        close(pipe_fd[1]);          /*  */

}

注:管道創建在內核中,命名管道創建在文件系統中

關閉後沒有了;關閉後內容沒了,剩下空殼

2.管道讀寫

管道主要用於不同進程間通信。實際上,通常先創建一個管道,再通過fork函數創建一個子進程。

 

 

 

2父子進程管道的文件描述符對應關係

子進程寫入和父進程讀的命名管道:

3 關閉父進程fd[1]子進程[0]

3.管道讀寫注意事項

可以通過打開兩個管道來創建一個雙向的管道。但需要在子進程中正確地設置文件描述符。

必須在系統調用fork( )中調用pipe( ),否則子進程將不會繼承文件描述符。

當使用半雙工管道時,任何關聯的進程都必須共享一個相關的祖先進程。因爲管道存在於系統內核之中,所以任何不在創建管道的進程的祖先進程之中的進程都將無法尋址它。而在命名管道中卻不是這樣。注:管道只用於父子進程間。

管道實例見:pipe_rw.c

#include <unistd.h>

#include <sys/types.h>

#include <errno.h>

#include <stdio.h>

#include <stdlib.h>

 

int main()

{

        int pipe_fd[2];

        pid_t pid;

        char buf_r[100];

        char* p_wbuf;

        int r_num;

        memset(buf_r,0,sizeof(buf_r));

        if(pipe(pipe_fd)<0)

        {

        printf("pipe create error/n");

        return -1;

        }

        if((pid=fork())==0)  在子進程裏,子進程讀

        {

                  printf("/n");

                  close(pipe_fd[1]);

                  sleep(2);

                  if((r_num=read(pipe_fd[0],buf_r,100))>0){

                           printf(   "%d numbers read from the pipe is %s/n",r_num,buf_r);

                  }       

                  close(pipe_fd[0]);

                  exit(0);

      }

        else if(pid>0)    在父進程裏,父進程寫

        {

                  close(pipe_fd[0]);

                  if(write(pipe_fd[1],"Hello",5)!=-1)

                           printf("parent write1 success!/n");

                  if(write(pipe_fd[1]," Pipe",5)!=-1)

                           printf("parent write2 success!/n");

                  close(pipe_fd[1]);

                  sleep(3);

                  waitpid(pid,NULL,0); 在父進程中等待某個進程結束,pid是等待結束子進程的ID

                  exit(0);

        }

}

 

程序功能:子進程讀,父進程寫

 

 

 

 

4.標準流管道

linux中文件操作有文件流的標準I/O一樣,管道的操作也支持基於文件流的模式。接口函數如下

庫函數:popen();

原型: FILE *popen ( char *command, char *type);

        返回值:如果成功,返回一個新的文件流。如果無法創建進程或者管道,返回NULL

管道中數據流的方向是由第二個參數type控制的。此參數可以是r或者w,分別代表讀或寫。但不能同時爲讀和寫。在Linux系統下,管道將會以參數type中第一個字符代表的方式打開。所以,如果你在參數type中寫入rw,管道將會以讀的方式打開。

 

使用popen()創建的管道必須使用pclose( )關閉。其實,popen/pclose和標準文件輸入/輸出流中的fopen() / fclose()十分相似。

庫函數: pclose();

原型: int pclose( FILE *stream );

返回值:返回系統調用wait4( )的狀態。

如果stream無效,或者系統調用wait4( )失敗,則返回 -1

注意此庫函數等待管道進程運行結束,然後關閉文件流。

庫函數pclose( )在使用popen( )創建的進程上執行wait4( )函數。當它返回時,它將破壞管道和文件系統。

 

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <fcntl.h>

#define BUFSIZE 1024

int main()

{

        FILE *fp;

        char *cmd = "ps -ef";

        char buf[BUFSIZE];

        buf[BUFSIZE] = '/0';

        if((fp=popen(cmd,"r"))==NULL)

                  perror("popen");

        while((fgets(buf,BUFSIZE,fp))!=NULL)

                  printf("%s",buf);

        pclose(fp);

        exit(0);

}

 

5.命名管道(FIFO

5.1基本概念

命名管道和一般的管道基本相同,但也有一些顯著的不同:

n 命名管道是在文件系統中作爲一個特殊的設備文件而存在的。

n 不同祖先的進程之間可以通過管道共享數據。

n 當共享管道的進程執行完所有的I / O操作以後,命名管道將繼續保存在文件系統中以便以後使用。

管道只能由相關進程使用,它們共同的祖先進程創建了管道。但是,通過FIFO,不相關的進程也能交換數據。

5.2命名管道創建與操作

名管道創建

#include <sys/types.h>

#include <sys/stat.h>

int mkfifo(const char * pathname, mode_t mode) ;

返回:若成功則爲0,若出錯則爲- 1

一旦已經用mkfifo創建了一個FIFO,就可用open打開它。確實,一般的文件I / O函數(closereadwriteunlink等)都可用於FIFO

 

當打開一個FIFO時,非阻塞標誌(O_NONBLOCK)產生下列影響:

 (1)在一般情況中(沒有說明O_NONBLOCK),只讀打開要阻塞到某個其他進程爲寫打開此FIFO。類似,爲寫而打開一個FIFO要阻塞到某個其他進程爲讀而打開它。

 (2)如果指定了O_NONBLOCK,則只讀打開立即返回。但是,如果沒有進程已經爲讀而打開一個FIFO,那麼只寫打開將出錯返回,其errnoENXIO

 類似於管道,若寫一個尚無進程爲讀而打開的FIFO,則產生信號SIGPIPE。若某個FIFO的最後一個寫進程關閉了該FIFO,則將爲該FIFO的讀進程產生一個文件結束標誌。

 

FIFO相關出錯信息:

n  EACCES (無存取權限)

n  EEXIST (指定文件不存在)

n  ENAMETOOLONG (路徑名太長)

n  ENOENT (包含的目錄不存在)

n  ENOSPC (文件系統剩餘空間不足)

n  ENOTDIR (文件路徑無效)

n  EROFS (指定的文件存在於只讀文件系統中)

實例見:fifo_write.c  fifo_read.c

fifo_write.c

#include <sys/types.h>

#include <sys/stat.h>

#include <errno.h>

#include <fcntl.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#define FIFO "/tmp/myfifo"

 

main(int argc,char** argv)

{

        char buf_r[100];

        int  fd;

        int  nread;

        

        if((mkfifo(FIFO,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST))

                  printf("cannot create fifoserver/n");

        printf("Preparing for reading bytes.../n");

        

        memset(buf_r,0,sizeof(buf_r));

        fd=open(FIFO,O_RDONLY|O_NONBLOCK,0);

        if(fd==-1)

        {

                  perror("open");

                  exit(1);    

        }

        while(1)

        {

                  memset(buf_r,0,sizeof(buf_r));

                  

                  if((nread=read(fd,buf_r,100))==-1){

                           if(errno==EAGAIN)

                                    printf("no data yet/n");

                  }

                  printf("read %s from FIFO/n",buf_r);

                  sleep(1);

        }       

        pause();

        unlink(FIFO);

}

 

fifo_read.c

#include <sys/types.h>

#include <sys/stat.h>

#include <errno.h>

#include <fcntl.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#define FIFO_SERVER "/tmp/myfifo"

 

main(int argc,char** argv)

{

        int fd;

        char w_buf[100];

        int nwrite;

        

        if(fd==-1)

                  if(errno==ENXIO)

                           printf("open error; no reading process/n");

        fd=open(FIFO_SERVER,O_WRONLY|O_NONBLOCK,0);

        if(argc==1)

                  printf("Please send something/n");

        strcpy(w_buf,argv[1]);

        if((nwrite=write(fd,w_buf,100))==-1)

        {

                  if(errno==EAGAIN)

                           printf("The FIFO has not been read yet.Please try later/n");

        }

        else

                  printf("write %s to the FIFO/n",w_buf);

}

 

注:本文重點講述黑體字3種進程間通信類型

 

信號機制

1,信號概述

信號是軟件中斷。信號(signal)機制是Unix系統中最爲古老的進程之間的通信機制。它用於在一個或多個進程之間傳遞異步信號。

很多條件可以產生一個信號。

n  當用戶按某些終端鍵時,產生信號。在終端上按DELETE鍵通常產生中斷信號(SIGINT)。這是停止一個已失去控制程序的方法。(第11章將說明此信號可被映射爲終端上的任一字符。)

n  硬件異常產生信號:除數爲0、無效的存儲訪問等等。這些條件通常由硬件檢測到,並將其通知內核。然後內核爲該條件發生時正在運行的進程產生適當的信號。例如,對執行一個無效存儲訪問的進程產生一個SIGSEGV

n 進程用kill( 2 )函數可將信號發送給另一個進程或進程組。自然,有些限制:接收信號進程和發送信號進程的所有者必須相同,或發送信號進程的所有者必須是超級用戶。

n 用戶可用kill( 1 )命令將信號發送給其他進程。此程序是kill函數的界面。常用此命令終止一個失控的後臺進程。

n 當檢測到某種軟件條件已經發生,並將其通知有關進程時也產生信號。這裏並不是指硬件產生條件(如被0除),而是軟件條件。例如SIGURG (在網絡連接上傳來非規定波特率的數據)SIGPIPE (在管道的讀進程已終止後一個進程寫此管道),以及SIGALRM(進程所設置的鬧鐘時間已經超時)

 

內核爲進程生產信號,來響應不同的事件,這些事件就是信號源。主要的信號源如下:

n 異常:進程運行過程中出現異常;

n 其它進程:一個進程可以向另一個或一組進程發送信號;

n 終端中斷:Ctrl-CCtrl-/等;

n 作業控制:前臺、後臺進程的管理;

n 分配額:CPU超時或文件大小突破限制;

n 通知:通知進程某事件發生,如I/O就緒等;

n 報警:計時器到期。

 

1) SIGHUP      2) SIGINT         3) SIGQUIT      4) SIGILL

5) SIGTRAP      6) SIGIOT         7) SIGBUS        8) SIGFPE

9) SIGKILL       10) SIGUSR1     11) SIGSEGV    12) SIGUSR2

13) SIGPIPE     14) SIGALRM   15) SIGTERM   17) SIGCHLD

18) SIGCONT   19) SIGSTOP     20) SIGTSTP      21) SIGTTIN

22) SIGTTOU   23) SIGURG      24) SIGXCPU    25) SIGXFSZ

26) SIGVTALRM       27) SIGPROF              28) SIGWINCH 29) SIGIO         30) SIGPWR     

下面是幾個常見的信號。

n SIGHUP從終端上發出的結束信號;

n SIGINT來自鍵盤的中斷信號(Ctrl-C);

n SIGQUIT:來自鍵盤的退出信號(Ctrl-/);

n SIGFPE浮點異常信號(例如浮點運算溢出);

n SIGKILL:該信號結束接收信號的進程;

n SIGALRM:進程的定時器到期時,發送該信號;

n SIGTERMkill命令發出的信號;

n SIGCHLD:標識子進程停止或結束的信號;

n SIGSTOP:來自鍵盤(Ctrl-Z)或調試程序的停止執行信號

…………

可以要求系統在某個信號出現時按照下列三種方式中的一種進行操作。

(1)忽略此信號。大多數信號都可使用這種方式進行處理,但有兩種信號卻決不能被忽略。它們是:SIGKILLSIGSTOP。這兩種信號不能被忽略的原因是:它們向超級用戶提供一種使進程終止或停止的可靠方法。另外,如果忽略某些由硬件異常產生的信號(例如非法存儲訪問或除以0),則進程的行爲是未定義的。

(2)捕捉信號。爲了做到這一點要通知內核在某種信號發生時,調用一個用戶函數。在用戶函數中,可執行用戶希望對這種事件進行的處理。如果捕捉到SIGCHLD信號,則表示子進程已經終止,所以此信號的捕捉函數可以調用waitpid以取得該子進程的進程ID以及它的終止狀態。

(3)執行系統默認動作。對大多數信號的系統默認動作是終止該進程。

每一個信號都有一個缺省動作,它是當進程沒有給這個信號指定處理程序時,內核對信號的處理。有5種缺省的動作:

n 異常終止(abort):在進程的當前目錄下,把進程的地址空間內容、寄存器內容保存到一個叫做core的文件中,而後終止進程。

n 退出(exit):不產生core文件,直接終止進程。

n 忽略(ignore):忽略該信號。

n 停止(stop):掛起該進程。

n 繼續(continue):如果進程被掛起,則恢復進程的運行。否則,忽略信號。

 

2,信號發送與捕捉

2.1 kill()raise()

kill()不僅可以中止進程,也可以向進程發送其他信號。

kill函數不同的是,raise()函數運行向進程自身發送信號。

#include <sys/types.h>

#include <signal.h>

int kill(pid_t pid, int signo) ;

int raise(int signo) ;

        兩個函數返回:若成功則爲0,若出錯則爲-1

killpid參數有四種不同的情況:

n  pid>0將信號發送給進程IDpid的進程。

n  pid == 0將信號發送給其進程組I D等於發送進程的進程組ID,而且發送進程有許可權向其發送信號的所有進程。

n  pid < 0將信號發送給其進程組ID等於pid絕對值,而且發送進程有許可權向其發送信號的所有進程。如上所述一樣,“所有進程”並不包括系統進程集中的進程。

n pid==-1 POSIX.1未定義此種情況。

kill()實例見:kill.c

2.2 alarmpause函數

使用alarm函數可以設置一個時間值(鬧鐘時間),在將來的某個時刻該時間值會被超過。當所設置的時間值被超過後,產生SIGALRM信號。如果不忽略或不捕捉此信號,則其默認動作是終止該進程。

#include <unistd.h>

unsigned int alarm(unsigned int seconds) ;

返回:0或以前設置的鬧鐘時間的餘留秒數

參數seconds的值是秒數,經過了指定的seconds秒後會產生信號SIGALRM

每個進程只能有一個鬧鐘時間。如果在調用alarm時,以前已爲該進程設置過鬧鐘時間,而且它還沒有超時,則該鬧鐘時間的餘留值作爲本次alarm函數調用的值返回。以前登記的鬧鐘時間則被新值代換。

如果有以前登記的尚未超過的鬧鐘時間,而且seconds值是0,則取消以前的鬧鐘時間,其餘留值仍作爲函數的返回值。

pause函數使調用進程掛起直至捕捉到一個信號。

#include <unistd.h>

int pause(void);

        返回:-1errno設置爲EINTR

只有執行了一個信號處理程序並從其返回時,pause才返回。

實例見:alarm.c

 

3,信號的處理

當系統捕捉到某個信號時,可以忽略該信號或是使用指定的處理函數來處理該信號,或者使用系統默認的方式。

信號處理的主要方法有兩種,一種是使用簡單的signal函數,另一種是使用信號集函數組。

3.1 signal()

#include <signal.h>

void (*signal (int signo, void (*func)(int)))(int)

        返回:成功則爲以前的信號處理配置,若出錯則爲SIG_ERR

func的值是: (a)常數SIG_IGN,或(b)常數SIG_DFL,或(c)當接到此信號後要調用的函數的地址。如果指定SIG_IGN,則向內核表示忽略此信號(有兩個信號SIGKILLSIGSTOP不能忽略)。如果指定SIG_DFL,則表示接到此信號後的動作是系統默認動作。當指定函數地址時,我們稱此爲捕捉此信號。我們稱此函數爲信號處理程序(signal handler)或信號捕捉函數(signal-catching function)。

signal函數原型太複雜了,如果使用下面的typedef,則可使其簡化。

typedef void sign(int);

sign *signal(int, handler *);

實例見:mysignal.c

 

3.2信號集函數組

我們需要有一個能表示多個信號——信號集(signal set)的數據類型。將在sigprocmask()這樣的函數中使用這種數據類型,以告訴內核不允許發生該信號集中的信號。信號集函數組包含幾大模塊:創建函數集、登記信號集、檢測信號集。

1)創建函數集

#include <signal.h>

int sigemptyset(sigset_t * set) ;

int sigfillset(sigset_t * set) ;

int sigaddset(sigset_t * set,int signo) ;

int sigdelset(sigset_t * set,int signo) ;

        四個函數返回:若成功則爲0,若出錯則爲-1

int sigismember(const sigset_t * set, int                        signo) ;

        返回:若真則爲1,若假則爲0

sigemptyset初始化信號集合爲空。

 sigfillset初始化信號集合爲所有信號的集合。

 sigaddset將指定信號添加到現存集中。

 sigdelset從信號集中刪除指定信號。

 sigismember查詢指定信號是否在信號集合中。

 

2)登記信號集

登記信號處理機主要用於決定進程如何處理信號。首先要判斷出當前進程阻塞能不能傳遞給該信號的信號集。這首先使用sigprocmask函數判斷檢測或更改信號屏蔽字,然後使用sigaction函數改變進程接受到特定信號之後的行爲。

一個進程的信號屏蔽字可以規定當前阻塞而不能遞送給該進程的信號集。調用函數sigprocmask可以檢測或更改(或兩者)進程的信號屏蔽字。

# include <signal.h>

int sigprocmask(int how, const sigset_t * set, sigset_t * oset) ;

        返回:若成功則爲0,若出錯則爲-1

oset是非空指針,進程的當前信號屏蔽字通過oset返回。其次,若set是一個非空指針,則參數how指示如何修改當前信號屏蔽字。

sigprocmask更改當前信號屏蔽字的方法,how參數設定:

n  SIG_BLOCK該該進程新的信號屏蔽字是其當前信號屏蔽字和set指向信號集的並集。set包含了我們希望阻塞的附加信號。

n  SIG_UNBLOCK該該進程新的信號屏蔽字是其當前信號屏蔽字和set所指向信號集的交集。set包含了我們希望解除阻塞的信號。

n  SIG_SETMASK該該進程新的信號屏蔽是set指向的值。

如果set是個空指針,則不改變該進程的信號屏蔽字, how的值也無意義。

sigaction函數的功能是檢查或修改(或兩者)與指定信號相關聯的處理動作。此函數取代了UNIX早期版本使用的signal函數。

#include <signal.h>

int sigaction(int signo, const struct sigaction * act,struct sigaction * oact) ;

        返回:若成功則爲0,若出錯則爲- 1

 參數signo是要檢測或修改具體動作的信號的編號數。若act指針非空,則要修改其動作。如果oact指針非空,則系統返回該信號的原先動作。此函數使用下列結構:

struct sigaction {

        void (*sa_handler)(int signo);

        sigset_t sa_mask;
         int sa_flags;

        void (*sa_restore);

} ;

sa_handler是一個函數指針,指定信號關聯函數,可以是自定義處理函數,還可以SIG_DFL SIG_IGN

sa_mask是一個信號集,它可以指定在信號處理程序執行過程中哪些信號應當被阻塞。

sa_flags中包含許多標誌位,是對信號進行處理的各種選項。具體如下:

n     SA_NODEFER/SA_NOMASK: 當捕捉到此信號時,在執行其信號捕捉函數時,系統不會自動阻塞此信號。

n     SA_NOCLDSTOP: 進程忽略子進程產生的任何SIGSTOPSIGTSTPSIGTTINSIGTTOU信號

n     SA_RESTART: 可讓重啓的系統調用重新起作用。

n     SA_ONESHOT/SA_RESETHAND: 自定義信號只執行一次,在執行完畢後恢復信號的系統默認動作。

 

3)檢測信號集

檢測信號是信號處理的後續步驟,但不是必須的。 sigpending函數運行進程檢測“未決”信號(進程不清楚他的存在),並進一步決定對他們做何處理。

 sigpending返回對於調用進程被阻塞不能遞送和當前未決的信號集。

#include <signal.h>

int sigpending(sigset_t * set) ;

        返回:若成功則爲0,若出錯則爲-1

信號集實例見:sigaction.c

 

 

 

共享內存

共享內存區域是被多個進程共享的一部分物理內存。如果多個進程都把該內存區域映射到自己的虛擬地址空間,則這些進程就都可以直接訪問該共享內存區域,從而可以通過該區域進行通信。共享內存是進程間共享數據的一種最快的方法,一個進程向共享內存區域寫入了數據,共享這個內存區域的所有進程就可以立刻看到其中的內容。

共享內存實現分爲兩個步驟:

 一、創建共享內存,使用shmget函數。

 二、映射共享內存,將這段創建的共享內存映射到具體的進程空間去,使用shmat函數。

 

系統調用:shmget( ) ;

原型:int shmget ( key_t key, int size, int shmflg );

        返回值:如果成功,返回共享內存段標識符。

        如果失敗,則返回- 1

        errno = EINVAL (無效的內存段大小)

                     EEXIST (內存段已經存在,無法創建)

                     EIDRM (內存段已經被刪除)

                     ENOENT (內存段不存在)

                    EACCES (權限不夠)

                     ENOMEM (沒有足夠的內存來創建內存段)

 

系統調用:shmat();

原型:int shmat ( int shmid, char *shmaddr, int shmflg);

        返回值:如果成功,則返回共享內存段連接到進程中的地址。

        如果失敗,則返回- 1

        errno = EINVAL (無效的IPC ID值或者無效的地址)

                     ENOMEM (沒有足夠的內存)

                     EACCES (存取權限不夠)

 

當一個進程不在需要共享的內存段時,它將會把內存段從其地址空間中脫離。

系統調用:shmdt();

調用原型:int shmdt ( char *shmaddr );

        返回值:如果失敗,則返回- 1

                  errno = EINVAL (無效的連接地址)

共享內存實例見:shmadd.c

 

 

 

 

消息隊列

消息緩衝隊列通信機制

1.消息緩衝隊列通信機制簡介

   由於消息緩衝機制中所使用的緩衝區爲公用緩衝區,因此使用消息緩衝機制傳送數據時,兩通信進程必須滿足如下條件。

第一,在發送進程把寫入消息的緩衝區掛入消息隊列時,應禁止其他進程對該消息隊列的訪問,否則,將引起消息隊列的混亂。同理,當接收進程正從消息隊列中取消息時,也應禁止其他進程對該隊列的訪問。

第二,當緩衝區中無消息存在時,接收進程不能接收到任何消息;而發送進程是否可以發送消息,則只由發送進程是否能夠申請到緩衝區決定。

2.消息緩衝隊列通信機制中的數據結構

1)消息緩衝區

   typedefstruct message buffer

   {

      sender  //發送者進程標識符

      size                 //消息長度

      text        //消息正文

      next       //指向下一個消息緩衝區的指針

   }

2PCB中有關進程通信的數據項

   typedefstruct message block

   {

     

     mq  //消息隊列隊首指針

     mutex//消息隊列互斥信號量,初值爲1

     sm   //消息隊列資源信號量,用於消息隊列中的消息計數,初值爲0

     

}

 

消息隊列的實現

消息隊列就是消息的一個鏈表,它允許一個或多個進程向它寫消息,一個或多個進程從中讀消息。具有一定的FIFO的特性,但是可實現消息的隨即查詢。這些消息存在於內核中,由“隊列ID”來標識。

 消息隊列的實現包括創建和打開隊列、添加消息、讀取消息和控制消息隊列這四種操作。

        msgget:創建和打開隊列,其消息數量受系統限制。

        msgsnd:添加消息,將消息添加到消息隊列尾部。

        msgrcv:讀取消息,從消息隊列中取走消息。

        msgctl:控制消息隊列。

 

nt msgget (key_t key, int flag)

  key:返回新的或已有隊列的IDIPC_PRIVATE

int msgsnd (int msqid, struct msgbuf             *msgp, size_t msgsz, int flag)

        其中:msqid是消息隊列的隊列ID

n  msgp是消息內容所在的緩衝區;

n  msgsz是消息的大小;

n  msgflg是標誌,IPC_NOWAIT若消息並沒有立交發送而調用進程會立即返回。

 

struct msgbuf

{

        long mtype; /* type of message */

        char mtext[1]; /* message text */

};

int msgrcv (int msqid, struct      msgbuf *msgp, size_t msgsz,long         msgtyp, int flag)

n  msqid是消息隊列的引用標識符;

n  msgp是接收到的消息將要存放的緩衝區;

n  msgsz是消息的大小;

n  msgtyp是期望接收的消息類型;

n  msgflg是標誌。

 

int msgctl (int msqid, int cmd, struct msqid_ds *buf)

msqid是消息隊列的引用標識符;

cmd是執行命令;

buf是一個緩衝區。

cmd參數指定對於由msqid規定的隊列要執行的命令:

n  IPC_STAT取此隊列的msqid_ds結構,並將其存放在buf指向的結構中。

n  IPC_SET按由buf指向的結構中的值,設置與此隊列相關的結構中的下列四個字段:

        msg_perm.uidmsg_perm.gidmsg_perm;modemsg_qbytes。此命令只能由下列兩種進程執行:一種是其有效用戶ID等於msg_perm.cuidmsg_perm.uid;另一種是具有超級用戶特權的進程。只有超級用戶才能增加msg_qbytes的值。

n  IPC_RMID從系統中刪除該消息隊列以及仍在該隊列上的所有數據。這種刪除立即生效。仍在使用這一消息隊列的其他進程在它們下一次試圖對此隊列進行操作時,將出錯返回EIDRM

此命令只能由下列兩種進程執行:一種是其有效用戶ID等於msg_perm.cuidmsg_perm.uid;另一種是具有超級用戶特權的進程。

消息隊列實例見:msg.c

 

 轉自: http://blog.csdn.net/zccst/article/details/4836937  

 

 

 


發佈了73 篇原創文章 · 獲贊 18 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章