首页 > 代码库 > IPC(shm+sem)

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函数库参考手册》