概念:是不同进程间或一个给定进程内部不同线程间同步的机制。类似我们的
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);}}
这个程序的主要功能是从标准输入中读取用户输入,并将其写入共享内存,然后另一个进程从共享内存中读取数据并打印到标准输出。
运行结果: