進程間通信

進程間通信及同步的方式有:pipe、fifo、共享內存、信號量、消息隊列、socket通信等

1:管道
兩個或多個進程間通過管道,可以互相傳遞信息,利用read和write系統調用函數來進行讀寫操作。

    int pipe(int filedes[2]);
    filedes是一個有兩個成員的整形數組。
   (1)filedes[0]將用來從管道讀取數據
   (2)filedes[1]用來向管道寫入數據。

如下圖所示:
這裏寫圖片描述
示例代碼:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()  {
    int data_processed;
    int file_pipes[2];
    const char some_data[] = “123”;
    char buffer[512];
    memset(buffer, ‘\0’, sizeof(buffer));
    if (pipe(file_pipes) == 0) 
    {
        data_processed=write(file_pipes[1],some_data,strlen(some_data));                     
        printf(“Wrote %d bytes\n”, data_processed);
        data_processed = read(file_pipes[0], buffer, BUFSIZ);
        printf(“Read %d bytes: %s\n”, data_processed, buffer);
        exit(EXIT_SUCCESS);
    }
    exit(EXIT_FAILURE);
  }

2:命名管道FIFO
(1)提供進程間數據交換的一種機制
(2)與pipe不同的是,命名管道不需要程序由一個共同的祖先進程啓動
(3)命名管道是一種特殊類型的文件,因爲是文件,具備了和文件相同的特點,有文件名、所有者,訪問權限等,但行爲和管道相同

2.1:命令行方式創建FIFO管道

$ mknod filename p
$ mkfifo filename 

2.2:程序中創建FIFO管道

#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *filename, mode_t mode);
int mknod(const char *filename, mode_t mode | S_IFIFO, (dev_t)0);

參數filename爲指定管道文件的名字
參數mode給出了FIFO的訪問權限

例程:創建命名管道FIFO

#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>

int main()
{
    int res = mkfifo(“/tmp/my_fifo”, 0777);
    if (res == 0) 
    printf(“FIFO created\n”);
    exit(EXIT_SUCCESS);
}

2.3:打開FIFO文件
與通過pipe調用創建管道不同,FIFO是以命名文件的形式存在,而不是打開的文件描述符,所以對它進行讀寫操作之前必須先打開它。
FIFO也用open和close函數打開和關閉,但多了額外的功能
例:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#define FIFO_NAME “/tmp/my_fifo”
int main()  {
    int res;
    int open_mode = 0;
    open_mode = O_RDONLY | O_NONBLOCK;
    res=open(FIFO_NAME, open_mode);
    if (res == -1)
    {
        perror(“Failed tp open FIFO file”);
        exit(1);
    }
    printf(“FIFO file is opened\n”);
    clsoe(res);
    return 0;
  }

注:注意:打開管道文件,可以選擇阻塞與非阻塞兩種方式。

例程:FIFO讀、寫程序

讀:

#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#define FIFO_NAME "/tmp/my_fifo“
 int main() 
 {   
    char buf[100];   
    int fd; 
    int nread;   
    fd = open(FIFO_NAME, O_RDONLY ,0);   
    if (fd == -1) 
    {      
         perror("open");      
         exit(1);
    }      
    memset(buf,0,sizeof(buf));      
   if ((nread = read(fd, buf, 100))==-1)
   { 
         perror("read");         
         exit(1);
   }      
   printf("read %s from FIFO\n",buf);   
   return 0;
   }

寫:

#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#define FIFO_NAME "/tmp/my_fifo“
int main()  
{   
     int fd;   
     int nwrite;   
     char buf[100];      
     fd = open(FIFO_NAME,O_WRONLY  ,0);   
     if (fd == -1)   
     {     
        perror("Failed to open FIFO file");     
     }    
     printf("FIFO file is opened\n");   
     strcpy(buf,"helloworld");   
     if ((nwrite=write(fd,buf,100))==-1)   
     {      
           perror("write");      
           exit(1);   
     }   
      close(fd);   
      return 0;
  }

