進程間通信之SystemV IPC--共享內存和信號量

共享內存:最快的可用IPC形式,一旦這樣的內存區映射到共享它的進程的地址空間,這些進程間數據的傳輸就不再涉及內核。然而往該共享內存區存放信息或從中取走信息的進程間通常需要某種形式的同步。(不涉及內核:進程不是通過執行任何進入內核的系統調用來傳遞彼此的數據)。

在消息隊列處實現過一個服務器和客戶機通信的例子:簡單的客戶端---服務器例程

下圖展示了從服務器到客戶機的文件數據流向:


當我們使用共享內存區對對文件進行拷貝時,文件數據的流向如下圖:


圖中的數據之拷貝了兩次,即只涉及了兩次內核訪問:一次從輸出文件到共享內存,一次從共享內存區到輸出文件。

創建和打開共享內存:

int shmget(key_t key, size_t size, int shmflg);             //size--分配共享內存段的大小(N * 4K)        shmflg--創建(IPC_CREAT|0644)已存在(置0)

返回值:成功返回共享存儲區ID,出錯返回-1

說明: 參數size是該共享存儲段的長度。實現通常將其向上取爲系統頁長的整數倍。但是,若指定的size值並非系統頁長的整數倍,那麼最後一頁的餘下部分是不可用的。如果正在創建一個新段(一般是在服務器中),則必須指定其size。如果正在引用一個現存的段(一個客戶進程),則將size指定爲0,當創建一釁端時,段內的內容初始化爲0

刪除共享內存:

int shmctl(int shmid, int cmd, struct shmid_ds *buf);//cmd--置爲IPC_RMID,          buf---不關心(置0)

返回值:成功返回0,出錯返回-1

將共享內存掛載到自己的內存空間中:

void *shmat(int shmid, const void *shmaddr, int shmflg);             //shmaddr--一般置爲NULL,    shmflg-- 一般置爲0

返回值:  成功返回掛載的內存空間的起始地址,出錯返回-1

卸載共享內存:

 int shmdt(const void *shmaddr);                  //形參爲shmat函數的返回地址

返回值:成功返回0,出錯返回-1

案例程序:分別實現讀寫進程對共享內存操作

創建共享內存:

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

int main()
{
    int num = 10;

    int id  = shmget(ftok(".", 'a'), sizeof(int), IPC_CREAT|0644);
    if(-1 == id)
    {
        perror("shmget");
        exit(1);
    }
    
    printf("creat ok\n");
    return 0;
}

讀進程:

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

int main()
{
    int id  = shmget(ftok(".", 'a'), sizeof(int), IPC_CREAT|0644);
    if(-1 == id)
    {
        perror("shmget");
        exit(1);
    }
    
    int *p = (int*)shmat(id, NULL, 0);
    while(1)
    {
        printf("read = %d\n", *p);
        usleep(1000);
    }
    return 0;
}

寫進程:

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

int main()
{
    int id  = shmget(ftok(".", 'a'), sizeof(int), IPC_CREAT|0644);
    if(-1 == id)
    {
        perror("shmget");
        exit(1);
    }
    
    int *p = (int*)shmat(id, NULL, 0);
    int i = 1;
    while(1)
    {
        *p = i++;
        printf("%d write ok\n", i);
        sleep(1);
    }
    return 0;
}

刪除共享內存:

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

int main()
{
    int id = shmget(ftok(".", 'a'), sizeof(int), 0);
    if(-1 == id)    perror("shmget"), exit(1);

    if(-1 == shmctl(id, IPC_RMID,0))
    {
        perror("shmctl");
        exit(1);
    }
    return 0;
}
Makefile:
.PHONY: clean all

all : rd wr ctl cr

cl=rd wr ctl cr

rd : read.o
	cc $^ -o $@
wr : write.o
	cc $^ -o $@
ctl : shmctl.o
	cc $^ -o $@
cr : creat.o
	cc $^ -o $@
%.o:%.c
	cc -c $^ -o $@

clean:
	rm -rf *.o $(cl)
信號量:信號量是一個計數器,用於多進程對共享數據對象的訪問。即主要用於解決同步和互斥問題。信號量解決同步和互斥原語

信號量集的創建:

int semget(key_t key, int nsems, int semflg);                  //nsems---信號集中有幾個信號量(默認爲1,已存在打開時置0)      semflg--- 創建IPC_CREAT|0644   存在(置0)

返回值:成功返回信號量ID,出錯返回-1

信號量集的操作:設置信號量初值

union semun {                                      //設置初值時只用到val
               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 semctl(int semid, int semnum, int cmd, ...);                   //semnum---第幾個信號量0,1,2……               cmd---SETVAL            semnum的值

返回值:成員semun的val的值

信號量集的操作:得到信號量初值

int semctl(int semid, int semnum, int cmd);                   //semnum---第幾個信號量0,1,2……               cmd---GETVAL  

返回值:成員semun的val的值

實現PV操作:

struct sembuf{
                unsigned short sem_num;	                      //要操作第幾個信號量
                short 	sem_op;	                              //  -1     或     1
                short   sem_flg;                              //   0   指定IPC_NOWAIT(出錯返回EAGAIN)
};
int semop(int semid, struct sembuf *sops, unsigned nsops);        //sops---信號量操作數組起始地址            nops規定數組的大小

返回值:成功返回0,出錯返回-1

PV操作保護重要斷碼段:


案例程序:

創建信號量集並設置初值:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/sem.h>

union semun
{
    int value;
};

int main()
{
    int num;
    union semun su;
    int id = semget(123, 1, IPC_CREAT|0644);
    if(-1 == id) perror("semget"), exit(1);
    
    printf("設置初值:\n");
    scanf("%d", &num);
    su.value = num;
    if(-1 == semctl(id, 0, SETVAL, su))
    {
        perror("semctl");
        exit(1);
    }

    return 0;
}
P操作:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/sem.h>

void p(int id)
{
    struct sembuf sb[1];
    sb[0].sem_num = 0;
    sb[0].sem_op = -1;
    sb[0].sem_flg = 0;
    if(semop(id, sb, 1) == -1)
    {
        perror("semop");
            exit(1);
    }   
}

int main()
{
    int id = semget(123, 0, 0);
    if(-1 == id) perror("semget"), exit(1);

    p(id);

    return 0;
}
V操作:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/sem.h>

void v(int id)
{
    struct sembuf sb[1];
    sb[0].sem_num = 0;
    sb[0].sem_op = 1;
    sb[0].sem_flg = 0;
    if(semop(id, sb, 1) == -1)
    {
        perror("semop");
            exit(1);
    }   
}

int main()
{
    int id = semget(123, 0, 0);
    if(-1 == id) perror("semget"), exit(1);

    v(id);

    return 0;
}
獲得信號量值:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int main()
{
    int id = semget(123, 0, 0);
   if(-1 == id)
   {
       perror("semget");
       exit(1);
   }
    int ret = semctl(id, 0, GETVAL);
    printf("ret = %d\n", ret);

    return 0;
}
刪除信號量集:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/sem.h>


int main()
{
    int id = semget(123, 1, IPC_CREAT|0644);
    if(-1 == id) perror("semget"), exit(1);
    
    if(-1 == semctl(id, 0, IPC_RMID))
    {
        perror("semctl");
        exit(1);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章