IPC一个综合小实践

Posted randyniu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IPC一个综合小实践相关的知识,希望对你有一定的参考价值。

多进程读写共享内存使用信号量进行同步实现链接访问技术功能

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一个综合小实践的主要内容,如果未能解决你的问题,请参考以下文章

Android 中IPC实践

zeromq实践

AIDL 定向tag IPC Parcelable综合案例

# 2017-2018-1 20155302 课下实践IPC

C程序IPC消息

20191130 实验四 Python综合实践