linux進程間通訊--共享內存

共享內存 :內核空間預留出來的一塊內存,用於進程間通訊linux

共享內存是一種最高效的進程間通訊方式,由於進程能夠直接讀寫內存,不須要任何數據的複製,爲了在進程間交換信息,內核專門留出了一塊內存區,這段內存能夠由要訪問的進程將其映射到本身的私有地址空間,所以,進程就能夠直接讀寫這一內存而不須要進行數據的複製,從而大大提升了效率,固然,因爲多個進程共享一段內存,所以也須要依靠某種同步機制,如互斥鎖和信號量等對象

(1)int shmget(key_t key, size_t size, int shmflg);blog

功能:獲取共享內存段的ID進程

參數:
  @key IPC_PRIVATE 或 ftok()
  @size 申請的共享內存段大小 [4k的倍數]
  @shmflg IPC_CREAT | 0666 或 IPC_CREAT | IPC_EXCL 內存

返回值:
成功返回ID,失敗返回-1 ci

(2)void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:映射共享內存到用戶空間
參數:
@shmid 共享內存段ID
@shmaddr NULL:系統自動完成映射
@shmflg:   SHM_RDONLY:只讀   0:讀寫
返回值:
成功返回映射後的地址,失敗返回(void *)-1資源

(3)int shmdt(const void *shmaddr);
功能:撤銷映射
參數:
@shmaddr 共享內存映射的地址 get

注意:當一個進程結束的時候,它映射共享內存,會自動撤銷映射cmd


(4)int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:根據命令控制共享內存
參數:
@shmid 共享內存段的ID
@cmd IPC_STAT[獲取屬性],IPC_SET[設置屬性],IPC_RMID[刪除IPC對象]
@buf 保存屬性
返回值:
成功返回0,失敗返回 -1 同步

----------------------------------------------------------------------------
注意:當咱們調用shmctl刪除共享內存的時候,並不會當即刪除。只有當共享內存映射
次數爲0,纔會刪除共享內存對象
-----------------------------------------------------------------------------

實例以下:

shm_r.c

 

#define SHM_SIZE 4096

typedef struct{
  int read_flag;
  int write_flag;
  char mtxt[1];//標示數據存放地址
}data_t;

int shm_init(const char *pathname,int id)
{
  int shmid;
  key_t key;

  key = ftok(pathname,id);
  if(key < 0){
    fprintf(stderr,"Fail to ftok : %s!\n",strerror(errno));
    exit(EXIT_FAILURE);
  }

  shmid = shmget(key,SHM_SIZE,IPC_CREAT | 0666);
  if(shmid < 0){
    fprintf(stderr,"Fail to shmget : %s\n",strerror(errno));
    exit(EXIT_FAILURE);
  }

  return shmid;
}

void read_shm(void *addr)
{
  data_t *pdata;
  pdata = (data_t *)addr;

  pdata->read_flag = 0;
  pdata->write_flag = 1;

while(1){
    if(pdata->read_flag){
    printf("Read : %s!\n",pdata->mtxt);
    pdata->write_flag = 1;
    pdata->read_flag = 0;
  }
 }

}

//./a.out pathname
int main(int argc, const char *argv[])
{
  int shmid;
  void *addr;

  if(argc < 2){
    fprintf(stderr,"Usage : %s <pathname> \n",argv[0]);
    exit(EXIT_FAILURE);
  }

  shmid = shm_init(argv[1],'k');
  addr = (void *)shmat(shmid,NULL,0);
  if(addr == (void *)-1){
    fprintf(stderr,"Fail to shmat : %s\n",strerror(errno));
    exit(EXIT_FAILURE);
  }

  read_shm(addr);

  return 0;
}

 

shm_w.c

#define SHM_SIZE 4096

typedef struct{
  int read_flag;
  int write_flag;
  char mtxt[1];//標示數據存放地址
}data_t;

int shm_init(const char *pathname,int id)
{
  int shmid;
  key_t key;

  key = ftok(pathname,id);
  if(key < 0){
    fprintf(stderr,"Fail to ftok : %s!\n",strerror(errno));
    exit(EXIT_FAILURE);
  }

  shmid = shmget(key,SHM_SIZE,IPC_CREAT | 0666);
  if(shmid < 0){
    fprintf(stderr,"Fail to shmget : %s\n",strerror(errno));
    exit(EXIT_FAILURE);
  }

  return shmid;
}

void write_shm(void *addr)
{
  data_t *pdata;
  pdata = (data_t *)addr;

  pdata->read_flag = 0;
  pdata->write_flag = 1;

  while(1){
    if(pdata->write_flag){
    fgets(pdata->mtxt,SHM_SIZE - 8,stdin);//讀寫標誌共佔8個字節
    pdata->mtxt[strlen(pdata->mtxt) - 1] = '\0';

    pdata->write_flag = 0;
    pdata->read_flag = 1;
}
}

}

//./a.out pathname
int main(int argc, const char *argv[])
{
  int shmid;
  void *addr;

  if(argc < 2){
    fprintf(stderr,"Usage : %s <pathname> \n",argv[0]);
    exit(EXIT_FAILURE);
  }

  shmid = shm_init(argv[1],'k');
  addr = (void *)shmat(shmid,NULL,0);
  if(addr == (void *)-1){
    fprintf(stderr,"Fail to shmat : %s\n",strerror(errno));
    exit(EXIT_FAILURE);
  }

  write_shm(addr);

  return 0;
}

