信号灯概念和有名信号灯
目录
信号灯概念和有名信号灯
有名信号灯
无名信号灯
信号灯P操作
信号灯V操作
system V信号灯的
信号灯/信号量(semaphore)
信号量代表某一类资源,其值表示系统中该资源的数量;
信号量是一个受保护的变量,只能通过三种操作来访问
初始化、P操作(申请资源)、V操作(释放资源)(P消费者、V生产者)
概念:
是不同进程间或一个给定进程内部不同线程间同步的机制。类似我们的PV操作概念:
生产者和消费者场景
P(S) 含义如下:
if (信号量的值大于0) {
申请资源的任务继续运行;
信号量的值减一;
} else {
申请资源的任务阻塞;
}
V(S) 含义如下:
信号量的值加一;
if (有任务在等待资源) {
唤醒等待的任务,让其继续运行
}
三种信号灯:
Posix 有名信号灯;
Posix 无名信号灯;
System V 信号灯;
Posix 有名信号灯和无名信号灯使用:
有名信号灯
有名信号灯打开:
sem_t *sem_open(const char *name, int oflag);
sem_t*sem_open(const char *name, int oflag, mode_t mode, unsigned int value);
参数:
name:name是给信号灯起的名字;
oflag:打开方式,常用O_CREAT
mode :文件权限,常用06666
value:信号量值。二元信号量值为1,普通表示资源数目;
信号灯文件为止:/dev/shm/
有名信号灯关闭:
int sem_close(sem_t *sem);
有名信号灯的删除:
int sem_unlink(const char* name);
代码实现:
mysem_w:
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <signal.h>
void delsemfile(int sig) {
sem_unlink("mysem_w");
exit(0);
}
int main() {
//捕捉信号,按下Ctrl+c时删除myser_w
struct sigaction act;
act.sa_handler = delsemfile;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
sigaction(SIGINT, &act, NULL);
//创建共享内存
//1 申请共享内存key值;
key_t key;
key = ftok(".", 100);
if (key < 0) {
perror("ftok");
return 0;
}
//2 根据key值创建共享内存
int shmid;
shmid = shmget(key, 500, 0666|IPC_CREAT);
if (shmid < 0) {
perror("shmget");
return 0;
}
//3 获取共享内存地址
char *shmaddr;
shmaddr = shmat(shmid, NULL, 0);
//定义读写信号量
sem_t *sem_r, *sem_w;
//打开信号量;(初始化 读信号量为0,写信号量为1)
sem_r = sem_open("mysem_r", O_CREAT|O_RDWR, 0666, 0);
sem_w = sem_open("mysem_w", O_CREAT|O_RDWR, 0666, 1);
//向共享内存中写数据
//1 判断是否能写
while(1) {
sem_wait(sem_w); //sem_w--
printf(">");
fgets(shmaddr, 500, stdin);
sem_post(sem_r); //sem_r++
}
}
mysem_r:
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <signal.h>
void delsemfile(int sig) {
sem_unlink("mysem_r");
exit(0);
}
int main() {
//捕捉信号,按下Ctrl+c时删除myser_r
struct sigaction act;
act.sa_handler = delsemfile;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
sigaction(SIGINT, &act, NULL);
//创建共享内存
//1 申请共享内存key值;
key_t key;
key = ftok(".", 100);
if (key < 0) {
perror("ftok");
return 0;
}
//2 根据key值创建共享内存
int shmid;
shmid = shmget(key, 500, 0666|IPC_CREAT);
if (shmid < 0) {
perror("shmget");
return 0;
}
//3 获取共享内存地址
char *shmaddr;
shmaddr = shmat(shmid, NULL, 0);
//定义读写信号量
sem_t *sem_r, *sem_w;
//打开信号量;(初始化 读信号量为0,写信号量为1)
sem_r = sem_open("mysem_r", O_CREAT|O_RDWR, 0666, 0);
sem_w = sem_open("mysem_w", O_CREAT|O_RDWR, 0666, 1);
//向共享内存中写数据
//1 判断是否能写
while(1) {
sem_wait(sem_r); //sem_r--
printf("%s\n", shmaddr);
sem_post(sem_w); //sem_w++
}
}
无名信号灯
无名信号灯在Linux下只支持线程间同步不支持进程间同步
无名信号灯的初始化
int sem_close(sem_t *sems, int shared, unsigned int value);
参数:
sem:需要初始化的信号灯变量;
shared:shared指定为0,表示信号量只能由这个初始化这个信号量的进程使用,不能在进程间使用,Linux不支持进程间同步;
value:信号量的值
无名信号灯的销毁:
int sem_destroy(sem_t *sem);
信号灯P操作
int sem_wait(sem_t *sem);
获取资源,如果信号量为0,表示这时没有相应资源空闲,那么调用线程就将挂起,直到有空闲资源可以获取
信号灯V操作
int sem_post(sem_t *sem);
释放资源,如果没有线程阻塞在该sem上,表示没有线程等待该资源,这时该函数就对信号量的值进行增1操作,表示同类资源多增加了一个。如果至少有一个线程阻塞在该sem上,表示有线程等待资源,信号量为0,这时该函数保持信号量为0不变,并使某个阻塞在该sem上的线程从sem_wait函数中返回
注意:编译posix信号灯需要加pthread动态库。
代码实现
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <signal.h>
#include <pthread.h>
sem_t sem_r, sem_w;
char *shmaddr;
void deseroysem(int sig) {
sem_destroy(&sem_r);
sem_destroy(&sem_w);
exit(0);
}
void *readmem(void *arg) {
while(1) {
sem_wait(&sem_r);
printf("%s\n", shmaddr);
sem_post(&sem_w);
}
}
int main() {
//捕捉信号,按下Ctrl+c时删除myser_w
struct sigaction act;
act.sa_handler = deseroysem;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
sigaction(SIGINT, &act, NULL);
//创建共享内存
//1 申请共享内存key值;
key_t key;
key = ftok(".", 100);
if (key < 0) {
perror("ftok");
return 0;
}
//2 根据key值创建共享内存
int shmid;
shmid = shmget(key, 500, 0666|IPC_CREAT);
if (shmid < 0) {
perror("shmget");
return 0;
}
//3 获取共享内存地址
shmaddr = shmat(shmid, NULL, 0);
//无名信号灯初始化;
sem_init(&sem_r, 0, 0);
sem_init(&sem_w, 0, 1);
//创建线程
pthread_t tid;
pthread_create(&tid, NULL, readmem, NULL);
//向共享内存中写数据
while(1) {
sem_wait(&sem_w); //sem_w--
printf(">");
fgets(shmaddr, 500, stdin);
sem_post(&sem_r); //sem_r++
}
}
system V信号灯的
int semget*key_t key, int nsems, int semflg);
功能:创建 / 打开信号灯;
参数:
key:ftok 产生的key值;(和信号灯关联的key值)
nsems :信号灯集 中包含的信号灯数目;
semflg :信号灯集 的访问权限,通常是IPC_CREAT | 0666
返回值:成功返回信号灯集id, 失败返回-1;
int semop(int semid, struct sembuf *opsptr, size_t nops);
功能:对信号灯集合中的信号量进行p - v操作
参数:
semid:信号灯集合 的id;
struct sembuf {
short sem_num; //要操作的信号灯的编号;
short sem_op; //1:释放资源,V操作; -1:分配资源, P操作;
short sem_flg; // 0(阻塞),IPC_NOWAT,SEM_UNDO
}; //对某一个信号灯的操作,如果同时对多个操作,则需要定义这种结构以数组
nops:要操作的信号灯的个数, 1个;
返回值:成功:0,; 失败:-1.
int semctl(int semid, int semnum, int cmd.../*union semun arg*/);
功能:信号灯集合的控制(初始化 / 删除)
参数:
semid:信号灯集id
semnum: 要操作的集合中的信号灯编号
cmd:
GETVAL:获取信号灯的值,返回值是获得值
SETVAL:设置信号灯的值,需要用到第四个参数:共用体
IPC_RMID:从系统中删除信号灯集合
返回值:成功 0 ; 失败 -1
代码实现:
#include <stdio.h>
#include <semaphore.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/sem.h>
#define SEM_READ 0
#define SEM_WRITE 1
union semun{
int val;
};
//p操作
void Poperation(int semid, int semindex) {
struct sembuf sbuf;
sbuf.sem_num = semindex;
sbuf.sem_op = -1;
sbuf.sem_flg = 0;
semop(semid, &sbuf, 1);
}
//v操作
void Voperation(int semid, int semindex ) {
struct sembuf sbuf;
sbuf.sem_num = semindex;
sbuf.sem_op = 1;
sbuf.sem_flg = 0;
semop(semid, &sbuf, 1);
}
int main() {
key_t key;
int semid;
int shmid;
char *shmaddr;
key = ftok(".", 100);
if (key < 0) {
perror("ftok");
return 0;
}
semid = semget(key, 2, IPC_CREAT|0666);
if (semid < 0) {
perror("semget");
return 0;
}
shmid = shmget(key, 500, IPC_CREAT|0666);
shmaddr = shmat(shmid, NULL, 0);
//初始化
union semun mysem;
mysem.val = 0;
semctl(semid, SEM_READ, SETVAL, mysem);
mysem.val = 1;
semctl(semid, SEM_WRITE, SETVAL, mysem);
pid_t pid;
pid = fork();
if (pid < 0) {
perror("fork");
shmctl(shmid, IPC_RMID, NULL);
semctl(semid, 0, IPC_RMID);
exit(-1);
}else if(pid == 0) {
while(1){
Poperation(semid, SEM_READ);
printf("%s\n", shmaddr);
Voperation(semid, SEM_WRITE);
}
}else{
while(1){
Poperation(semid, SEM_WRITE);
printf(">");
fgets(shmaddr, 32, stdin);
Voperation(semid, SEM_READ);
}
}
}