(十二)信号量互斥编程(转)

 原文链接https://blog.csdn.net/qq_27312943/article/details/79067893

 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查看内容

信号量(灯)

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

信号量分类

  • 二值信号灯:信号灯的值只能取0或1
  • 计数信号灯:信号灯的值可以取任意非负值

利用信号量实现互斥访问

为使多个进程能互斥地访问某临界资源,只须为该资源设置一互斥信号量 mutex,并设其初始值为 1,然后将各进程访问该资源的临界区 CS 置于 wait(mutex)和 signal(mutex)操作之间即可。

这样,每个欲访问该临界资源的进程在进入临界区之前,都要先对 mutex 执行wait 操作,若该资源此刻未被访问,本次 wait 操作必然成功,进程便可进入自己的临界区,这时若再有其他进程也欲进入自己的临界区,此时由于对 mutex 执行 wait 操作定会失败,因而该进程阻塞,从而保证了该临界资源能被互斥地访问。当访问临界资源的进程退出临界区后,又应对 mutex 执行 signal 操作,以便释放该临界资源

 信号量和文件对比

在linux系统中文件都有一个文件名(包含路径的文件名),通过open函数可以打开文件,同时open函数的返回值为一个整数,即文件描述符fd,利用文件描述符可以进行更多的操作,如write。

对信号量也有类似的操作,可以打开信号量,得到一个标示符,利用标示符可以对信号量进行更多的操作。

键值

信号量通过键值这样一个数字可以找到,

也就是说系统中的每一个IPC对象(如信号量,共享内存)都有一个键值与之对应,

键值是打开之前就有的,标示符是打开IPC对象之后才有的。

键值用来标明系统中的信号量,通过键值可以找到信号量。

键值的指定

 键值是在信号量创建时指定的,指定键值的方式有两种。

  • (1)任意指定一个数 

        缺点:这个数已经被别的IPC对象(消息队列,共享内存)所使用了,在与新创建的信号量关联时就会失败。

  • (2)构造一个尽量不会被别的IPC对象用到的数

       方法:使用key_t ftok( char * fname, int id )

 键值指定-ftok:

函数原型

key_t ftok(const char *pathname,int proj_id);

函数功能

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

所属头文件

<sys/types.h><sys/ipc.h>

返回值

成功,则返回System V IPC键值。   失败,则返回-1。

参数说明

pathname:指定的带路径的文件名。

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

 

 

函数学习

 

创建/获取信号量-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/semctl

semop

函数原型

int semop(int semid, struct sembuf *sops, unsigned nsops);

函数功能

操作信号量集合里的信号量

所属头文件

<sys/types.h><sys/ipc.h>sys/sem.h>

返回值

成功:0  失败:-1

参数说明

semid:要操作的信号量集合的标识符  
nsops:要操作多少个信号量
sops:对信号执行什么样的操作

 

semctl 

函数原型

int semctl(int semid,int semnum,int cmd,union semun arg);

函数功能

信号灯的控制

所属头文件

<sys/types.h><sys/ipc.h>sys/sem.h>

返回值

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

             GETPID:返回sempid

             SETVAL:返回semval

参数说明

semid:信号灯集的ID.

semnum:操作的信号灯编号。

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

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

    GETPID:  获得sempid

    GETVAL:  获得semval

    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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值