運行並執行shm_w.c

運行並執行shm_r.c

思路仍是比較簡單

上面的代碼能夠改進嗎?

這段代碼涉及到信號量的知識,能夠查看:linux進程間通訊--信號量

先引進:

sem.c

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

void init_sem_value(int sem_id,int sem_num,int value)
{
  union semun sem_val;

  sem_val.val = value;

  if(semctl(sem_id,sem_num,SETVAL,sem_val) < 0)
  {
    perror("Fail to semctl");
    exit(EXIT_FAILURE);
  }

  return ;
}

int init_sems(const char *filename,int nsem)
{
  key_t key;
  int sem_id;

  key = ftok(filename,'b');
  if(key < 0){
    perror("Fail to ftok");
    exit(EXIT_FAILURE);
  }

  sem_id = semget(key,nsem,IPC_CREAT|IPC_EXCL| 0666);
  if(sem_id < 0)
  {
  //已經存在
  if(errno == EEXIST)
  {
    //從新得到ID
    sem_id = semget(key,nsem,IPC_CREAT | 0666);

  }else{
    //其餘錯誤
    perror("Fail to semget");
    exit(EXIT_FAILURE);
  }  

//不存在,建立好後,初始化
 }else{

  init_sem_value(sem_id,0,0);//0號信號燈初始化爲0
  init_sem_value(sem_id,1,1);//1號信號燈初始化爲1
 }

return sem_id;
}

void P(int sem_id,int sem_num)
{
  struct sembuf sem;

  sem.sem_num = sem_num;
  sem.sem_op = -1;
  sem.sem_flg = 0;

  if(semop(sem_id,&sem,1) < 0)
  {
    perror("Fail to semop for P\n");
    exit(EXIT_FAILURE);
  }

  return ;
}

void V(int sem_id,int sem_num)
{
  struct sembuf sem;

  sem.sem_num = sem_num;
  sem.sem_op = 1;
  sem.sem_flg = 0;

  if(semop(sem_id,&sem,1) < 0)
  {
    perror("Fail to semop for V\n");
    exit(EXIT_FAILURE);
  }

  return ;
}

 

shm_r.c修改以下:

#define SHM_SIZE 4096

int shm_init(const char *pathname,int id)
{
  int shmid;
  key_t key;

  key = ftok(pathname,id);
  if(key < 0){
    fprintf(stderr,"Fail to ftok : %s!\n",strerror(errno));
    exit(EXIT_FAILURE);
  }

  shmid = shmget(key,SHM_SIZE,IPC_CREAT | 0666);
  if(shmid < 0){
    fprintf(stderr,"Fail to shmget : %s\n",strerror(errno));
    exit(EXIT_FAILURE);
  }

  return shmid;
}

void read_shm(void *addr,int semid)
{
  char *pdata;
  pdata = (char *)addr;


while(1){
  //P操做讀資源
  P(semid,0);//申請讀資源

  printf("Read : %s!\n",pdata);
  //V操做寫資源
  V(semid,1);//釋放寫資源
 }

}

//./a.out pathname
int main(int argc, const char *argv[])
{
  int shmid;
  int semid;
  void *addr;

  if(argc < 2){
    fprintf(stderr,"Usage : %s <pathname> \n",argv[0]);
    exit(EXIT_FAILURE);
  }

  shmid = shm_init(argv[1],'k');
  addr = (void *)shmat(shmid,NULL,0);
  if(addr == (void *)-1){
    fprintf(stderr,"Fail to shmat : %s\n",strerror(errno));
    exit(EXIT_FAILURE);
  }

  //信號燈集
  semid = init_sems(argv[1],2);

  read_shm(addr,semid);

  return 0;
}

 

shm_w.c修改以下:

#define SHM_SIZE 4096

int shm_init(const char *pathname,int id)
{
  int shmid;
  key_t key;

  key = ftok(pathname,id);
  if(key < 0){
    fprintf(stderr,"Fail to ftok : %s!\n",strerror(errno));
    exit(EXIT_FAILURE);
  }

  shmid = shmget(key,SHM_SIZE,IPC_CREAT | 0666);
  if(shmid < 0){
    fprintf(stderr,"Fail to shmget : %s\n",strerror(errno));
    exit(EXIT_FAILURE);
  }

  return shmid;
}

void write_shm(void *addr,int semid)
{
  char *data = (char *)addr;

  while(1){
    P(semid,1);
    fgets(data,SHM_SIZE,stdin);
    data[strlen(data) - 1] = '\0';
    V(semid,0);
  }
}

//./a.out pathname
int main(int argc, const char *argv[])
{
  int shmid;
  int semid;
  void *addr;

  if(argc < 2){
    fprintf(stderr,"Usage : %s <pathname> \n",argv[0]);
    exit(EXIT_FAILURE);
  }

  shmid = shm_init(argv[1],'k');
  addr = (void *)shmat(shmid,NULL,0);
  if(addr == (void *)-1){
    fprintf(stderr,"Fail to shmat : %s\n",strerror(errno));
    exit(EXIT_FAILURE);
}

  semid = init_sems(argv[1],2);

  write_shm(addr,semid);

  return 0;
}

 運行結果如上

相關文章
相關標籤/搜索