linux进程间的通信机制--信号量互斥

时间:2022-12-12 15:17:07

1.核心理论

  • 进程互斥:多个并发的进程互相排斥的按照一定的先后顺序访问临街资源的过程叫做进程互斥。

  • 信号量的实质:数字

  • 信号量的操作:获取信号量(减法),释放信号量(加法)。

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

  • 信号量分类:

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

2.函数学习:

  • 创建信号量
    函数名:semget
    函数原型:int semget(key_t key , int nsems , int semflg)
    功能:获取信号量集合的标识符。此外当key所指定的信号量不存在的时候,并且semflg里面包含了IPC_CREAT,这时候就会创建一个信号量集合。
    头文件:sys/types.h sys.ipc.h sys/sem.h
    返回值:成功:返回信号量的标识符。失败-1
    参数说明:key:键值。 emflg:标志,可取IPC_CREATnsems:创建的这个信号集合里面所包含的信号量的数目。

注意:
信号量用法:信号的操作方法和文件的操作方法相似,首先打开信号量获取信号量的描述符;然后通过描述符来操作信号量。

  • 操作信号量:
    函数名:semop
    函数原型:int semop(int semid , struct sembuf *sops, unsigned nops)
    功能:操作信号量集合里面的信号量
    头文件:sys/types.h sys.ipc.h sys/sem.h
    返回值:成功0,失败 -1
    参数:
    semid:要操作信号量集合的标识符
    nsops:要操作多少个信号量
    sops:对信号执行什么样的操作

  • 信号量的控制
    函数名:semctl
    函数原型:int semctl(int semid , int semnum , int cmd,...)
    头文件:sys/types.h sys.ipc.h sys/sem.h
    功能:对信号量进行操作
    返回值:成功返回操作结果,失败-1
    参数:semid:要操作信号量集合的标识符,semnum:操作的信号量,cmd:操作方法(常用的操作命令有GETVAL:获取信号量的值,SETVAL:设置信号量的值)

注意:键值

  • 关于键值的理解
    键值含义:文件通过文件名来获取文件描述符,而信号量则通过键值来获取信号量的标识符。

  • 指定键值的方法:
    (1)任意指定一个数。这样做的缺点是这个数已经被别的IPC对象(消息队列,共享内存)所使用了,在于新创建的信号量相关连时就会失败
    (2)构造一个尽量不被别的IPC对象所使用到的数字。
    方法:使用ftok函数

  • 构造键值的函数
    函数名:ftok
    函数原型:key_t ftok(char *fname ,int id)
    头文件:sys/types.h sys.ipc.h
    功能:构造一个新的键值
    返回值:成功返回一个键值,失败-1
    参数:fname:文件名 id:项目id

  • ftok函数工作原理:将文件名在内核中与之对应的数字与项目id组合起来形成一个键值(这两个组合的键值是固定的且唯一的)。

3.信号量互斥编程实例:

  • 解决公告板问题
    student1去公告板写入一段字符,期间休息10s,student2在student1休息期间也写入一串字符,结果导致公告板的信息就是studen1想要表达的,也不是student2想要表达。
    为了防止这种糟糕情况的出现,于是引入了信号量,student1.在未完成写入之前不会释放信号量,student2得不到信号量,就无法使用公告板,从而避免上述情形的出现。

  • student1.c

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

void main()
{
    int fd;
    key_t key;
    int semid;
    int ret;
    struct sembuf sops;

    key = ftok("/home",1);//创建键值

    semid = semget(key,1,IPC_CREAT);//创建并打开信号量集合
    semctl(semid,0,SETVAL,1);//设置信号量的值为1

    fd = open("./board.txt",O_RDWR|O_APPEND);//打开文件

    sops.sem_num = 0;//要设置的信号量
    sops.sem_op =  -1;//执行-1操作
    semop(semid,&sops,1);//获取信号量

    ret = semctl(semid,0,GETVAL);//获取信号量的值
    printf("The semval is %d\n",ret);

    write(fd,"class math",10);//写入字符
    sleep(10);//休息
    write(fd," is cancel",9);//再次写入

    sops.sem_num = 0;
    sops.sem_op = 1;//执行加一操作
    semop(semid,&sops,1);//释放信号量

    close(fd);
}
  • student2.c
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/sem.h>

void main()
{
    int fd;
    key_t key;
    int semid;
    int ret;
    struct sembuf sops;

    key = ftok("/home",1);//指定键值
    semid = semget(key,1,IPC_CREAT);//创建信号量
    ret = semctl(semid,0,GETVAL);//获取信号量的值
    printf("The semval is %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," class English is test",21);//写入字符

    sops.sem_num = 0;
    sops.sem_op =+ 1;
    semop(semid,&sops,1);//释放信号量
    ret = semctl(semid,0,GETVAL);//获取信号量的值
    printf("The semval is %d\n",ret);

    close(fd);

}

程序执行步骤:首先执行student1进程,然后执行student2进程

执行结果:
在文件board.txt中写入class math is cancel class English is test