1. 程式人生 > >【IO程序】程序間通訊

【IO程序】程序間通訊

【1】程序間通訊概述

每一個程序雖然獨立,但也需要讓不同的程序實現資料的傳輸、還有訊號通知

通訊方式:
傳統的程序間通訊:
    無名管道、有名管道  -->  資料傳輸
    訊號                -》  非同步通知

系統5(System V)通訊方式:

    共享記憶體、訊息佇列          --》  資料傳輸
    訊號量集                   --》  同步和互斥

網路套接字通訊
    不同的電腦不同的程序之間實現通訊

不同的程序間如何實現通訊?
    在作業系統的所執行的那段空間(核心空間3-4g),開闢一款緩衝區

【2】無名管道

 是作業系統在核心空間所開闢的一塊記憶體區域。記憶體區域有一定的大小,大小為64K

 無名管道:就是在對當前檔案系統找不到對應的名字,對應的操作函式會給你返回兩個描述符,一個代表管道的讀操作,一個代表管道的寫操作 

 特點:只能在父子程序或者兄弟程序或者子孫程序之間實現通訊,資料傳輸方向的單向,是一個半雙工的通訊方式。


 int  pipe(int pipdfd[2]);
  功能:在核心空間開間一個記憶體區域用來實現程序間通訊
  引數:
    pipefd[0]   代表管道的讀端,也就是用來從pipefd[0]讀取資料
    pipefd[1]   代表管道的寫端,也就是用來從pipefd[1]寫入資料
  返回值:
        成功  0
        失敗  -1

  當管道中沒有資料的時候,執行讀操作,read函式會產生阻塞
  如果管道有 資料則依照讀取的資料個數進行read操作     

  往管道中寫資料,如果寫滿管道64k。則write會產生阻塞,只有讀操作讀取資料
  管道的剩餘空間大於4k時,write才能繼續寫資料     

【3】有名管道

  有名管道:可以在檔案系統下找到某個檔案,對檔案對開啟,執行相應的讀寫操作,內部實現與無名管道相同,檔案中僅儲存inode編號,指向記憶體中的區域。

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

       功能:建立有名管道,用來實現不同的程序間通訊
       引數:
          pathname:  指定管道檔案的名字,檔案型別是p,以後通過開啟管道檔案
                  往有名管道中寫入資料、或者讀出資料
          mode:      檔案許可權(mode & ~umask)
       返回值:
            成功  0
            失敗  -1
  有名管道與無名管道的使用方式相同,只需使用open系統呼叫獲取檔案描述符即可。

【4】訊號

實現程序跟程序之間的通知,依據訊號通知,具體執行什麼操作,決定於訊號的預設操作
但是訊號有三種相應(操作)方式:
  (1)預設,執行系統設定的操作方式
  (2)忽略,程序接收到訊號之後,不做任何操作
  (3)自定義操作:需要自己在程序中定義一個訊號處理函式,執行對應訊號操作

    kill -l
常用預設訊號的操作方式:
 2) SIGINT     ctrl+c         結束程序
 3) SIGQUIT    ctrl+\         結束程序
 9) SIGKILL                   結束程序  
10) SIGUSR1
12) SIGUSR2   使用者自定義訊號   結束程序   
13) SIGPIPE                   管道破裂,結束程序
14) SIGALRM                   鬧鐘訊號,結束程序
17) SIGCHLD                   子程序狀態改變,會向父程序傳送這個訊號
18) SIGCONT                   讓暫停的程序恢復執行
20) SIGTSTP                   暫停程序
19) SIGSTOP                   暫停程序


【注意】9 和 19這兩個訊號的預設操作方式不能被改變




kill(pid_t pid, int sig) -->  kill(1000, SIGKILL)


int raise(int sig);

功能:  都是用來發送訊號
    pid     程序的pid號
    sig     訊號種類中的一種
成功     0


unsigned int seconds alarm(unsigned int seconds)
功能:  用來設定鬧鐘,本身不具有阻塞作用
引數  senonds   設定鬧鐘秒數
返回值:
    之前沒有設定鬧鐘,返回值是0
    之前有設定過鬧鐘,則返回之前鬧鐘剩餘的時間值

    typedef void (*sighandler_t)(int); --> sighandler_t 

    typedef別名重定義,sighandler_t表示函式指標型別

   sighandler_t signal(int signum, sighandler_t handler);

