进程通信(IPC)——实现信号量

        今天我们来分享一下进程通行(IPC)-实现信号量。

       信号量的本质是计数器,且进程与进程之间都能够看到,所以我们用一个信号量来衡量临界资源,通过计数器限制进程对临界资源进行操作的个数。也就是说信号量是用来保护临界资源的,并且因为信号量是原子的,所以在他能在保护自己的基础下去保护临界资源。

       本文我们简单的用父子进程来展示二元信号量的作用,本文说用的二元信号量(初始为1,占用为0)可以起到互斥功能(相当于互斥锁),来保证同一时刻进入临界资源的进程只有一个,避免父子进程(多个进程)同一时间操作临界资源而导致无法保证实现不同进程之间的同步与互斥。

      用父子进程举例说明同步与互斥被破坏的情况如下:

 

 

信号量需要注意的两点:

        1》任意进程占用资源(信号量)时,因某些原因(时间片用尽)被暂时剥离,此时进程拥有信号量(锁),即会影响其他进程去申请资源(信号量),此时其他进程被迫等待。

       2》任意进程占用资源(信号量)时,被用户或其他KILL时,由于被KILL时进程拥有信号量(锁),即会影响其他进程去申请资源(信号量),此时会造成死锁。

信号量缺点:

       初始化和创建是否分开的,用户容易误操作。

    由1》,可以看出信号量的另一个缺点,拥有信号量所在的多进程程序所用时间相对于没有信号量约束的同一进程会多一些。

       信号量是随内核的,除了需要调用系统接口去建立信号量,还需要调用系统接口去销毁信号量。

最后,介绍关于查看信号量和销毁信号量的系统命令,截图如下:

 

实现信号量的父子进程代码如下:

comm.h

#ifndef _COMM_H_
#define _COMM_H_
   
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
 
union semu{
int val;
      struct semid_ds* buf;
      unsigned short* array;
      struct seminfo* _buf;
};
   
#define PATHNAME "."
#define PROJ_ID 0x6666
  
int CommSem(int flags);
int GreatSem();
int GetSem();
int InitSem(int semid, int nums, int val);
int PVcom(int semid, int which, int _op);
int P(int semid, int which);
int V(int semid, int which);
int DestroySem(int semid);
#endif

comm.c

#include"comm.h"
  
  int CommSem(int flags){
       int keys = ftok(PATHNAME, PROJ_ID);
      if(keys<0){
           perror("ftok");
           return -1;
       }
       int semid = semget(keys, 1, flags);
      if(semid<0){
          perror("semget");
          return -2;
      }
      return semid;
  }
  
  int GreatSem(){
      return CommSem(IPC_CREAT|IPC_EXCL);
  }
  
  int GetSem(){
      return CommSem(IPC_CREAT);
  }
  
  int InitSem(int semid, int nums, int val){
      union semu signal;
      signal.val = val;
 if(semctl(semid, nums, SETVAL, signal)<0){
          perror("semctl");
          return -4;
      }
      return 0;
  }
  
  int PVcom(int semid, int which, int _op){
      struct sembuf _buf;
      _buf.sem_num = which;
      _buf.sem_op = _op;
      _buf.sem_flg = 0;
  
      if(semop(semid, &_buf, 1)<0){
          perror("semop");
          return -5;
      }
  }
  
  int P(int semid, int which){
      return PVcom(semid, which, -1);
  }
  
  int V(int semid, int which){
      return PVcom(semid, which, 1);
  }
  int DestroySem(int semid){
      if(semctl(semid, 0, IPC_RMID)<0){
          perror("semctl");
          return -3;
      }
      return 0;
  }

client.c

 #include"comm.h"
 #include<unistd.h>
 #include<sys/wait.h>
   
   int main(){
       int semid = GreatSem();
       printf("new semid:%d\n", semid);
       InitSem(semid, 0, 1);
   
      pid_t id = fork();
      if(id<0){
          perror("fork");
          return -1;
      }
      else if(id == 0){//child
          int _semid = GetSem();
          int i=0;
          while(i<10){
              P(_semid, 0);
              i++;
              printf("A");
              fflush(stdout);
              sleep(1);
              printf("A");
              fflush(stdout);
              sleep(1);
              V(_semid, 0);
          }
      }
      else
      {
          int i=0;;
          while(i<10){
              P(semid, 0);
              i++;
              printf("B");
              fflush(stdout);
              sleep(1);
              printf("B");
              fflush(stdout);
              sleep(1);
              V(semid, 0);
          }
          pid_t ret = waitpid(id, NULL, 0);
          if(ret>0){
              printf("wait success!\n");
          }
      }
      DestroySem(semid);
  
      return 0;
  }

Makefile

  client:client.c comm.c
       gcc -o $@ $^
   
   .PHONY:clean
   clean:
       rm -f client

运行界面

 

 


分享如上!愿共同进步。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值