信号量互斥编程

一 公示栏问题

1 公示栏问题程序化

  两个同学交叉的使用公示栏发布公告,导致两者的信息发布的都不对

  astudent: class match cancel 

  bstudent: english exam

astudent:

//a student 写入数据后进行休息,然后再进行写入数据
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

void main()
{
  int fd;
  fd=open("./board.txt",O_RDWR|O_APPEND);
  if(fd<0)
  {
    printf("open file error\n");	
  }
  
  write(fd,"class math",10);
  printf("class math\n");
  sleep(10);
  
  write(fd,"is cancel",9);
  close(fd);  //关闭文件
		
}


bstudent:

#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

void main()
{
  int fd=open("./board.txt",O_RDWR|O_APPEND);	

  if(fd<0)
  {
    printf("file open error\n");
    exit(0);
  }
   write(fd,"english exam",12);
   close(fd);	
	
}

  先执行astudent程序,在执行astudent时执行bstudent,最后打开board.txt查看内容



二 信号量

1 信号量(又名:信号灯)与其他进程间通信方式不大相同,主要用途是 保护临界资源(进程互斥)。进程可以根据它判定是否能够访问某些共享资源。除了用于访问控制外,还可用于进程同步

2 信号量的分类

二值信号灯:信号灯的值只能取01
计数信号:信号灯的值可以取任意非负值。 

3 利用信号量实现互斥访问
为使多个进程能互斥地访问某临界资源,只须为该资源设置一互斥信号量 mutex,并设其初始值为 1,然后将各进程访问该资源的临界区 CS 置于 wait(mutex)和 signal(mutex)操作之间即可。这样,每个欲访问该临界资源的进程在进入临界区之前,都要先对 mutex 执行wait 操作,若该资源此刻未被访问,本次 wait 操作必然成功,进程便可进入自己的临界区,这时若再有其他进程也欲进入自己的临界区,此时由于对 mutex 执行 wait 操作定会失败,因而该进程阻塞,从而保证了该临界资源能被互斥地访问。当访问临界资源的进程退出临界区后,又应对 mutex 执行 signal 操作,以便释放该临界资源。---《计算机操作系统》(第三版)汤小丹
4 信号量和文件对比学习
在linux系统中文件都有一个文件名(包含路径的文件名),通过open函数可以打开文件,同时open函数的返回值为一个整数,即文件描述符fd,利用文件描述符可以进行更多的操作,如write。对信号量也有类似的操作,可以打开信号量,得到一个标示符,利用标示符可以对信号量进行更多的操作。
5 键值
  信号量通过键值这样一个数字可以找到,也就是说系统中的每一个IPC对象(如信号量,共享内存)都有一个键值与之对应,键值是打开之前就有的,标示符是打开IPC对象之后才有的。键值用来标明系统中的信号量,通过键值可以找到信号量。
6 键值的指定
  键值是在信号量创建时指定的,指定键值的方式有两种。
  (1) 任意指定一个数
   缺点:这个数已经被别的 IPC 对象(消息队列,共享内存)所使用了,在与新创建的信号量关联时就会失败。
  (2) 构造一个尽量不会被别的 IPC 对象用到的数
   方法:使用 key_t ftok( char * fname, int id )
7 ftok:

函数功能:通过将文件路径名和子序号,获得System V IPC 键值(即创建消息队列、共享内存所用的键值)

头文件:

  #include<sys/types.h>

  #include<sys/ipc.h>

函数原型:key_t ftok(const char *pathname,int proj_id);

参数说明:pathname:指定的带路径的文件名。

                 proj_id:子序号id,或称工程id。

如指定文件的索引节点号为65538,,换算成16进制为0x010002,而指定的ID值为38,换算成16进制为0x26,则最后的key_t返回值为0x26010002。

返回值:成功,则返回System V IPC键值。

             失败,则返回-1。

8 函数学习
函数名:semget
函数原型:int semget(key_t  key, int nsems, int semflg);
函数功能:获取信号量集合的标识符,当key所指定的信号量不存在时并semflag为IPC_CREAT时创建信号量集合。
头文件:<sys/types.h> 
              <sys/ipc.h> 
              <sys/sem.h>
返回值:成功:返回信号量集合的标识符
              失败:-1
参数说明:key:要打开的信号量对应的键值。
                 semflag:标志,可以取IPC_CREAT, IPC_CREAT标明,如果key标识的信号量不存在时,创建信号量。
                 nsems:创建的信号量集合里面包含的信号量数目。

