多进程+共享内存+信号量综合实例

具体说明看注释,另外关于信号量的陷进可参考
http://os.51cto.com/art/201311/418977_all.htm

http://blog.csdn.net/killmice/article/details/41516399

测试框架

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <signal.h>

#include "myipc_sem.h"
#include "myipc_shm.h"

void Test(int var)
{

  int cnt = 0;
  int ret = 0;
  int shmdl = 0;
  int *addr = NULL;
  key_t key ;
  int semid = 0;

  //利用获取的key打开/创建信号量集合
  key = ftok("./",'a');

  //创建信号量集合--默认创建1个信号量
  ret = sem_open(key, &semid);
  if(ret == SEMERR_ENOXIST)
  {
    ret = sem_create(key, &semid);
    if(ret != 0)
    {
      printf("sem_create err\n");
      return -1;
    }
  }

  //sem_p(semid);//p操作
      printf("Aloha!Fork()....%d\n",var);
      ret = IPC_CreatShm("./", sizeof(int), &shmdl);
      if(ret != 0)
      {
        printf("IPC_CreatShm err\n");
        return -1;
      }

      ret = IPC_MapShm(shmdl,(void**)&addr);

      cnt = ++(*addr);

      printf("cnt:%d\n",cnt);

      ret = IPC_UnMapShm(addr);//显示解除映射关系
  //sem_v(semid);//v操作

  printf("child: %d exit!\n=================================================================\n",getpid());   
}

int main()
{
  int loop_num = 0;
  int proc_num = 0;
  int i = 0,j = 0;
  pid_t pid ;

  int  ret = 0;
  int  shmdl = 0; 
  key_t key = 0; 
  int semid = 0;

  signal(SIGCHLD,SIG_IGN);
  printf("Input the procnum:\n");
  scanf("%d",&proc_num);

  printf("Input the loopnum:\n");
  scanf("%d",&loop_num);

  //创建共享内存--父进程创建的时候使用的key和子进程一样,
  //所以即使在子进程再次创建,实则是获取已存在的共享内存--所以是同一块共享内存
  //当然可以在子进程里面不再重新调用创建共享内存的函数(实际上也没有真的创建)
  //可以通过向子进程传递已经创建好的共享内存ID--让他映射到自己的内存空间
  //就算父进程没有创建,众多子进程创建了,只要key一样--就是同一块共享内存
  ret = IPC_CreatShm("./", sizeof(int), &shmdl);
  if(ret != 0)
  {
    printf("IPC_CreatShm err\n");
    return -1;
  }


  //利用获取的key打开/创建信号量集合
  key = ftok("./",'a');

  //创建信号量集合--默认创建1个信号量
  //尽管子进程再次调用该部分代码--表面上重新创建信号量
  //但由于key相同,系统里面已经有父进程创建的信号量,所以他们是同一个信号量集合
  //原理和父子进程创建共享内存一样
  ret = sem_open(key, &semid);
  if(ret == SEMERR_ENOXIST)
  {
    ret = sem_create(key, &semid);
    if(ret != 0)
    {
      printf("sem_create err\n");
      return -1;
    }
  }

  int val= 1;

  ret = sem_set(semid, val);//设置信号量计数值--默认设置第1个,下标位0
  if(ret == -1)
  {
    printf("sem_set err\n");
    return -1;
  }

  ret = sem_get(semid, &val);//获取信号量计数值
  if(ret != 0)
  {
    printf("sem_get err\n");
    return -1;
  }

  printf("val:%d\n",val);

  for(i= 0; i < proc_num;i++)
  {
    pid = fork();
    if(0 == pid)
    { 
      sem_p(semid);//p操作
          printf("child : %d\tparent : %d\n",getpid(),getppid());//父进程如果退出以后,会将创建的子进程托孤给init进程--1号进程--父进程的PID就是1 
          for(j = 0;j < loop_num;j++)
          {
            Test(j);
          }
      sem_v(semid);//v操作
      //一旦子进程完成任务立即结束子进程--避免子进程执行下一次循环的fork--避免子生孙
      //进程结束的同时,Linux内核还会自动撤销当前进程与共享内存的映射关系
      exit(0);

      //如果进程不结束,则映射关系不会自动解除,除非显示调用shmdt函数,
      //则ipcs命令看到的ntach选项就是所有进程的数量--表示多少个进程与该块内存连接
      //while(1);
    }
  }
  wait(NULL);

  IPC_DelShm(shmdl);
  return 0;
}

信号量模块

头文件


#ifndef __MYIPC_SEM_H__
#define __MYIPC_SEM_H__

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>

#include <sys/time.h>
#include <sys/mman.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>

#define SEM_NUM 1

#define SEMERR_BASE 100
#define SEMERR_PARAM (SEMERR_BASE+1)
#define SEMERR_EXIST (SEMERR_BASE+2)
#define SEMERR_ENOXIST (SEMERR_BASE+3)


typedef 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) */
}semun_t;

#if 0
typedef struct sembuf{
  unsigned short sem_num;  /* semaphore number */
  short          sem_op;   /* semaphore operation */
  short          sem_flg;  /* operation flags */
}sembuf_t;
#endif

typedef struct sembuf sembuf_t;

int sem_create(key_t key,int *semid);
int sem_open(key_t key,int *semid);
int sem_set(int semid,int val);
int sem_get(int semid,int* val);
int sem_p(int semid);
int sem_v(int semid);

#endif

实现文件

#include "myipc_sem.h"