功能:訊號處理函式,首先需要向核心註冊訊號signum,註冊完之後,signal本身不具有阻塞作用,會繼續往後執行
    handler   使用者自定操作,也就是使用者需要定義一個函式   void handler(int),這是函式的型別,形參表示的是核心向你這個程序傳送的訊號是什麼?


    void  handler(int signo)
    {
        if(signo == SIGKILL)
        {
            處理操作,實現功能,需要使用者自己定義
        }
        if(signo == SIGINT)
        {
            處理操作
        }
    }

【5】System V 程序間通訊物件

共享記憶體、訊息佇列、訊號量集


也是在核心當中去建立,建立好之後,一直保留系統當中,
如果不用操作核心物件了,需要手動的刪除

a、【刪除】核心物件命令
ipcrm [ -M key | -m id | -Q key | -q id | -S key | -s id ]

                  舉例:ipcs  -m  id      --》檢視共享記憶體

b、在程式中【檢視】核心物件   

int system(const char *command);
引數  :
       conmand  shell命令
       舉例:system(“ls -l”)

核心物件操作流程: 

1、建立key值
2、建立核心物件
3、操作核心物件,具體操作,依據核心物件
4、刪除核心物件

key: 讓進城找到核心物件。(共享記憶體、訊息佇列、訊號量集)
  IPC_PRIVATE   當前程序建立,只允許當前程序使用   0x00000000
  ftok          當一個程序建立好之後,其他的程序可以通過這個key值,找到核心物件


#include <sys/ipc.h>

key_t ftok(const char *pathname, int proj_id);
   功能:建立一個key值,讓不同的程序找到核心物件
   引數:
      pathname  檔名(必須存在,並且可以訪問),取得是檔案的inode節點號
      proj_id   整形變數,值得範圍是(1~255)
   返回值:
        成功   key
        失敗   -1

【6】共享記憶體

    共享記憶體:是在核心空間中的一塊區域,需要設定,大小也有使用者自己定義,以位元組為單位進行分配,並且是連續的地址空間,在所有通訊當中,效率最高,可以直接方位記憶體空間的地址

    #include <sys/shm.h>

    int shmget(key_t key, size_t size, int shmflg);
    功能:  建立共享記憶體
    引數:
          key   讓不同的程序找到核心物件
          size   共享記憶體的大小是多少? 以位元組為單位
          shmflg  
                IPC_CREAT   表示要建立共享記憶體
                IPC_EXCL    如果已經建立,則會判斷是否重複建立
                mode_flags  指定9位有效許可權位    0666
                舉例:      IPC_CREAT  |   IPC_EXCL   |   0666 
    返回值:
            成功  shmid,用來對內和物件(共享記憶體)操作的變數
            失敗  -1


    void *shmat(int shmid, const void *shmaddr, int shmflg);
    功能:   將核心空間的記憶體地址對映到呼叫的使用者空間
    引數: 
        shmid   通過shmget建立或者開啟的共享記憶體
        shmaddr  NULL  表示系統自動選擇合適的未使用的是給給使用者空間
        shmflg  
                0   表示可讀可寫
                SHM_RDONLY  表示只讀
   返回值:
        成功   對映後的地址,需要進行強制型別轉換
        失敗   (void *)-1



   int shmdt(const void *shmaddr);
   功能:解除對映
   引數:  
        shmaddr   對映後的地址(指標變數)
   返回值:
        成功 0 
        失敗 -1

   int shmctl(int shmid, int cmd, struct shmid_ds *buf);
   功能:共享記憶體控制函式
   引數:
         shmid    表示共享記憶體
         cmd   
                                  IPC_RMID      IPC_STAT

         buf    資訊結構體         NULL         struct shmid_ds(定義變數取地址)   
   返回值:
         成功  0
         失敗  -1

   【注意】     
   在訪問共享記憶體時,有可能出現多個程序共同訪問的現象,造成資料的混亂或者說是不一致,需要使用訊號量或者互斥鎖來保護共享資源      