3:信號量
(1)確保程序對某個特定的資源具有獨佔式的訪問的機制
(2)信號量是特殊的變量,只能取正整數並且只允許有兩種操作:等待P和信號V

3.1:IPC機制關鍵值

#include <sys/ipc.h>

key_t ftok(const char *path, int id);

函數的作用是以path相關的信息爲基礎返回一個關鍵值,利用此關鍵值實現IPC同步和通信機制,也可使用IPC_PRIVATE作爲參數由系統自動生成。
ftok與IPC_PRIVATE的區別:
ftok藉助文件實現功能,常在不同的進程中使用,尤其是兩個進程不是父子進程時,但因爲是藉助文件實現,一旦文件被刪除後重新創建,獲得的key就會發生變化,影響兩個進程同步及通信機制的實現

IPC_PRIVATE由系統自動生成鍵值,常用於父子進程中,如要運行非父子進程關係的兩個進程間,需要傳值才能讓另外的進程獲得IPC_PRIVATE的值

3.2:semget函數:創建信號量集

#include <sys/sem.h>

int semget(key_t key, int nsems, int sem_flags);

參數key:整數,相關進程可以通過它訪問一個信號量集
參數nsems:指定一個信號量集裏的信號量數量。它幾乎總是取值1
參數sem_flags:是一組標誌,與open函數的標誌相似

3.3:semop函數:實際執行各種信號量操作

#include <sys/sem.h>

int semop(int semid, struct sembuf * op_array, size_t num_ops);

參數semid:爲semget函數返回的信號量集標識符
參數op_array:對應一個或多個信號量的sembuf結構體操作
參數num_ops:同時對幾個信號量進行操作
sembuf結構體數據成員說明

struct sembuf
{
    short sem_num;
    short sem_op;
    short sem_flg;
}

sem_num: 對信號量集中的哪個信號量進行操作,從0開始表示
sem_op: 操作數設置,通常是+1或-1操作
sem_flg: 功能設置,IPC_NOWAIT與SEM_UNDO

3.4:semctl 函數:在一組信號量上做各種控制操作,諸如信號量集合的初始化、刪除和狀態查詢等

#include <sys/sem.h>

int semctl(int semid, int sem_num, int command, union semun ctl_arg);

參數semid:爲semget的返回值
參數sem_num:指定特定的信號量
參數ctl_arg 是一個聯合體,對應三種不同功能的semctl調用
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
}
參數command:有三種功能:
(1)標準的IPC函數:
IPC_STAT : 把狀態信息放入ctl_arg.stat中
IPC_SET : 用ctl_arg.stat中的值設置所有權/許可權
IPC_RMID : 從系統中刪除信號量集合
(2)單信號量操作:
GETVAL : 返回信號量的值
SETVAL : 把信號量的值寫入ctl_arg.val中
GETPID : 返回sempid值
GETNCNT : 返回semncnt
GETZCNT : 返回semzcnt
(3)全信號量操作:
GETALL : 把所有信號量的semvals值寫入ctl_arg.array
SETALL : 用ctl_arg.array中的值設置所有信號量的semvals

3.5例子:
(1)創建信號量

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
    int semid;
    int nsems = 1;
    int flags = 0666;
    struct sembuf buf;
    semid = semget(IPC_PRIVATE, nsems, flags);
    if (semid < 0) {
       perror(“semget”);
       exit(EXIT_FAILURE);
    }
    printf(“semphore created: %d\n”, semid);
    buf.sem_num = 0;
    buf.sem_op = 1;
    buf.sem_flg = IPC_NOWAIT;
    if ((semop(semid, &buf, nsems)) < 0) {
       perror(“semop”);
       exit(EXIT_FAILURE);
    }
    system(“ipcs –s”);
    exit(EXIT_SUCCESS);
}