//封装创建信号量集合的代码
int sem_create(key_t key,int *semid)
{
  int sem_id = 0;
  int ret = 0;

  if(semid == NULL)
  {
    ret = SEMERR_PARAM;
    perror("argv err\n");
    return ret;
  }
  sem_id = semget(key,SEM_NUM,0666|IPC_CREAT|IPC_EXCL);
  if(sem_id == -1)
  {
    ret = SEMERR_EXIST;
    perror("semget");
    if(errno == EEXIST)
    {
      printf("Judge by self..exist\n");
    }
    return ret;
  }
  *semid = sem_id;

  return ret;
}

//封装打开已存在的信号量集合
int sem_open(key_t key,int *semid)
{
  int sem_id = 0;
  int ret = 0;

  if(semid == NULL)
  {
    ret = SEMERR_PARAM;

    perror("argv err\n");
    return ret;
  }
  sem_id = semget(key,SEM_NUM,0666);
  if(sem_id == -1)
  {
    if(errno == ENOENT)
    {
      printf("Judge by self..exist\n");
      ret = SEMERR_ENOXIST;
      return ret;
    }
  }
  *semid = sem_id;

  return ret;
}

//设置信号量集合里面的信号量的计数值
int sem_set(int semid,int val)
{
  int ret = 0;

  if(semid < 0)
  {
    return -1;
  }
  semun_t su;
  su.val = val;
  ret = semctl(semid,0,SETVAL,su);
  return ret;
}

//获取信号量集合里面的信号量的计数值
int sem_get(int semid,int* val)
{
  int ret = 0;
  if(semid < 0)
  {
    return -1;
  }
  semun_t su;
  ret = semctl(semid,0,GETVAL,su);//这里需要特别注意
  if(ret == -1)//返回-1表示semctl失败--直接返回
    return ret;
  else//非零表示获取成功--但是返回值需要设置位0--上层应用收到0表示调用成功
    ret = 0;
  *val = su.val;//将获取到的值甩出去到上层应用
  return ret;
}


//原子p操作
int sem_p(int semid)
{
  int ret = 0;

  sembuf_t buf = {0,-1,0};//一般默认是从第0个信号量开始,第三个参数默认是0表示阻塞模式

  ret = semop(semid,&buf,1 );//只操作一个信号量

  return ret;
}

//原子v操作
int sem_v(int semid)
{
  int ret = 0;

  sembuf_t buf = {0,+1,0};//一般默认是从第0个信号量开始,第三个参数默认是0表示阻塞模式

  ret = semop(semid,&buf,1);//只操作一个信号量

  return ret;
}

共享内存模块

头文件


#ifndef __MYIPC_SHM_H__
#define __MYIPC_SHM_H__

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define _OS_LINUX_

#if defined _OS_LINUX_
#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 "myipc_shm.h" 

#endif


#ifdef __cplusplus 
extern "C" {
#endif

int IPC_CreatShm(char *shmseedfile, int shmsize, int *shmhdl);

int IPC_MapShm(int shmhdl,void **mapaddr);

int IPC_UnMapShm(void *unmapaddr);

int IPC_DelShm(int shmhdl);


#ifdef __cplusplus
}
#endif
#endif


实现文件


#include "myipc_shm.h"

int shmflag = 0;
int shmkey;

/***********************************************************************
  功能描述:    创建共享内存
  参数说明:    shmname  [in]  是共享内存名,系统中唯一标志
                shmsize  [in]  是要创建的共享内存的大小;
                shmhdl   [out] 共享内存的句柄.
  返回值:      返回0函数执行成功;非0返回错误码
************************************************************************/
int IPC_CreatShm(char *shmseedfile, int shmsize, int *shmhdl)
{
    if(shmflag == 0)            //判断接口中共享内存key是否已经存在
    {
        shmkey = ftok(shmseedfile, 'c');
        if (shmkey == -1)
        {
            perror("ftok");
            return -1;
        }

        shmflag = 1;
    }

    //创建共享内存
    *shmhdl = shmget(shmkey,shmsize,IPC_CREAT|0666);
    if (*shmhdl == -1)          //创建失败
        return -2;
    return 0;

}
/***********************************************************************
  功能描述:    关联共享内存
  参数说明:    shmhdl   [in]  共享的句柄
                mapaddr [out] 共享内存首地址
  返回值:      返回0函数执行成功;非0返回错误码
************************************************************************/
int
IPC_MapShm(int  shmhdl, void  **mapaddr)
{
    void *tempptr = NULL;

    //连接共享内存
    tempptr = (void *)shmat(shmhdl,0,SHM_RND);
    if (tempptr == (void*)-1)       //共享内存连接失败
        return -1;
    *mapaddr = tempptr;         //导出共享内存首指针

    return 0;
}
/***********************************************************************
  功能描述:    取消共享内存关联
  参数说明:    unmapaddr   [in] 共享内存首地址
  返回值:      返回0函数执行成功;非0返回错误码
************************************************************************/
int IPC_UnMapShm(void *unmapaddr)
{
    int  rv;
    //取消连接共享内存 
    rv = shmdt((char *)unmapaddr);
    if (rv == -1)           //取消连接失败
        return -1;

    return 0;
}
/***********************************************************************
  功能描述:    删除共享内存
  参数说明:    shmhdl   [in]  共享的句柄
  返回值:      返回0函数执行成功;非0返回错误码
************************************************************************/
int IPC_DelShm(int shmhdl)
{
    int  rv;
    //删除共享内存
    rv = shmctl(shmhdl,IPC_RMID,NULL);
    if(rv < 0)              //删除共享内存失败
        return -1;
    return 0;
}

Makefile文件

CC=gcc
CFLAGS=-Wall -g
BIN=app 
.PHONY:clean all 

all:$(BIN)
$(BIN):mulfork.o myipc_sem.o myipc_shm.o
    $(CC) $(CFLAGS) $^ -o $@
%.o:%.c 
    $(CC) $(CFLAGS) -c $^ -o $@

clean:
    rm -f *.o $(BIN)

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值