IPC(shm+sem)

   共享内存是两个或多个进程共享同一块内存区域,并通过该内存区域实现数据交换的进程间通信。虽然共享内存是进程间通信的最快速的机制,但是进程间的同步问题靠自身难以解决,于是就需要信号量机制,信号量能很好的解决互斥资源的同步问题。这些牵涉到操作系统里的知识,要好好研究一番同步互斥问题才能继续。

 

共享内存的工作模式一般是:

1.     int shmget(key_t key, int size, int shmflg);
创建或取得一块共享内存

2.     void *shmat(int shmid, const void *shmaddr, int shmflg);
将shmid所指共享内存和当前进程连接(attach)

3.     要做的事。。。。

4.     int shmdt(const void *shmaddr);
将先前用shmat连接好的共享内存分离(detach)当前的进程

5.     int shmctl(int shmid ,int cmd, struct shmid_ds *buf)
把cmd设成IPC_RMID删除共享内存及其数据结构

 

附加说明:

1.     在经过fork()后,子进程将继承已连接的共享内存地址

2.     在经过exec()后,已连接的共享内存地址会自动detach

3.     在结束进程后,已连接的共享内存地址会自动detach

 

信号量大致是如此的:

      1. int semget(key_t key, int nsems, int semflg);
         创建或获取信号队列

    2. int semctl(int semid, int semnum, int cmd, union semun arg);
         控制信号队列,如cmd=SETVAL设置信号量的值; 或者cmd=IPC_STAT获得          semid_ds结构

3. int semop(int semid, struct sembuf *sop, unsigned nsops);
       信号处理

4. 完后将semctl的cmd设置为IPC_RMID将信号队列删除 

 


/*一个例子程序,来自《linux环境下C编程指南》,有小改动:
 *简单的服务器和客户端程序,启动不带参数运行服务器,带参数则是客户端
 *服务器启动后,创建信号量和共享内存,并将共享内存的引用ID显示出来,
 * 将信号量的引用ID放在共享内存中,利用服务器端提供的共享内存引用ID
 *将共享内存附加到地址段,读取信号量以实现两个进程之间的同步,之后,这两个进程就可 *利用共享内存进行进程间通信,客户端输入的信息将在服务器端显示出来
 */



#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>

#define SHMDATASZ 1000
#define BUFSZ (SHMDATASZ-sizeof(int))
#define SN_EMPTY 0
#define SN_FULL 1

int delete_semid = 0;
union semun
{
  int val;
  struct semid_ds *buf;
  ushort array;
};

void server(void);
void client(int shmid);
void delete(void);
void sigdelete(int signum);
void locksem(int semid, int semnum);
void unlocksem(int semid, int semnum);
void clientwrite(int shmid, int semid, char *buffer);

int main(int argc, char *argv[])
{
  if(argc < 2)
    server();
  else
    client(atoi(argv[1]));
  return 0;
}

void server(void)
{
  union semun sunion;
  int semid, shmid;
  void *shmdata;
  char *buffer;
  
  //创建2个信号量
  semid = semget(IPC_PRIVATE, 2, SHM_R|SHM_W);
  if(semid == -1)
    perror("semget");
  delete_semid = semid;


 // 当接收到SIGINT信号时删除信号量并终止程序
  atexit(&delete);
  signal(SIGINT, &sigdelete);

  //设EN_EMPYT的信号值为1
  sunion.val = 1;
  if(semctl(semid, SN_EMPTY, SETVAL, sunion) == -1)
    perror("semctl");
  sunion.val = 0;
  //设EN_FULL的信号值为0,说明没有一个资源可用
  if(semctl(semid, SN_FULL, SETVAL, sunion) == -1)
    perror("semctl");

  //创建一块共享内存
  shmid = shmget(IPC_PRIVATE, SHMDATASZ, IPC_CREAT|SHM_R|SHM_W);
  if(shmid == -1)
    perror("shmget");

  //附加到shmdata
  shmdata = shmat(shmid, 0, 0);
  if(shmdata == (void*)-1)
    perror("shmat");

  //删除共享内存,刚刚创建还没用呢,就删了,不明白
// if(shmctl(shmid, IPC_RMID, NULL) == -1)
  // perror("shmctl");


  //把信号标识符放在首地址
  *(int*)shmdata = semid;
  buffer = shmdata + sizeof(int);  //后面是缓冲区

  printf("Server is running with SHM id ** %d **\n", shmid);
  while(1)
  {
    printf("waiting until full...");
    fflush(stdout);


    //申请一个资源
    locksem(semid, SN_FULL);
    printf("done\n");
    printf("message received :%s\n", buffer);
    //释放用完的资源
    unlocksem(semid, SN_EMPTY);
  }
}
void client(int shmid)
{
  int semid;
  void *shmdata;
  char *buffer;
  
  //把server创建的共享内存附加到shmdata
  shmdata = shmat(shmid, 0, 0);
  //取出信号标识符
  semid = *(int*)shmdata;
  //再找到缓冲区
  buffer = shmdata+sizeof(int);
  printf("client operational: shm id is %d, sem id is %d\n", shmid, semid);
  while(1)
  {
    char input[3];
    printf("\n\nMenu\n1.Send a message\n");
    printf("2.Exit\n");
    fgets(input, sizeof(input), stdin);
    switch(input[0])
    {
      case '1':clientwrite(shmid, semid, buffer);
               break;
      case '2':exit(0);
               break;
    }
  }
}

