0.信息量
为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域。临界区域是指执行数据更新的代码需要独占式地执行。而信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个线程在访问它,也就是说信号量是用来调协进程对共享资源进行访问的。
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
int main ( int argc, char * argv[ ] ) {
fork ( ) ;
int i= 0 ;
for ( ; i< 5 ; i++ ) {
printf ( "PID:%d,enter\n" , getpid ( ) ) ;
sleep ( 1 ) ;
printf ( "PID:%d,do something\n" , getpid ( ) ) ;
printf ( "PID:%d,leave\n" , getpid ( ) ) ;
}
}
数据竞争
分类 根据共享资源的数目可分为二值信号量 和计数信号量
分类 取值 e.g. 二值信号量 0和1 指示锁 计数信号量 0和n 停车场电子牌
指示锁
停车场电子指示牌
作用 控制多进程共享资源的访问(资源有限并且不共享) 本质 任一时刻只有一个进程访问临界区(代码),数据更新的代码 基本操作:PV
信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即P(信号变量))和发送(即V(信号变量))信息操作。
原子操作也被称为PV原语(P来源于Dutchproberen"测试",V来源于Dutchverhogen"增加")
信号量
由于信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv),他们的行为是这样的: P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行 V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.
分类 取值 P(信号量) 0:挂起进程;>0:减1 V(信号量) 0:恢复进程;>0:加1 例如:就是两个进程共享信号量sv,一旦其中一个进程执行了P(sv)操作,它将得到信号量,并可以进入临界区,使sv减1。而第二个进程将被阻止进入临界区,因为当它试图执行P(sv)时,sv为0,它会被挂起以等待第一个进程离开临界区域并执行V(sv)释放信号量,这时第二个进程就可以恢复执行。
1.POSIX信息量
资料:unpv22e-ch10.1~10.3
查看:man sem_overview
1.1 接口
头文件:semaphore.h
库:pthread
分类 信号量分为命名信号量(基于文件)与匿名信号量(基于内存)两种。
1.2 函数
1.2.1命名信号量/基于文件
操作 函数 创建 sem_t *sem_open(const char *name,int oflag,mode_t mode,unsigned int value)
删除 int sem_unlink(const char *name)
打开 sem_t *sem_open(const char *name,int oflag)
关闭 int sem_close(sem_t *sem)
挂出 int sem_post(sem_t *sem)
等待 int sem_wait(sem_t *sem)
尝试等待 int sem_trywait(sem_t *sem)
获取信号量的值 int sem_getvalue(sem_t *sem,int *sval)
1.2.1.1 创建
sem_t * sem_open ( const char * name信号量IPC名字, int oflag标志, mode_t mode权限位, unsigned int value信号量初始值)
返回值 含义 非SEM_FAILED 信号量的指针 SEM_FAILED 出错
# include <unistd.h>
# include <iostream>
# include <fcntl.h>
# include <semaphore.h>
using namespace std;
enum Argment {
EXEC,
ARG_SEM_NAME,
ARG_NUM,
ARG_COUNT
} ;
int main ( int argc, char * argv[ ] ) {
if ( ARG_COUNT!= argc) {
cout << "Usage:" << argv[ EXEC] << " sem_name n" << endl;
return EXIT_FAILURE;
}
sem_t* sem = sem_open ( argv[ ARG_SEM_NAME] , O_CREAT, 0644 , atoi ( argv[ ARG_NUM] ) ) ;
if ( NULL == sem) {
perror ( "sem_open error" ) ;
return EXIT_FAILURE;
}
sem_close ( sem) ;
return EXIT_SUCCESS;
}
1.2.1.2 删除
int sem_unlink ( const char * name信号量IPC名字)
1.2.1.3 打开
sem_t * sem_open ( const char * name信号量IPC名字, int oflag标志)
返回值 含义 非SEM_FAILED 信号量的指针 SEM_FAILED 出错
1.2.1.4 关闭
int sem_close ( sem_t * sem信号量的指针)
1.2.1.5 挂出
int sem_post ( sem_t * sem信号量的指针)
1.2.1.6 等待
int sem_wait ( sem_t * sem信号量的指针)
# include <unistd.h>
# include <iostream>
# include <fcntl.h>
# include <signal.h>
# include <semaphore.h>
using namespace std;
void handler ( int sig) {
cout << getpid ( ) << ":" << sig << endl;
}
int main ( int argc, char * argv[ ] ) {
signal ( SIGINT, handler) ;
if ( 2 != argc) {
cout << "Usage:" << argv[ 0 ] << " sem_name " << endl;
return EXIT_FAILURE;
}
sem_t* sem = sem_open ( argv[ 1 ] , O_RDWR) ;
if ( NULL == sem) {
perror ( "sem_open error" ) ;
return EXIT_FAILURE;
}
sem_wait ( sem) ;
cout << getpid ( ) << " enter sem" << endl;
pause ( ) ;
sem_post ( sem) ;
cout << getpid ( ) << " exit sem" << endl;
sem_close ( sem) ;
return EXIT_SUCCESS;
}
1.2.1.7 尝试等待
int sem_trywait ( sem_t * sem信号量的指针)
1.2.1.8 获取信号量的值
int sem_getvalue ( sem_t * sem信号量的指针, int * sval信号量的值)
# include <unistd.h>
# include <iostream>
# include <fcntl.h>
# include <semaphore.h>
using namespace std;
int main ( int argc, char * argv[ ] ) {
if ( 2 != argc) {
cout << "Usage:" << argv[ 0 ] << " sem_name " << endl;
return EXIT_FAILURE;
}
sem_t* sem = sem_open ( argv[ 1 ] , O_RDONLY) ;
if ( NULL == sem) {
perror ( "sem_open error" ) ;
return EXIT_FAILURE;
}
int num = 0 ;
int res= sem_getvalue ( sem, & num) ;
if ( - 1 == res) {
perror ( "sem_getvalue error" ) ;
return EXIT_FAILURE;
}
cout << argv[ 1 ] << ":" << num << endl;
sem_close ( sem) ;
return EXIT_SUCCESS;
}
数据竞争解决方法
# include <stdio.h>
# include <stdlib.h>
# include <unisted.h>
# include <fcntl.h>
# include <semaphore.h>
int main ( int argc, char * argv[ ] ) {
sem_t* sem= sem_open ( "/sem.tmp" , O_CREAT| O_RDWR, 0644 , 1 ) ;
fork ( ) ;
int i= 0 ;
for ( ; i< 5 ; i++ ) {
sleep ( 1 ) ;
sem_wait ( sem) ;
printf ( "PID:%d,enter\n" , getpid ( ) ) ;
printf ( "PID:#d,do sonething\n" , getpid ( ) ) ;
printf ( "PID:%d,leave\n" , getpid ( ) ) ;
sem_post ( sem) ;
}
}
# include <iostream>
# include <unistd.h>
# include <semaphore.h>
# include <fcntl.h>
# include <sys/mman.h>
using namespace std;
enum ARGS {
CMD, SEM_NAME, SHM_NAME
} ;
int main ( int argc, char * argv[ ] ) {
if ( 3 != argc) {
cout<< "Usage:" << argv[ CMD] << "sem_name shm_name" << endl;
return EXIT_FAILURE;
}
int shm= shm_open ( argv[ SHM_NAME] , O_CREAT| O_RDWR, 0644 ) ;
if ( - 1 == shm) {
perror ( "shm_open error" ) ;
return EXIT_FAILURE;
}
ftruncate ( shm, sizeof ( int ) ) ;
void * p= mmap ( NULL , sizeof ( int ) , PROT_WRITE| PROT_READ, MAP_SHARED, shm, 0 ) ;
if ( MAP_FAILED== p) {
perror ( "mmap error" ) ;
return EXIT_FAILURE;
}
int * n= reinterpret_cast < int * > ( p) ;
* n= 0 ;
sem_t* sen= sem_open ( argv[ SEM_NAME] , O_CREAT| O_RDWR, 0644 , 1 ) ;
fork ( ) ;
for ( int i= 0 ; i< 5 ; ++ i) {
usleep ( 10000 ) ;
sem_wait ( sem) ;
++ * n;
cout<< getpid ( ) << ":" << * n<< endl;
sem_post ( sem) ;
}
sem_close ( sem) ;
munmap ( p, sizeof ( int ) ) ;
return EXIT_SUCCESS;
}
可以通过注释fork()
,重新编译程序后同时执行两次程序演示两个非亲缘进程的同步。
匿名信息量/基于内存
操作 函数 初始化 int sem_init (sem_t *sem,int pshared,unsigned int value
销毁 int sem_destroy(sem_t *sem)
挂出 int sem_post(sem_t *sem)
等待 int sem_wait(sem_t *sem)
尝试等待 int sem_trywait(sem_t *sem)
获取信号量的值 int sem_getvalue(sem_t* sem,int *sval)
挂出
和等待
操作与命名信号量相同
1.2.2.1 初始化
int sem_init ( sem_t * sem信号量的指针, int pshared ( 共享方式。0 :线程间共享;1 :进程间共享,需要共享内存) , unsigned int value信号量初始值)
1.2.2.2 销毁
int sem_destroy ( sem_t * sem信号量的指针)
数据竞争解决方法
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <fcntl.h>
# include <semaphore.h>
int main ( int argc, char * argv[ ] ) {
sem_t* sem = sem_open ( "/sem.tmp" , O_CREAT| O_RDWR, 0644 , 0 ) ;
fork ( ) ;
int i= 0 ;
for ( ; i< 5 ; i++ ) {
sleep ( 1 ) ;
sem_wait ( sem) ;
printf ( "PID:%d,enter\n" , getpid ( ) ) ;
printf ( "PID:%d,do something\n" , getpid ( ) ) ;
printf ( "PID:%d,leave\n" , getpid ( ) ) ;
sem_post ( sem) ;
}
}
# include <iostream>
# include <unistd.h>
# include <semaphore.h>
# include <fcntl.h>
# include <sys/mman.h>
using namespace std;
struct Data {
sem_t sem;
int data;
} ;
int main ( int argc, char * argv[ ] ) {
void * p = mmap ( NULL , sizeof ( Data) , PROT_WRITE| PROT_READ, MAP_SHARED| MAP_ANON, - 1 , 0 ) ;
if ( MAP_FAILED == p) {
perror ( "mmap error" ) ;
return EXIT_FAILURE;
}
Data* d = reinterpret_cast < Data* > ( p) ;
d-> data = 0 ;
sem_init ( & d-> sem, 1 , 1 ) ;
fork ( ) ;
for ( int i= 0 ; i< 5 ; ++ i) {
usleep ( 10000 ) ;
sem_wait ( & d-> sem) ;
++ ( d-> data) ;
cout << getpid ( ) << ":" << d-> data << endl;
sem_post ( & d-> sem) ;
}
sem_destroy ( & d-> sem) ;
munmap ( p, sizeof ( Data) ) ;
return EXIT_SUCCESS;
}
3. 计数信息量
模拟一个停车场的电子显示牌
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <fcntl.h>
# include <semaphore.h>
int main ( int argc, char * argv[ ] ) {
sem_t* sem = sem_open ( argv[ 1 ] , O_CREAT, 0644 , atoi ( argv[ 2 ] ) ) ;
}
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <fcntl.h>
# include <signal.h>
# include <semaphore.h>
void handler ( int sig) {
printf ( "PID:%d,prepare to leave\n" , getpid ( ) ) ;
}
int main ( int argc, char * argv[ ] ) {
signal ( SIGINT, handler) ;
sem_t* sem = sem_open ( argv[ 1 ] , O_RDWR) ;
sem_wait ( sem) ;
int val;
sem_getvalue ( sem, & val) ;
printf ( "PID:%d,enter. park %d\n" , getpid ( ) , val) ;
printf ( "PID:%d,do something\n" , getpid ( ) ) ;
pause ( ) ;
sem_post ( sem) ;
sem_getvalue ( sem, & val) ;
printf ( "PID:%d,leave. park %d\n" , getpid ( ) , val) ;
}
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <fcntl.h>
# include <signal.h>
# include <semaphore.h>
void handler ( int sig) {
printf ( "PID:%d,prepare to leave\n" , getpid ( ) ) ;
}
int main ( int argc, char * argv[ ] ) {
signal ( SIGINT, handler) ;
sem_t* sem = sem_open ( argv[ 1 ] , O_RDWR) ;
if ( - 1 == sem_trywait ( sem) ) {
perror ( "no park" ) ;
return 1 ;
}
int val;
sem_getvalue ( sem, & val) ;
printf ( "PID:%d,enter. park %d\n" , getpid ( ) , val) ;
printf ( "PID:%d,do something\n" , getpid ( ) ) ;
pause ( ) ;
sem_post ( sem) ;
sem_getvalue ( sem, & val) ;
printf ( "PID:%d,leave. park %d\n" , getpid ( ) , val) ;
}