1. 程式人生 > >IPC一個綜合小實踐

IPC一個綜合小實踐

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一個綜合小實踐