void clientwrite(int shmid, int semid, char *buffer)
{
  printf("waiting until empty..");
  fflush(stdout);


  //申请EMPTY资源
  locksem(semid, SN_EMPTY);
  printf("Enter Message:");
  fgets(buffer, BUFSZ, stdin);
  //释放资源
  unlocksem(semid, SN_FULL);
}

void delete()
{
  printf("\nMaster exiting; deleting semaphore %d\n", delete_semid);
  if(semctl(delete_semid, IPC_RMID, 0) == -1)
    perror("release semaphore");
}

void sigdelete(int num)
{
  exit(0);
}

void locksem(int semid, int semnum)
{
  struct sembuf sb;
  sb.sem_num = semnum;
  sb.sem_op = -1;
  sb.sem_flg = SEM_UNDO;
  semop(semid, &sb, 1);
}

void unlocksem(int semid, int semnum)
{
  struct sembuf sb;
  sb.sem_num = semnum;
  sb.sem_op = 1;
  sb.sem_flg = SEM_UNDO;
  semop(semid, &sb, 1);
}


参考资料《linux环境下C编程指南》《Linux C函数库参考手册》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
#include<stdio.h> #include<stdlib.h> #include<sys/ipc.h> #include<sys/shm.h> #include<sys/sem.h> #include<string.h> typedef struct _test{ int a_val; int b_val; int a_flag; int b_flag; int game_no; int stage; }test; int pk[3][3] = {0,-1,1,1,0,-1,-1,1,0}; void sem_p(); void sem_v(); void set_sem(); void del_sem(); int sem_id; union semun{ int val; struct semid_ds *buf; unsigned short *arry; }; int main(){ int shmid; test* shm; shmid = shmget((key_t)1236,sizeof(test),0666|IPC_CREAT); if(shmid == -1){ printf("shmget failed\n"); exit(EXIT_FAILURE); } printf("%d",shmid); shm = shmat(shmid,0,0); if (shm == (void*)-1){ printf("shmat failed\n"); exit(EXIT_FAILURE); } printf("\nMemory attached at %X\n",(int)shm); sem_id = semget((key_t)3000,1,0666|IPC_CREAT); set_sem(); int no=0,debug=0,a,b; shm->a_flag=0; shm->a_val = -2; shm->b_flag=0; shm->b_val = -2; shm->game_no=1; shm->stage=0; while(1){ sem_p(); //printf("a:%d b:%d\n",shm->a_val,shm->b_val); sleep(1); if(shm->game_no==-1){ sem_v(); break; } if (shm->stage==0){ if(no!=shm->game_no){ no = shm->game_no; printf("-------------------\n"); printf("game_no:%d\n",no); } if(shm->a_flag==1 && shm->b_flag==1) shm->stage=1; } else if(shm->stage==1){ printf("a:%d\n",shm->a_val); printf("b:%d\n",shm->b_val); a = pk[shm->a_val][shm->b_val]; b = pk[shm->b_val][shm->a_val]; shm->a_val=a; shm->b_val=b; shm->a_flag=0; shm->b_flag=0; shm->stage=2; } else if(shm->stage==2){ if(shm->a_flag==1 && shm->b_flag==1){ shm->stage=0; shm->game_no++; shm->a_flag=0; shm->b_flag=0; printf("-------------------\n"); if(shm->game_no > 100) shm->game_no=-1; } } sem_v(); } shmdt(shm); int ret=0; ret = shmctl(shmid,IPC_RMID,NULL); if(ret<0){ printf("shmctl error!\n"); } del_sem(); printf("finish"); } void set_sem(){ union semun sem_union; sem_union.val=1; semctl(sem_id,0,SETVAL,sem_union); } void del_sem(){ union semun sem_union; semctl(sem_id,0,IPC_RMID,sem_union); } void sem_p(){ struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_op = -1; sem_b.sem_flg = SEM_UNDO; semop(sem_id,&sem_b,1); } void sem_v(){ struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_op = 1; sem_b.sem_flg = SEM_UNDO; semop(sem_id,&sem_b,1); }
06-08

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值