信号量概念
它不是象管道,共享内存这样的用于进程间数据传输的;主要用于资源的保护,实现
互斥
(防止多个进程同时访问临界区)和
同步
(多个进程按一定顺序执行)。
可以用于不同的进程间或一个给定进程内部的不同线程之间进行资源互斥和同步。
信号量可以保护一个或多个资源,也叫信号灯(通俗
semaphore)。
信号灯种类
1、posix
有名信号灯;
2、posix
基于内存的信号灯
(
无名信号灯
);
3、System V
信号灯
(IPC
对象
),
现在用的是
IPC
对象。
信号量的含义
信号量相当于一个全局的整型
变量,
这个变量只能用于
原子操作
改变值。
也称为信号灯集
(System V
信号灯是一个信号灯的集合
,
每个信号灯都有一个值
,
可用来表示当前该信号灯代表的共享资源可用数量。
信号灯分为
:
1. 二值信号灯:
标识资源是否可用;
当信号量保护一个资源时,叫二值信号量,1:表示资源可用
0
:表示资源不可用;
2.
计数信号灯:
表示资源的多少/数量。
信号量的机制由内核提供。
PV 原语操作
1、P
操作会使当前进程阻塞直到信号灯值大于
0,
同时使信号灯值减
1。
2、V
操作唤醒
P
操作而阻塞的进程
(
线程
),
信号量值加
1。
两者交替实现进程间同步
信号量集的数据结构
System V
信号灯集是一个或多个信号灯构成的一个集合
,
对系统每个信号灯集
,
内核用一个结构体表示:
struct semid_ds {
struct ipc_perm sem_perm; /* operation permission struct */
struct sem *sem_base;
/* ptr to first semaphore in set */
unsigned short sem_nsems;
/* # of semaphores in set */
time_t sem_otime; /* last semop time */
time_t sem_ctime; /* last change time */
};
当前信号灯集中的每个信号灯对应一个
sem
结构。定义如下:
struct sem {
signed short semval; /* semaphore text map address */
pid_t sempid; /* pid of last operation */
unsigned short semncnt; /* # awaiting semval > cval */
unsigned short semzcnt; /* # awaiting semval = 0 */
}
![](https://img-blog.csdnimg.cn/b020e4103e2e42e986facf37a4da6eb9.png)
信号灯集在内核中是一个数组;这里表示信号灯集里面有两个信号灯:sem[0],sem[1].
信号灯使用的相关函数
System V
的信号灯是一个或者多个信号灯的一个集合。其中的每一个都是单独的计数信号灯。
System V
信号灯由内核维护。
主要函数
semget
,
semop
,
semctl;
创建或打开已存在的信号量用的函数 semget
int semget(key_t key, int nsems, int semflg);
key:
键值 获取键值由三种方式
1)
直接指定
2)ftok
函数 公有
3)
宏 私有;
nsems:
信号量的个数
(
要保护几个资源
,
就填多少
);
semflg:
包括创建的标志
(IPC_CREAT and IPC_EXCL)
和权限;
删除信号灯命令
ipcsrm -s semid
信号量的操作函数 semop
信号量相关信息的设置函数 semctl
示例:
/*** sem.h ***/
#ifndef __SEM_H__
#define __SEM_H__
#include <sys/ipc.h>
#include <sys/sem.h>//放信号灯的结构体
#include <stdio.h>
#include <stdlib.h>
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
extern void sem_p(int sem_id,int num);
extern void sem_v(int sem_id,int num);
extern void sem_init(int sem_id,int value);
extern void sem_remove(int sem_id);
#endif
/*** sem.c ***/
/*
实现信号灯集的初始化
实现PV操作
*/
#include "sem.h"
//信号灯初始化 ,现在用的信号灯集里面只有一个信号灯编号0
void sem_init(int sem_id, int value)
{
union semun sem;
sem.val = value; //value=0,1
//修改编号0的信号灯的值,值是由sem来设置的
if (semctl(sem_id, 0, SETVAL, sem)<0) {
perror("semctl");
exit(1);
}
}
void sem_remove(int sem_id)
{
union semun sem;
if (semctl(sem_id, 0, IPC_RMID, sem)<0) {
perror("semctl");
exit(1);
}
}
void sem_p(int sem_id, int num)
{
struct sembuf sp = {num, -1, 0}; //num信号灯编号,-1分配资源p操作,0阻塞
if (semop(sem_id, &sp, 1)<0) {
perror("semop");
exit(1);
}
}
void sem_v(int sem_id, int num)
{
struct sembuf sp = {num, 1, 0}; //num信号灯编号,1释放资源v操作,0阻塞
if (semop(sem_id, &sp, 1)<0) {
perror("semop");
exit(1);
}
}
/*** shm.h ***/
#ifndef __SHM_H__
#define __SHM_H__
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define SHMSIZE 1024
#endif
/*** write.c ***/
#include "shm.h"
#include "sem.h"
int main(void)
{
int shmid; //共享对象id
int semid; //信号量对象id
key_t key;
char buf[SHMSIZE];
char *shm_buf;
//1.生成key
key = ftok("/", 0xa);
if (key < 0) {
perror("ftok");
exit(1);
}
printf("key=0x%x\n", key);
//2. 创建或打开共享内存IPC对象
shmid = shmget(key, SHMSIZE, IPC_CREAT|0666);
if (shmid < 0) {
perror("shmget");
exit(1);
}
//2. 创建或打开信号量IPC对象
if ( (semid = semget(key, 1, IPC_CREAT|0666)) <0 ){
perror("semget");
exit(1);
}
//初始化信号量
sem_init(semid, 1); //对应信号灯集第0个信号量赋值 sem.value=1
//3.内核的共享内存映射到用户空间
if ((shm_buf = shmat(shmid, NULL, 0)) == NULL) {
perror("shmat");
exit(1);
}
bzero(shm_buf, SHMSIZE);//共享内存清0
//4.操作共享内存
//从键盘输入
while (1) {
printf("父进程等待键盘输入:");
fgets(buf,SHMSIZE, stdin);
//P操作
sem_p(semid, 0);
if(shm_buf[0]=='\0') {
strncpy(shm_buf, buf, strlen(buf));//将buf缓冲区内容拷贝到共享内存中
if (strncmp(buf, "quit", 4)==0) {
//V操作
sem_v(semid, 0);
break;
}
}
//V操作
sem_v(semid, 0);
}
//4.解除映射
if (shmdt(shm_buf) < 0) {
perror("shmdt");
exit(1);
}
//5.删除共享内存对象
sleep(2);
shmctl(shmid,IPC_RMID,NULL);
sem_remove(semid);
return 0;
}
/*** read.c ***/
#include "shm.h"
#include "sem.h"
int main(void)
{
int shmid; //共享对象id
int semid; //信号量对象id
key_t key;
char *shm_buf;
//1.生成key
key = ftok("/", 0xa);
if (key < 0) {
perror("ftok");
exit(1);
}
printf("key=0x%x\n", key);
//2. 创建或打开共享内存IPC对象
shmid = shmget(key, SHMSIZE, IPC_CREAT|0666);
if (shmid < 0) {
perror("shmget");
exit(1);
}
//2. 创建或打开信号量IPC对象
if ( (semid = semget(key, 1, IPC_CREAT|0666)) <0 ){
perror("semget");
exit(1);
}
if ((shm_buf = shmat(shmid, NULL, 0)) == NULL) {
perror("shmat");
exit(1);
}
while (1) {
//P操作
sem_p(semid, 0); //当进程1没有V操作就会阻塞
if (shm_buf[0]!='\0') {
printf("子进程输出:%s\n", shm_buf);
if (strncmp(shm_buf, "quit", 4)==0) {
sem_v(semid, 0);
break;
}
bzero(shm_buf, SHMSIZE);
}
//v操作
sem_v(semid, 0);
}
if (shmdt(shm_buf) < 0) {
perror("shmdt");
exit(1);
}
return 0;
}