linux 兩個進程通過 共享內存 通信例子


例子1:兩個進程通過共享內存通信,一個進程向共享內存中寫入數據,另一個進程從共享內存中讀出數據

    文件1 創建進程1,實現功能,打印共享內存中的數據

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>

int main(int argc, const char *argv[])
{
    key_t key;
    int shmid;
    char *p = NULL;

    //    key_t ftok(const char *pathname, int proj_id);;
    key = ftok("./app",'b'); //獲取唯一的 key 值,
    if(key < 0)
    {
        perror("fail ftok ");
        exit(1);
    }

    shmid = shmget(key, 128, IPC_CREAT|IPC_EXCL|0777);//創建/打開共享內存,返回id根據id映射
    if(shmid < 0)
    {
        if(errno == EEXIST)//文件存在時,直接打開文件獲取shmid
        {
            printf("file eexist");
            shmid = shmget(key,128,0777);
        }
        else
        {
            perror("shmget fail ");
            exit(1);
        }
    }
    p = (char *)shmat(shmid,NULL,0);//映射,返回地址,根據地址操作
    if( p == (char *)(-1) )
    {
        perror("shmat fail ");
        exit(1);
    }

    while(1)
    {
        sleep(1);
        printf("P:%s",p);
        if(strstr(p,"quit") != NULL) //接收到 quit 結束循環
        {
            break;
        }
    }
    shmdt(p);//解除映射

    //函數原型 int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    shmctl(shmid,IPC_RMID,NULL); //刪除
    return 0;
}

  文件2  創建進程2 實現功能,獲取終端輸入的數據寫到共享內存中,這兩個進程需要同時啟動才可以實現通信

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>

int main(int argc, const char *argv[])
{
    key_t key;
    int shmid;
    char *p = NULL;
    //    key_t ftok(const char *pathname, int proj_id);;
    key = ftok("./app",'b'); //創建key值,
    if(key < 0)
    {
        perror("fail ftok ");
        exit(1);
    }
    shmid = shmget(key,128,IPC_CREAT|IPC_EXCL|0777);//創建/打開共享內存,返回id根據id映射
    if(shmid < 0)
    {
        if(errno == EEXIST)//文件存在時,直接打開文件獲取shmid
        {
            printf("file eexist");
            shmid = shmget(key,128,0777); //共享內存存在時,直接打開
        }
        else
        {
            perror("shmget fail ");
            exit(1);
        }
    }
    p = (char *)shmat(shmid,NULL,0);//映射,返回地址,根據地址操作
    if( p == (char *)(-1) )
    {
        perror("shmat fail ");
        exit(1);
    }
    while(1) //接收到 quit 結束循環
    {
        //read 時p中 內容不會清空
        //read(0,p,10);//終端讀數據,寫入p指向的空間,讀取數據時不會清空
        fgets(p,10,stdin);
        if(strstr(p,"quit") != NULL)
        {
            break;
        }
    }
    shmdt(p);//解除映射

    //函數原型 int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    shmctl(shmid,IPC_RMID,NULL); //刪除
    return 0;
}

測試:在進程2終端下輸入數據,會在進程1 的終端下打印出來,但是一直打印(輸入quit退出循環)

 

 

例子2 :使用信號燈集訪問臨界資源(共享內存)處理上面的一直循環打印的問題

通過創建包含 2 個信號燈的燈集, 分別控制兩個進程訪問共享內存  ( 2值信號燈 )

文件 1  進程1 通過終端輸入數據寫入共享內存, 兩個信號燈,先允許通過終端 輸入數據進程操作

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>
#include <sys/sem.h>

int semid; //信號燈集id
union semnum
{
    int val; //信號燈值
};

union semnum mynum;

struct sembuf mybuf; //定義操作信號燈的結構,PV操作,哪個燈

//參數 信號燈集id 和 是哪個信號燈
void sem_p(int semid, unsigned short num) //P操作函數
{
    mybuf.sem_num = num; //第一個信號燈,(信號燈編號)
    mybuf.sem_op = -1; //進行P操作, 為 1 時表示V操作
    mybuf.sem_flg = 0; //阻塞,表示semop函數的操作是阻塞的,直到成功為止
    semop(semid, &mybuf, 1); //最后一個參數,表示操作信號燈的個數
}

//參數 信號燈集id 和 是哪個信號燈
void sem_v(int semid, unsigned short num) //V操作函數
{
    mybuf.sem_num = num;
    mybuf.sem_op = 1;  //1 表示V 操作
    mybuf.sem_flg = 0; //阻塞,表示semop函數的操作是阻塞的,直到操作成功為止
    semop(semid, &mybuf, 1);  //操作的 mybuf 全部變量信號燈集
}

int main(int argc, const char *argv[])
{
    key_t key;
    int shmid;
    char *p = NULL;

    //    key_t ftok(const char *pathname, int proj_id);;
    key = ftok("./app",'b'); //創建key值,
    if(key < 0)
    {
        perror("fail ftok ");
        exit(1);
    }

    //IPC_EXCL | IPC_CREAT 信號燈不存在就創建
    semid = semget(key, 2, IPC_CREAT|IPC_EXCL|0666); //創建信號燈,IPC_EXCL 問信號燈存不存在
    if(semid < 0)
    {
        if(errno == EEXIST)//存在時,只需要打開即可
        {
            semid = semget(key,2,0666); //打開信號燈
        }
        else
        {
            perror("semget fail ");
       exit(1); } }
else { mynum.val = 0; //設置信號燈值 semctl(semid,0,SETVAL,mynum); //初始化一個信號燈 mynum.val = 1; semctl(semid,1,SETVAL,mynum); } shmid = shmget(key,128,IPC_CREAT|IPC_EXCL|0777);//創建/打開共享內存,返回id根據id映射 if(shmid < 0) { if(errno == EEXIST)//文件存在時,直接打開文件獲取shmid { printf("file eexist"); shmid = shmget(key,128,0777); // 直接打開共享內存 } else { perror("shmget fail "); exit(1); } } p = (char *)shmat(shmid,NULL,0);//映射,返回地址,根據地址操作 if( p == (char *)(-1) ) //判斷共享內存是否正確 { perror("shmat fail "); exit(1); } while(1) { sem_p(semid, 1); // P 操作 //read 時 p 指向的共享內存中內容不會清空 //read(0,p,10);//終端讀數據,寫入p指向的空間,讀取數據時不會清空 fgets(p,10,stdin); sem_v(semid, 0); // V 操作 if(strstr(p,"quit") != NULL) //當輸入 "quit" 退出循環 { break; } } shmdt(p);//解除映射 //int shmctl(int shmid, int cmd, struct shmid_ds *buf); shmctl(shmid,IPC_RMID,NULL); //刪除共享內存 semctl(semid,0,IPC_RMID);//刪除信號燈 return 0; }

文件 2  進程2 讀取共享內存中的數據, 通過信號燈控制, 當寫操作完成時讀取一次, 然后等待下次寫入

 

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>
#include <sys/sem.h>

int semid; //信號燈集id
union semnum
{
    int val; //信號燈值
};

union semnum mynum;

struct sembuf mybuf; //定義操作信號燈的結構

//參數 信號燈集id 和 是哪個信號燈
void sem_p(int semid, unsigned short num) //P操作函數
{
    mybuf.sem_num = num; //第一個信號燈,(信號燈編號)
    mybuf.sem_op = -1; //進行P操作, 為 1 時表示V操作
    mybuf.sem_flg = 0; //阻塞,表示semop函數操作是阻塞的,直到成功為止
  //函數semop是對信號燈進行PV操作的函數,通過上面的結構體,參數,確定是P操作還是V操作,確定是對哪個信號燈的PV 操作,simid表示操作的是那個信號燈集 semop(semid, &mybuf, 1); //最后一個參數,表示操作信號燈的個數 } //參數 信號燈集id 和 是哪個信號燈 void sem_v(int semid, unsigned short num) //V操作函數 { mybuf.sem_num = num; mybuf.sem_op = 1; //1 表示V 操作 mybuf.sem_flg = 0; //阻塞,表示semop函數操作是阻塞的,直到成功為止 semop(semid, &mybuf, 1); //操作的 mybuf 全部變量信號燈集 } int main(int argc, const char *argv[]) { key_t key; int shmid; char *p = NULL; // key_t ftok(const char *pathname, int proj_id);; key = ftok("./app",'b'); //創建key值, if(key < 0) { perror("fail ftok "); exit(1); } //IPC_EXCL | IPC_CREAT 信號燈不存在就創建 semid = semget(key, 2, IPC_CREAT|IPC_EXCL|0666); //創建信號燈,IPC_EXCL 問信號燈存不存在 if(semid < 0) { if(errno == EEXIST)//存在時,只需要打開即可 { semid = semget(key,2,0666); //打開信號燈 } else { perror("semget fail "); } } else { mynum.val = 0; //設置信號燈值 semctl(semid,0,SETVAL,mynum); //初始化一個信號燈 mynum.val = 1; semctl(semid,1,SETVAL,mynum); } shmid = shmget(key,128,IPC_CREAT|IPC_EXCL|0777);//創建/打開共享內存,返回id根據id映射 if(shmid < 0) { if(errno == EEXIST)//文件存在時,直接打開文件獲取shmid { printf("file eexist"); shmid = shmget(key,128,0777); } else { perror("shmget fail "); exit(1); } } p = (char *)shmat(shmid,NULL,0);//映射,返回地址,根據地址操作 if( p == (char *)(-1) ) { perror("shmat fail "); exit(1); } while(1) { sleep(1); sem_p(semid, 0); printf("P:%s",p); sem_v(semid, 1); // write(0,p,10);//向終端寫數據,把p指向的空間的內容 if(strstr(p,"quit") != NULL) { break; } } shmdt(p);//解除映射 //int shmctl(int shmid, int cmd, struct shmid_ds *buf); shmctl(shmid,IPC_RMID,NULL); //刪除 semctl(semid,0,IPC_RMID);//刪除信號燈 return 0; }

測試:

 

例子2中通過兩個2值信號燈實現同步操作,先把寫數據到共享內存的信號燈初始化值為1,讀共享內存的信號燈初始化為0,讓寫操作的進程先操作共享內存

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM