具体说明看注释,另外关于信号量的陷进可参考
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)