问题定义
利用信号量及PV操作来实现进程间的同步与互斥
知识概要
- 信号量机制:利用PV操作对信号量进行处理
- 信号量(semaphore)的数据结构为一个值和一个指针,指针指向该信号量的下一个进程。
- 信号量的值与相关资源的使用情况有关:
- 当它的值大于0时,表示当前可用资源的数量。
- 当它的值小于0时,其绝对值表示等待使用该资源的进程的个数。
- 信号量的值能且仅能 通过PV操作进行修改。
- P操作:执行一次P操作意味着请求分配一个单位的资源,因此S的值减1,当S<0时,表示已经没有可用资源,请求者必须等待别的进程释放该类资源,它才能继续运行。
- V操作:执行一次V操作意味着释放一个资源,因此S的值加1,当S<0,表示有某些进程正在等待资源,因此要唤醒一个等待状态的进程,使之继续运行。
- 原语:不可中断的进程
PV原语操作的代码实现(以消费者-生产者问题为基本)。
- ipc.h头文件(函数的生命和结构体的定义)
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/msg.h>
#define KEY_MUTEX 100
#define KEY_FULL 102
#define KEY_EMPTY 101
#define KEY_SHM 200
#define BUFFER_SIZE 10
/*
Semaphore
它负责协调各个线程,以保证它们能够正确、合理的使用公共资源
*/
typedef int semaphore;
/*
1.union中可以定义多个成员,union的大小最大的成员的大小决定
2.union成员共享同一块大小的内存,一次只能使用其中的一个成员
3.对某一个成员赋值,会覆盖其他成员的值(因为它们共享同一块内存)
但前提是成员所占的字节不同时只会覆盖相应字节上的值
比如对char成员赋值就不会把整个int成员覆盖掉
*/
union semun
{
int val;
struct semid_ds *buf;
/*
struct semid_ds
内核为每个信号量集维护一个信号量结构体
{
struct ipc_sem *sem_perm;//权限
ushort sem_nsems; //在信号集中的序号
time_t sem_otime; //上次被semop的时间
time_t sem_ctime; //上次被改变的时间
}
struct ipc_perm
{
key_t key;
}
struct sem
{
ushort semval;
short sempid;
ushort semncnt;
ushort semzcnt;
}
*/
unsigned short int *array;
};
//共享内存的结构体
struct shared_use_st
{
int buffer[BUFFER_SIZE];//存放标志位的数组
int low;//当前消费者可以取东西的位置
int high;//当前生产商可以生产商品的位置
int cur;//记录当前生产的是第几个商品
};
extern int sem_p ( semaphore sem_id );//p函数
extern int sem_v ( semaphore sem_id );//v函数
-ipc.c (函数的具体定义)
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/msg.h>
#include "ipc.h"
static int sem_set ( semaphore sem_id , int val )//修改
{
union semun sem_union;
sem_union.val = val;
/*
int semctl ( int semid , int semnum , int cmd,... );
semid: 信号集的标识符,即是信号表的索引
semnum: 信号集的索引,用来存取信号集内的某个信号
cmd:需要执行的命令
SETVAL根据semnum返回信号的值
*/
if ( semctl(sem_id , 0 , SETVAL , sem_union ) == -1 )
return 0;
return 1;
}
static void sem_del ( semaphore sem_id )
{
union semun sem_union;
//IPC_RMID 立即删除信号集,唤醒所有被阻塞的线程
if ( semctl ( sem_id , 0 , IPC_RMID , sem_union ) == -1 )
fprintf ( stderr , "Failed to delete semaphore\n" );
}
//p函数,
int sem_p ( semaphore sem_id )
{
struct sembuf sem_b;
/*
struct sembuf
{
short sem_num;//信号量的数目
short sem_op;//信号量的变化量值
short sem_flg;
//设置为SEM_UNDO,这会使得操作系统跟踪当前进程对信号量
//所做的改变,而且如果进程终止而没有释放这个信号量,
//如果新海鸥梁为这个进程所占有,这个标记可以使得操作
//系统自动释放这个信号量。
}
*/
sem_b.sem_num = 0;
sem_b.sem_op = -1; //p函数所以是-1
sem_b.sem_flg = SEM_UNDO;
/*
int semop ( int sem_id , struct sembuf* sem_ops , size_t num_sem_ops )
对信号量集标识符为semid中的一个或多个信号量进行P操作或V操作
sem_id是由semget所返回的信号量标识符。
sem_ops,是一个指向结构体数组的指针,其中每一个结构至少包含下列成员
num_sem_ops就是上面的数组的大小
*/
if ( semop(sem_id, &sem_b , 1 ) == -1 )
{
fprintf ( stderr , "semaphore_p failed\n");
return 0;
}
return 1;
}
//V函数
int sem_v ( semaphore sem_id )
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1;//V函数所以是1
sem_b.sem_flg = SEM_UNDO;
if ( semop( sem_id , &sem_b , 1 ) == -1 )
{
fprintf ( stderr , "semaphore_v , failed\n");
return 0;
}
return 1;
}
static int shm_create ( int key )
{
/*
共享内存函数由shmget,shmat,shmat,shmdt,shmct四个函数组成
int shmget ( key_t key , size_t size , int shmflg )
key:
0 : 会建立新共享内存对象
1 : 大于0的三十二位整数,通常要求此值来源与flok返回的IPC键值
size:
0 : 只获取共享内存时指定为0
1 : 新建共享内存的大小
shmflg:
0: 取共享内存标识符
IPC_CREAT: 当shmflg&IPC_CREAT为真时,如果内核中不存在键值与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存,返回此共享内存的标识符
IPC_CREAT|IPC_EXCL:如果内核中不存在键值与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存就报错
成功返回共享内存的标识符
错误则返回-1
*/
shmget ( (key_t)key , sizeof ( struct shared_use_st) ,0666|IPC_CREAT );//建立共享的内存对象
}
5个经典的进程同步问题
本篇文章已经更新完毕,希望大家把我误解的地方指正~~~,如果有更好的理解可以在评论区大家交流一下,敬谢