(2)刪除信號量

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
 int main(int argc, char *argv[])
 {
      int semid;
      if (argc !=2) 
      {
         puts(“USAGE:sctl <semaphore id>”);
         exit(EXIT_FAILURE);
      }
      semid = atoi(argv[1]);
      if((semctl(semid, 0, IPC_RMID)) < 0)
      {
         perror(“semctl IPC_RMID”);
         exit(EXIT_FAILURE);
      } 
      else 
      {
         puts(“semaphore removed”);
         system(“ipcs –s”);
      }
       exit(EXIT_SUCCESS);
 }

(3)進程同步操作

#include <sys/types.h>
#include <linux/sem.h>
#include <unistd.h>
int pid,semid;
struct sembuf P,V;
union semun arg;
int main(void)   
{
   arg.val = 1;
   semid = semget(IPC_PRIVATE,1,0666|IPC_CREAT);
   semctl(semid, 0, SETVAL, arg);
   P.sem_num = 0;
   P.sem_op = -1;
   P.sem_flg = SEM_UNDO;
   V.sem_num = 0;
   V.sem_op = 1;
   V.sem_flg = SEM_UNDO;
   if ((pid = fork()) == -1)
   {
       perror(“fork”);
       exit(1);
    }
    else if (pid == 0)
    {
        semop(semid,&P,1);
        system(“echo –n ‘a’ >> ./printer);
        system(“echo –n ‘b’ >> ./printer);
        system(“echo ‘c’ >> ./printer);
        semop(semid,&V,1);
    }
    else
    { 
        semop(semid,&P,1);
        system(“echo –n ‘1’ >> ./printer);
        system(‘echo –n ‘2’ >> ./printer);
        system(‘echo ‘3’ >> ./printer);
        semop(semid,&V,1);
     }
     semctl(semid, IPC_RMID,0);
     exit(0);
 } 

4:消息隊列

4.1:msgget函數:創建和訪問消息隊列

#include<sys/msg.h>

int msgget(key_t key, int permflags);

參數key:爲標識好的消息隊列
參數permflags:有兩個值可選擇:
IPC_CREAT : 創建新的隊列
IPC_EXCL : 與IPC_CREAT合用,如要創建的隊列已存在,返回值爲-1

例:創建消息隊列

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[])
{
    int qid;
    key_t key;
    key = 123;
    if ((qid = msgget(key, IPC_CREAT | 0666)) < 0)   
   {
        perror(“msgget:create”);
        exit(EXIT_FAILURE);
    }
    printf(“created queue id = %d\n”, qid);
    if ((qid == msgget(key, 0)) < 0) {
         perror(“msgget:open”);
         exit(EXIT_FAILURE);
    }
    printf(“opened queue id = %d\n”, qid);
    exit(EXIT_SUCCESS);
   }

4.2:
msgsnd函數:功能爲向消息隊列添加信息
msgrcv函數:功能爲從隊列中讀取消息

#include <sys/msg.h>

int msgsnd(int mqid, const void * message, size_t size, int flags);
int msgrcv(int mqid, void * message, size_t size, long msg_type,int flags);

參數mqid:由msgget返回獲得的
參數message:由用戶自定義的結構
參數flags:只有一個有意義的值IPC_NOWAIT,當消息無法返回,調用立即返回,返回值爲-1
參數msg_type:決定實際將讀到的是哪條消息

4.3:msgctl函數:功能爲使進程獲取有關消息隊列的狀態信息、改變與隊列有關的限制或從系統刪除一個隊列

int msgctl(int mqid, int command, struct msqid_ds *msq_stat);

參數mqid:有效的隊列標識符
參數command:有三個值
IPC_STAT : 告訴系統把狀態信息存入msq_stat中
IPC_SET : 用於通過msq_stat中存放的信息爲消息隊列設置控制變量的值
IPC_RMID : 從系統中刪除消息隊列
參數msq_stat:指向msqid_ds結構的指針

例程:發送與接收

