IPC一個綜合小實踐
阿新 • • 發佈:2018-06-15
signal semi 讀寫 正常 ecif turn clu nbsp 刪除
多進程讀寫共享內存使用信號量進行同步實現鏈接訪問技術功能
1 共享內存相關操作API
2 信號量相關操作API
3 多進程測試框架
4 將上面的內容結合起來
定義共享內存的API操作
//ipc_shm.h #ifndef _IPC_SHM_H_ #define _IPC_SHM_H_ #ifdef __cplusplus extern "C" { #endif int shm_creat(char *shmseedfile, int shmsize, int *shmhdl); int shm_map(int shmhdl,void **mapaddr); int shm_unmap(void*unmapaddr); int shm_delete(int shmhdl); #ifdef __cplusplus } #endif #endif
// ipc_sem.c
#include <stdio.h> #include <errno.h> #include <unistd.h> #include <memory.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/sem.h> #include <sys/msg.h> #include"ipc_shm.h" //兩個全局變量,用來作為標識 int shmflag = 0; int shmkey; int shm_creat(char *shmseedfile, int shmsize, int *shmhdl) { if(shmflag == 0) //判斷接口中共享內存key是否已經存在 { shmkey = ftok(shmseedfile, ‘a‘); if (shmkey == -1) { perror("ftok"); return -1; } shmflag = 1; } //創建共享內存 int ret = shmget(shmkey,shmsize,IPC_CREAT | 0666); if (ret == -1) //創建失敗 return -1; *shmhdl = ret; return 0; } int shm_map(int shmhdl, void **mapaddr) { void *tempptr = NULL; //連接共享內存 tempptr = (void *)shmat(shmhdl,0,SHM_RND); if ((int)tempptr == -1) //共享內存連接失敗 return -1; *mapaddr = tempptr; //導出共享內存首指針 return 0; } int shm_unmap(void *unmapaddr) { int rv; //取消連接共享內存 rv = shmdt(unmapaddr); if (rv == -1) //取消連接失敗 return -1; return 0; } int shm_delete(int shmhdl) { int rv; //刪除共享內存 rv = shmctl(shmhdl,IPC_RMID, NULL); if(rv < 0) //刪除共享內存失敗 return -1; return 0; }
//ipc_shm.h
#ifndef _IPC_SEM_H_ #define _IPC_SEM_H_ #ifdef __cplusplus extern "C" { #endif int sem_creat(int key, int *semid); int sem_delete(int key); int sem_open(int key, int *semid); int sem_setval(int semid, int val); int sem_getval(int semid, int *val); int sem_p(int semid); int sem_v(int semid); #ifdef __cplusplus } #endif #endif
// ipc_shm.c
#include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/sem.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <sys/ipc.h> #include <sys/sem.h> #include "ipc_sem.h" //用來操作信號量值的聯合體 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) */ }; // 成功返回0,失敗返回其他 // 如果沒有就創建信號量,並且設置信號,如果有就報錯,下次調用sem_open函數打開信號量,同時也設置信號量。 int sem_creat(int key, int *semid) { if (semid == NULL) { printf("sem_creat() Param Err.\n"); return -1; } int ret = semget(key, 1, 0666| IPC_CREAT | IPC_EXCL); if (ret == -1) { if (errno == EEXIST) { printf("semget() EXIST Err.\n"); return -1; } } *semid = ret; ret = sem_setval(*semid, 1); if (ret != 0) { printf("sem_setval() Err : %d\n", ret); return ret; } return 0; } int sem_delete(int key) { int semid; int ret = sem_open(key, &semid); if(ret < 0) { return -1; } ret = semctl(semid, IPC_RMID, 0); if(ret < 0) { return -1; } return 0; } int sem_open(int key, int *semid) { if (semid == NULL) { printf("sem_creat() Param Err.\n"); return -1; } int ret = semget(key, 0, 0); if (ret == -1) { printf("sem_open() Err:%d\n", errno); return ret; } *semid = ret; return 0; } int sem_setval(int semid, int val) { union semun su; su.val = val; return semctl(semid, 0, SETVAL, su); } int sem_getval(int semid, int *val) { int ret = semctl(semid, 0, GETVAL); *val = ret; return 0; } //信號量P操作時候,需要傳遞好幾個信息給linux內核,所以linux內核定義了一個結構 //操作信號量集的下標 0 //執行什麽操作 -1 +1 //按照什麽策略執行操作 0 UNDO NOWAITing int sem_p(int semid) { struct sembuf buf = {0, -1, 0}; return semop(semid, &buf, 1); } int sem_v(int semid) { struct sembuf buf = {0, 1, 0}; return semop(semid, &buf, 1); }
#include <sys/types.h> #include <unistd.h> #include <signal.h> #include <errno.h> #include <sys/wait.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include "ipc_sem.h" #include "ipc_shm.h" int g_key = 0x1234; // 進程調用的執行函數 int doServeice() { //開始使用信號量來創造臨界區 int semid = 0; sem_open(g_key, &semid); sem_p(semid); //臨界區開始 int shmhdl = 0; int ret = shm_creat(".", 0, &shmhdl); if(ret!=0) { return ret; } int *addr = NULL; ret =shm_map(shmhdl, (void **)&addr); if(ret!=0) { return ret; } // 進程訪問數+1 *((int *)addr) += 1; //統計總共訪問的次數。 int ncount = *((int *)addr); printf("ncount: %d\n", ncount); ret =shm_unmap(addr); //sleep(1); sem_v(semid); //臨界區開始 } int main() { int procnum; printf("輸入創建子進程的個數 : \n"); scanf("%d", &procnum); int loopnum; printf("輸入每個子進程測試多少圈 :\n"); scanf("%d", &loopnum); //共享內存創建 int shmhdl = 0; int ret = shm_creat(".", sizeof(int), &shmhdl); if (ret != 0) { printf("shm_creat() Err.\n"); return ret; } //共享內存創建成功 //信號量的創建 int semid = 0; ret = sem_creat(g_key, &semid); if (ret != 0) { printf("sem_creat() Err. Try Again.\n"); if (errno == EEXIST) { ret = sem_open(g_key, &semid); if (ret != 0) { printf("Failed Again!\n"); return ret; } } else { return ret; } } int val = 0; ret = sem_getval(semid, &val); if (ret != 0 ) { printf("sem_getval() Err.\n"); return ret; } printf("sem val: %d\n", val); getchar(); pid_t pid; for (int i=0; i<procnum; i++) { pid = fork(); //有幾個fork就有幾個子進程 if (pid == 0) { for (int j=0; j<loopnum; j ++) { int rev = doServeice(); if(rev!=0) { return -1; } } exit(0); } } //用來等待子進程的結束,防止僵屍進程的產生, 為了不讓父進程死亡,采用這種方式 while(1) { if((pid = waitpid(-1, NULL, WNOHANG)) > 0) { printf("子進程正常退出:%d\n", pid); } else { if(pid == -1) { if(errno == EINTR) continue; else break; } } } // 釋放資源 shm_delete(shmhdl); sem_delete(g_key); return 0; }
IPC一個綜合小實踐