【7】訊號量集

訊號量集:訊號量的集合,代表一類資源,每一類資源都有數量,需要使用者自己設定
建立訊號量的數目,需要使用者事先指定,並初始化設定每一個訊號量的數量值同樣也需要進行P(申請)V(釋放)操作。


int semget(key_t key, int nsems, int semflg);
功能:建立訊號量集合,裡面可以包含多個訊號量,具體數量自己指定
引數:
      key   為了方便其他程序找到訊號量集合
      nsems 指定訊號量集合中訊號量的數目是多少個?
            1     集合有一個訊號量    0
            2     集合有兩個訊號量    0  1
      semflg  
            IPC_CREAT   建立訊號量
            IPC_EXCL    防止重複建立
            0666        許可權
返回值:
      成功   操作識別符號(操作訊號量用的)
      失敗   -1  


int semctl(int semid, int semnum, int cmd, ...);
功能:訊號量的控制操作函式
引數:
      semid      代表你要操作的訊號量集合
      sennum     表示你要操作第幾個訊號量 
      cmd  
           SETVAL   用來設定每一個訊號量的數量是多少個?
           IPC_RMID   用來刪除訊號量集合
      union semun 
      {
          int val;    /* Value for SETVAL 設定每一個訊號量的數量*/
          struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
          unsigned short  *array;  /* Array for GETALL, SETALL */
          struct seminfo  *__buf;  /* Buffer for IPC_INFO (Linux-specific) */
      };





int semop(int semid, struct sembuf *sops, unsigned nsops);      
功能:訊號量的pv操作

引數:
     semid  操作那個訊號量集合
     sops   如何對訊號量進行操作 
            unsigned short sem_num; 對第幾個訊號量進行操作/* semaphore number */
            short sem_op;    執行什麼操作P(正數)  V(負數)/* semaphore operation */
            short sem_flg;   設定阻塞還是非阻塞方式  0阻塞  IPC_NOWAIT非阻塞/* operation flags */

     nsops  表示連續操作幾個訊號量
返回值:
      成功  0            
      失敗  -1

【8】訊息佇列

(1)訊息佇列機制

    特點:
        1、先進先出 
        2、按照型別傳送、讀取訊息
    使用ipcs -q檢視系統中的訊息

(2)如何建立訊息佇列

    int msgget(key_t key, int msgflg);
    功能:建立或者開啟訊息佇列
    引數:
        key:讓不同的程序找到同一個訊息佇列
        msgflg: 
            IPC_CREAT 建立
            IPC_EXCL  防止重複建立
            0666      許可權

        msqid:訊息佇列識別符號
(3)傳送訊息/接收訊息
    結構體1:
        struct msgbuf 
        {
               long mtype;       /* message type, must be > 0 */訊息型別
               char mtext[1];    /* message data */訊息正文
        };


    int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
    功能:傳送訊息到訊息佇列
    引數:
        msgid: 訊息佇列標示符  (用於操作訊息佇列傳送或者接收訊息)
        msgp:首先定義一個訊息佇列的結構體,型別如上所示結構體1
        msgsz:訊息佇列正文(text)的大小
        msgflg:設定為阻塞的方式0  非阻塞的方式 IPC_NOWAIT

    ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
                  int msgflg);
    功能:從佇列接收訊息
    引數:
        msgid: 訊息佇列標示符  (用於操作訊息佇列傳送或者接收訊息)
        msgp:首先定義一個訊息佇列的結構體,型別如上所示結構體1
        msgsz:訊息佇列正文(text)的大小
        msgtyp:訊息的型別(必須是大於0的整數)
        msgflg:設定為阻塞的方式0  非阻塞的方式 IPC_NOWAIT

(4)刪除訊息佇列

int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:操作訊息佇列
引數:
    msgid: 訊息佇列標示符  (用於操作訊息佇列傳送或者接收訊息)
    cmd:操作訊息佇列的命令   
        IPC_STAT   獲取訊息佇列屬性資訊的命令,需要定義一個struct msqid_ds結構體
        IPC_RMID   刪除訊息佇列的命令