#include <sys/types.h>
#include <linux/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main()
{
   int qid, len;
   struct msgbuf sndmsg;
   struct msgbuf rcvmsg;
   if ((qid  = msgget(IPC_PRIVATE, IPC_CREAT|0666))  == -1)
   {
      perror(“msgget”);
      exit(1);
   }
   sprintf(sndmsg.mtext, “hello”);
   sndmsg.mtype = 1;
   len = strlen(sndmsg.mtext);
   if ((msgsnd(qid, &sndmsg, len, 0)) < 0)
   {
       perror(“msgsnd”);
       exit(1);
   }
   puts(“message posted”);
   sleep(1);
   if ((msgrcv(qid, &rcvmsg, len, 0, 0)) < 0)
   {
      perror(“msgrcv”);
      exit(1);
   }
   puts(“message received”);
   rcvmsg.mtext[5]=‘\0\;
   printf(“This message is %s\n”,rcvmsg.mtext);
   msgctl(qid, IPC_RMID, NULL);
   exit(0);
}

5:共享內存
(1)共享存儲操作使得兩個或兩個以上的進程可以共用一段物理內存
(2)一般情況下,兩個進程的數據區是完全獨立的,通常它在所有的IPC機制中是最有效的

5.1:shmget函數:創建一段內存用於進程共享數據

int shmget(key_t key, size_t size, int permflags);
參數key:內存段的關鍵值
參數size:內存段所需最小字節數
參數permflags:給出了內存段的訪問權限
例程:創建共享內存

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#define BUFSZ 4096

int main(void)
{
   int shmid;
   if((shmid = shget(IPC_PRIVATE, BUFSZ,0666)) < 0) 
  {
      perror(“shmget”);
      exit(EXIT_FAILURE);
   }
   printf(“segment created: %d\n”, shmid);
   system(“ipcs –m”);
   exit(EXIT_SUCCESS);
}

5.2:shmat函數:功能爲將標識的內存段掛接到調用進程的有效地址上

void *shmat(int shmid, const void *daddr, int shmflags);
參數shmid:表示的共享內存段
參數daddr:控制調用選擇的地址
參數shmflags有2個值:
SHM_RDONLY: 使內存段只讀掛接
SHM_RND : 使掛接的地址可以在daddr的一頁範圍內變動
如不設置,shmat將精確使用daddr的值

5.3:shmdt函數:取消共享內存的掛接
int shmdt(char *shmadr);
參數shmadr:取消掛接的共享內存

5.4:shmctl函數:功能爲對共享內存進行控制管理
int shmctl(int shmid, int command, struct shmid_ds * shm_stat);
功能使用與msgctl函數類似,其中command參數可以取以下參數:
IPC_STAT 把shmid_ds結構中的數據設置爲共享內存當前關聯值
IPC_SET 進程有足夠權限,就設置成shmid_ds中給出的值
IPC_RMID 刪除共享內存段

struct shmid_ds
 {
         uid_t shm_perm.uid;
        uid_t shm_perm.gid;
        mode_t shm_perm.mode;
}

5.5:例:共享內存

#include <stdio.h>
#include <sys/shm.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
int main()
{  
   int shmid;  
   char *shmbuf;  
   pid_t pid;  
   key_t key;   
   shmid = shmget(IPC_PRIVATE, 100, IPC_CREAT|0777);  
   if (shmid == -1)  
   {    
       perror("shmid");    
       exit(1);
   }  
   shmbuf = shmat(shmid, NULL, 0);  
   if (shmbuf == (void*) -1)  
   {    
        perror("shmat");    
        exit(1);
   }  
   pid=fork();  
   if(pid == -1)  
   {    
      perror("fork");     
      exit(1);
   }  
   if(pid == 0) 
   {  
      strcpy(shmbuf,"this is child");
   }  
   else
   {      
      printf("%s\n",shmbuf);    
      wait(NULL);
      shmdt(shmid);  
      shmctl(shmid, IPC_RMID, NULL);
   } 
   exit(0);
}

_

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