操作信号量
函数名:semop
函数原型:int semop(int semid, struct sembuf *sops, unsigned nsops);
函数功能:操作信号量集合里的信号量
头文件:<sys/types.h> 

             <sys/ipc.h> 

              <sys/sem.h>
返回值:成功:0  失败:-1
参数说明:semid:要操作的信号量集合的标识符  
                nsops:要操作多少个信号量
                sops:对信号执行什么样的操作

  the elements of this structure are of type struct sembuf,containing the following number:

  unsigned short sem_num;/*semaphore number*/  //信号灯编号

  short                 sem_op;/*semaphore operation*/  //为正时,代表释放的信号灯值,为负时,代表获取信号灯值

  short                sem_flg;/*operation flags*/  //操作的标识

semctl

函数名:semctl()

函数功能:信号灯的控制

头文件:#include<sys/types.h>

          #include<sys/ipc.h>

           #include<sys/sem.h>

函数原型:int semctl(int semid,int semnum,int cmd,union semun arg);

参数说明:

semid:信号灯集的ID.

semnum:操作的信号灯编号。

cmd:是控制命令,常用的命令有

IPC_RMID:将信号灯集从内存中删除。

GETPID:获得sempid

GETVAL:获得semval

SETVAL:设置semval

arg:是一个共同体类型的副本。其中各个量的使用情况和参数cmd的设置有关。

返回值:失败时返回-1,成功返回与cmd相关的正数,例如:

          GETPID:返回sempid

          SETVAL:返回semval



三 公告栏问题使用信号量解决

astuent

#include <unistd.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <fcntl.h>  
#include <sys/ipc.h>  
#include <sys/sem.h>  

void main()
{
  int fd=0;
  key_t key;
  int semid;
  struct sembuf sops;  
  //创建键值  
  key=ftok("/home",1);
  //创建并且打开信号量集合 
  semid=semget(key,1,IPC_CREAT); 
  
  /* 设置信号量初始值为1 */   //信号量的初始值可以为任意值
    semctl(semid,0,SETVAL,1);  
  //
  //打开文件
  fd=open("board.txt",O_RDWR|O_APPEND|O_CREAT,666);
  if(fd<0)
  {
    printf("error\n");
    exit(0);
  }
  //获取信号量
  
  //semop第一个参数是信号量集合,即semid
  //只操作一个信号量,所以第三个参数为1
  sops.sem_num=0;
  sops.sem_op=-1;  //获取信号量  
  semop(semid,&sops,1); 
  //向公告栏中写入数据
  write(fd,"class math",10);   
  //暂停
  sleep(10);
  write(fd,"is cancel",10); 
  //释放信号量
   sops.sem_num = 0;  
   sops.sem_op = 1;  
   semop(semid,&sops,1);  
   
   close(fd);
  
 
}

bstudent

#include <unistd.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <fcntl.h>  
#include <sys/ipc.h>  
#include <sys/sem.h>  

void main()
{
    int fd = 0;  
    key_t key;  
    int semid;  
    struct sembuf sops;  
    int ret;  
      
    /* 创建键值 */  //确保A,B打开的是同一个信号量,也就是说B必须指定相同的文件名和项目ID
    key = ftok("/home",1);  
      
    /* 打开信号量集合 */   //A已经创建了一个信号量,此时不会创建新的信号量,会打开已有的信号量
    semid = semget(key,1,IPC_CREAT);  
    //打印信号量此时的值
    ret = semctl(semid,0,GETVAL);  
    printf("%d\n",ret);  
      
    /* 打开公告板文件*/  
    fd = open("./board.txt",O_RDWR|O_APPEND);  
      
    /* 获取信号量 */  
    sops.sem_num = 0;  
    sops.sem_op = -1;   //如果获取失败,进程会阻塞
    semop(semid,&sops,1);  
      
    /* 写入英语课“考试” */  
    write(fd," english exam",13);  
      
    /* 释放信号量 */  
    sops.sem_num = 0;  
    sops.sem_op = 1;  
    semop(semid,&sops,1);  
      
    close(fd);  
}
运行结果



参考:

1 https://www.cnblogs.com/gary-guo/p/5558861.html 信号量互斥编程

2 国嵌《信号量互斥编程》

3 http://blog.csdn.net/zhuwenfeng215/article/details/45267585 信号量互斥编程

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值