概念:是不同进程间或一个给定进程内部不同线程间同步的机制。类似我们的
PV操作概念:
生产者和消费者场景
P(S) 含义如下:
if (信号量的值大于0) {
申请资源的任务继续运行;
信号量的值减一;
} else {
申请资源的任务阻塞;
}
V(S) 含义如下:
信号量的值加一;
if (有任务在等待资源) {
唤醒等待的任务,让其继续运行
}
信号灯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动态库。
三种信号灯:
Posix 有名信号灯
Posix 无名信号灯 (linux只支持线程同步)
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:文件权限。常用0666
value:信号量值。二元信号灯值为1,普通表示资源数目
信号灯文件位置:/dev/shm
有名信号灯关闭
int sem_close(sem_t *sem);
有名信号灯的删除
int sem_unlink(const char* name);
有名信号灯:
sem_w.c
#include<stdio.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<semaphore.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/shm.h>
int main()
{
key_t key;
key = ftok(".",100);
if(key < 0)
{
perror("ftok");
return 0;
}
int shmid;
shmid = shmget(key,500,0666|IPC_CREAT);
if(shmid < 0)
{
perror("shmget");
return 0;
}
char* shmaddr;
shmaddr = shmat(shmid,NULL,0);
sem_t* sem_r,*sem_w;
sem_r = sem_open("mysem_r",O_CREAT|O_RDWR,0666,0);
sem_w = sem_open("mysem_w",O_CREAT|O_RDWR,0666,1);
while(1)
{
sem_wait(sem_w);
printf(">");
fgets(shmaddr,500,stdin);
sem_post(sem_r);
}
return 0;
}
sem_r.c
#include<stdio.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<semaphore.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/shm.h>
int main()
{
key_t key;
key = ftok(".",100);
if(key < 0)
{
perror("ftok");
return 0;
}
int shmid;
shmid = shmget(key,500,0666|IPC_CREAT);
if(shmid < 0)
{
perror("shmget");
return 0;
}
char* shmaddr;
shmaddr = shmat(shmid,NULL,0);
sem_t* sem_r,*sem_w;
sem_r = sem_open("mysem_r",O_CREAT|O_RDWR,0666,0);
sem_w = sem_open("mysem_w",O_CREAT|O_RDWR,0666,1);
while(1)
{
sem_wait(sem_r);
printf("%s\n",shmaddr);
sem_post(sem_w);
}
return 0;
}
运行结果:
test_r.c
#include<stdio.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<semaphore.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<signal.h>
void handle(int sig)
{
sem_unlink("mysem_w");
exit(0);
}
int main()
{
struct sigaction act;
act.sa_handler = handle;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
sigaction(SIGINT,&act,NULL);
key_t key;
key = ftok(".",100);
if(key < 0)
{
perror("ftok");
return 0;
}
int shmid;
shmid = shmget(key,500,0666|IPC_CREAT);
if(shmid < 0)
{
perror("shmget");
return 0;
}
char* shmaddr;
shmaddr = shmat(shmid,NULL,0);
sem_t* sem_r,*sem_w;
sem_r = sem_open("mysem_r",O_CREAT|O_RDWR,0666,0);
sem_w = sem_open("mysem_w",O_CREAT|O_RDWR,0666,1);
while(1)
{
sem_wait(sem_r);
printf("%s\n",shmaddr);
sem_post(sem_w);
}
return 0;
}
test_w.c
#include<stdio.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<semaphore.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<signal.h>
void handle(int sig)
{
sem_unlink("mysem_r");
exit(0);
}
int main()
{
struct sigaction act;
act.sa_handler = handle;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
sigaction(SIGINT,&act,NULL);
key_t key;
key = ftok(".",100);
if(key < 0)
{
perror("ftok");
return 0;
}
int shmid;
shmid = shmget(key,500,0666|IPC_CREAT);
if(shmid < 0)
{
perror("shmget");
return 0;
}
char* shmaddr;
shmaddr = shmat(shmid,NULL,0);
sem_t* sem_r,*sem_w;
sem_r = sem_open("mysem_r",O_CREAT|O_RDWR,0666,0);
sem_w = sem_open("mysem_w",O_CREAT|O_RDWR,0666,1);
while(1)
{
sem_wait(sem_w);
printf(">");
fgets(shmaddr,500,stdin);
sem_post(sem_r);
}
return 0;
}
运行结果:
无名信号灯初始化
int sem_init(sem_t *sem, int shared, unsigned int value);
参数:
sem:需要初始化的信号灯变量
shared: shared指定为0,表示信号量只能由初始化这个信号量的进程使用,不能在进程间使用,linux 不支持进程间同步。
Value:信号量的值
无名信号灯销毁
int sem_destroy(sem_t* sem);
无名信号灯:
该程序包含一个主进程和一个读取进程。主进程负责从标准输入中读取用户输入,并将其写入共享内存中,读取进程则从共享内存中读取数据并输出到标准输出。
1.信号处理函数,用于在程序中断时销毁信号量destroysem
2.读取进程函数readmem
sem_wait(&sem_r); // 等待写入信号量
printf("%s\n",shmaddr); // 读取共享内存内容并打印
sem_post(&sem_w); // 发送写入信号量
3.设置信号处理函数sigaction
4.生成共享内存ftok
5.创建共享内存shmget
6.将共享内存连接到当前进程的地址空间shmat
7.初始化信号量sem_init
8.创建读取进程pthread_create
9.读取数据并输出到标准输出
sem_wait(&sem_w); // 等待读取信号量
printf(">");
fgets(shmaddr,500,stdin); // 从标准输入读取数据到共享内存
sem_post(&sem_r); // 发送读取信号量
具体代码:
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <signal.h>
#include <pthread.h>
sem_t sem_r,sem_w;
char *shmaddr;
void destroysem(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(){
key_t key;
int shmid;
struct sigaction act;
act.sa_handler = destroysem;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
sigaction(SIGINT,&act,NULL);
key = ftok(".",100);
if(key<0){
perror("ftok");
return 0;
}
shmid = shmget(key,500,0666|IPC_CREAT);
if(shmid<0){
perror("shmget");
return 0;
}
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);
printf(">");
fgets(shmaddr,500,stdin);
sem_post(&sem_r);
}
}
这个程序的主要功能是从标准输入中读取用户输入,并将其写入共享内存,然后另一个进程从共享内存中读取数据并打印到标准输出。
运行结果: