linux信号量机制(semaphore
  信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。当公共资源增加时,调用函数sem_post()增加信号量。只有当信号量值大于0时,才能使用公共资源,使用后,函数sem_wait()减少信号量。函数sem_trywait()和函数pthread_ mutex_trylock()起同样的作用,它是函数sem_wait()的非阻塞版本。它们都在头文件/usr/include/semaphore.h中定义。
  信号量的数据类型为结构sem_t,它本质上是一个长整型的数。函数sem_init()用来初始化一个信号量。它的原型为:
  extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));
  sem为指向信号量结构的一个指针;pshared不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享;value给出了信号量的初始值。
  函数sem_post( sem_t *sem )用来增加信号量的值。当有线程阻塞在这个信号量上时,调用这个函数会使其中的一个线程不在阻塞,选择机制同样是由线程的调度策略决定的。
函数sem_wait( sem_t *sem )被用来阻塞当前线程直到信号量sem的值大于0,解除阻塞后将sem的值减一,表明公共资源经使用后减少。
函数sem_trywait ( sem_t *sem )是函数sem_wait()的非阻塞版本,它直接将信号量sem的值减一。
  函数sem_destroy(sem_t *sem)用来释放信号量sem。
1:使用信号量。例子中一共有4个线程,其中两个线程负责从文件读取数据到公共的缓冲区,另两个线程从缓冲区读取数据作不同的处理(加和乘运算)。
/* File sem.c */
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#define MAXSTACK 100
int stack[MAXSTACK][2];
int size=0;
sem_t sem;
/* 从文件1.dat读取数据,每读一次,信号量加一*/
void ReadData1(void){
FILE *fp=fopen("1.dat","r");
while(!feof(fp)){
fscanf(fp,"%d %d",&stack[size][0],&stack[size][1]);
sem_post(&sem);
++size;
}
fclose(fp);
}
/*从文件2.dat读取数据*/
void ReadData2(void){
FILE *fp=fopen("2.dat","r");
while(!feof(fp)){
fscanf(fp,"%d %d",&stack[size][0],&stack[size][1]);
sem_post(&sem);
++size;
}
fclose(fp);
}
/*阻塞等待缓冲区有数据,读取数据后,释放空间,继续等待*/
void HandleData1(void){
while(1){
sem_wait(&sem);
printf("Plus:%d+%d=%d\n",stack[size][0],stack[size][1],
stack[size][0]+stack[size][1]);
--size;
}
}
void HandleData2(void){
while(1){
sem_wait(&sem);
printf("Multiply:%d*%d=%d\n",stack[size][0],stack[size][1],
stack[size][0]*stack[size][1]);
--size;
}
}
int main(void){
pthread_t t1,t2,t3,t4;
sem_init(&sem,0,0);
pthread_create(&t1,NULL,(void *)HandleData1,NULL);
pthread_create(&t2,NULL,(void *)HandleData2,NULL);
pthread_create(&t3,NULL,(void *)ReadData1,NULL);
pthread_create(&t4,NULL,(void *)ReadData2,NULL);
/* 防止程序过早退出,让它在此无限期等待*/
linux下的sleep函数pthread_join(t1,NULL);
}
  在Linux下,用命令gcc -lpthread sem.c -o sem生成可执行文件sem。 事先编辑好数据文件1.dat和2.dat,假设它们的内容分别为1 2 3 4 5 6 7 8 9 10和 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 ,运行./sem,得到如下的结果:
Multiply:-1*-2=2
Plus:-1+-2=-3
Multiply:9*10=90
Plus:-9+-10=-19
Multiply:-7*-8=56
Plus:-5+-6=-11
Multiply:-3*-4=12
Plus:9+10=19
Plus:7+8=15
Plus:5+6=11
  从中可以看出各个线程间的竞争关系。而数值并未按我们原先的顺序显示出来这是由于size这个数值被各个线程任意修改的缘故。这也往往是多线程编程要注意的问题
信号量和临界区
学习目标:
学习信号量及其属性
进行同步实验
研究临界区的行为
使用POSIX命名信号量和无名信号量
理解信号量的管理
1. 临界区
临界区是指必须以互斥的方式执行的代码段,也就是说临界区范围内只能由一个活动的线程。
例如:修改共享变量的过程中其他的执行线程可能会访问共享变量,那么修改共享变量的代码就被看成是临界区的一部分。
临界区问题指用安全、公平和对称的方式来执行临界区代码的问题
2. 信号量
信号量是(S)一个整型变量,它带有两个原子操作信号量锁定wait和信号量解锁signal
可以将其看成一个整数值和一个等待signal操作的进程列表。
wait操作:如果S大于零,wait就在一个原子操作中测试S,并对其进行减量运算;
              如果S等于零,wait就在一个原子操作中测试S,并阻塞调用程序。
signal操作:如果有线程在信号量上阻塞,S就等于零,signal就会解除对某个等待线程的阻塞;
                    如果没有线程在信号量上阻塞,signal就对S进行增量运算。
信号量作用:
a:保护临界区
wait(&s)
<critical section>
signal(&s);
<remainder section>
b:线程同步
process 1 executes:          process 2 executes:
a;                          wait(&sync);
signal(&sync);                b;
3. POSIX:SEM无名信号量
信号量是一个sem_t类型的变量,有相关的原子操作来对它的值进行初始化、增量和减量操作。如果一个实现在unistd.h中定义了_POSIX_SEMAPHORES,那么这个实现就支持PO
SIX:SEM信号量。无名信号量和命名信号量之间的区别类似于普通管道和命名管道之间的区别
信号量的申明:
#include <semaphore.h>
sem_t sem;
信号量的初始化:必须在使用信号量之前对其进行初始化
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned value);
没有规定成功时返回值,不成功返回-1并设置errno,必须检测的错误码:
EINVAL value大于SEM_VALUE_MAX
ENOSPC 初始化资源已经耗尽,或者信号量的数目超出了SEM_NSEMS_MAX的范围
EPERM 调用程序不具有适当的特权
参数pshared等于0,说明信号量只能由初始化这个信号量的进程中的线程使用;
如果pshared非零,任何可以访问sem的进程都可以使用这个信号量。
注:在创建信号量之后创建一个子进程,并没有提供对信号量的访问,子进程收到的是信号量的拷贝,而不是真的信号量。
例:创建一个有进程中的线程使用的信号量
sem_t semA;
if (sem_init(&semA, 0, 1) == -1 )
{
perror (“failed to initialize semaphore semA”);
}
信号量的销毁:
#include <semaphore.h>
int sem_destroy(sem_t *sem);
成功返回0,不成功返回-1并设置errno,检测错误码:
EINVAL sem不是有效的信号量
例:
if (sem_destroy(&semA) == -1)
{
perror (“Failed to destroy semA”);
}
POSIX申明:销毁一个已经销毁的信号量的结果是未定义的。有其他线程阻塞在一个信号量上时,销毁这个信号量的结果也是未定义的。
4. POSIX:SEM信号量的操作
这里描述的信号量的操作适用与无名信号量,同时也适用命名信号量
signal操作:
#include <semaphore.h>
int sem_post(sem_t *sem);
成功返回0,不成功返回-1并设置errno,必须检测的错误码:
EINVAL *sem不对应有效的信号量
函数sem_init是信号安全的,可以在信号处理程序中调用它。
wait操作:
#include <semaphore.h>
int sem_trywait(sem_t *sem);
int sem_wait(sem_t *sem);