分类: C/C++
2011-05-11 16:53:33
有两种基本信号:二进制信号和计数信号量。二进制信号的值只能是 0 或 1,计数信号量可以是任意非负值。二进制信号在逻辑上相当于一个互斥锁。不过,尽管不会强制,但互斥锁应当仅由持有该锁的线程来解除锁定。因为不存在“持有信号的线程”这一概念,所以,任何线程都可以执行 V 或 sem_post(3RT) 操作。
1、命名信号量和未命名信号量
POSIX信号可以是未命名的,也可以是命名的。未命名信号在进程内存中分配,并会进行初始化。未命名信号可能可供多个进程使用,具体取决于信号的分配和初始化的方式。未命名信号可以是通过fork()继承的专用信号,也可以通过用来分配和映射这些信号的常规文件的访问保护功能对其进行保护。命名信号类似于进程共享的信号,区别在于命名信号是使用路径名而非pshared值引用的。命名信号可以由多个进程共享。命名信号具有属主用户ID、组ID和保护模式。对于open、retrieve、close和remove命名信号,可以使用以下函数:sem_open、sem_getvalue、sem_close和sem_unlink。通过使用sem_open,可以创建一个命名信号,其名称是在文件系统的名称空间中定义的。
2、计数信号量概述
从概念上来说,信号量是一个非负整数计数。信号量通常用来协调对资源的访问,其中信号计数会初始化为可用资源的数目。然后,线程在资源增加时会增加计数,在删除资源时会减小计数,这些操作都以原子方式执行。如果信号计数变为零,则表明已无可用资源。计数为零时,尝试减小信号的线程会被阻塞,直到计数大于零为止。
由于信号无需由同一个线程来获取和释放,因此信号可用于异步事件通知,如用于信号处理程序中。同时,由于信号包含状态,因此可以异步方式使用,而不用象条件变量那样要求获取互斥锁。但是,信号的效率不如互斥锁高。缺省情况下,如果有多个线程正在等待信号,则解除阻塞的顺序是不确定的。信号在使用前必须先初始化,但是信号没有属性。
3、初始化信号量
使用sem_init(3RT)可以将sem所指示的未命名信号变量初始化为value。
sem_init语法
int sem_init(sem_t *sem, int pshared, unsigned int value);
#include
sem_t sem;
int pshared;
int ret;
int value;
pshared =0;
value =1;
ret = sem_init(&sem, pshared, value);
如果pshared的值为零,则不能在进程之间共享信号。如果pshared的值不为零,则可以在进程之间共享信号。
注意:
(1)多个线程决不能初始化同一个信号。
(2)不得对其他线程正在使用的信号重新初始化。
4、初始化进程内信号量
pshared为0时,信号只能由该进程内的所有线程使用。
#include
sem_t sem;
int ret;
int count = 4;
ret = sem_init(&sem, 0, count);
5、初始化进程间信号量
pshared不为零时,信号可以由其他进程共享。
#include
sem_t sem;
int ret;
int count = 4;
ret = sem_init(&sem, 1, count);
6、sem_init返回值
sem_init()在成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下任一情况,该函数将失败并返回对应的值。
EINVAL
描述:参数值超过了SEM_VALUE_MAX。
ENOSPC
描述:初始化信号所需的资源已经用完。到达信号的SEM_NSEMS_MAX限制。
ENOSYS
描述:系统不支持sem_init()函数。
EPERM
描述:进程缺少初始化信号所需的适当权限。
7、增加信号
sem_post语法
int sem_post(sem_t *sem);
#include
sem_t sem;
int ret;
ret = sem_post(&sem);
如果所有线程均基于信号阻塞,则会对其中一个线程解除阻塞。
sem_post返回值
sem_post()在成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下情况,该函数将失败并返回对应的值。
EINVAL
描述: sem所指示的地址非法。
8、基于信号计数进行阻塞
使用sem_wait(3RT)可以阻塞调用线程,直到sem所指示的信号计数大于零为止,之后以原子方式减小计数。
sem_wait语法
int sem_wait(sem_t *sem);
#include
sem_t sem;
int ret;
ret = sem_wait(&sem);
sem_wait返回值
sem_wait()在成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下任一情况,该函数将失败并返回对应的值。
EINVAL
描述: sem所指示的地址非法。
EINTR
描述:此函数已被信号中断。
9、减小信号计数
使用sem_trywait(3RT)可以在计数大于零时,尝试以原子方式减小sem所指示的信号计数。
sem_trywait语法
int sem_trywait(sem_t *sem);
#include
sem_t sem;
int ret;
ret = sem_trywait(&sem);
此函数是sem_wait()的非阻塞版本。sem_trywait()在失败时会立即返回。
sem_trywait返回值
sem_trywait()在成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下任一情况,该函数将失败并返回对应的值。
EINVAL
描述: sem所指示的地址非法。
EINTR
描述:此函数已被信号中断。
EAGAIN
描述:信号已为锁定状态,因此该信号不能通过sem_trywait()操作立即锁定。
10、销毁信号状态
使用sem_destroy(3RT)可以销毁与sem所指示的未命名信号相关联的任何状态。
sem_destroy语法
int sem_destroy(sem_t *sem);
#include
sem_t sem;
int ret;
ret = sem_destroy(&sem);
sem_destroy返回值
sem_destroy()在成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下情况,该函数将失败并返回对应的值。
EINVAL
#include
#include
#include
#define MAXSTACK 100
int stack[MAXSTACK][2];
int size=0;
sem_t sem;
//从文件1.dat读取数据,每读一次,信号量加1
void ReadData1(void){
}
//从文件2.dat读取数据,每读一次,信号量减1
void ReadData2(void){
}
//阻塞等待缓冲区有数据,读取数据进行加法运算后,释放空间,继续等待
void HandleData1(void){
}
//阻塞等待缓冲区有数据,读取数据进行乘法运算后,释放空间,继续等待
void HandleData2(void){
}
int main(void){
}
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这个数值被各个线程任意修改的缘故。这也往往是多线程编程要注意的问题。
#include
#include
#include
void *producter_f(void *arg);
void *consumer_f(void *arg);
sem_t sem;
int running = 1;
int main (void)
{
}
void *producter_f(void *arg)
{
}
void *consumer_f(void *arg)
{
}
struct mystruct
{
};
sem_t sem;
int main (void)
{
}
//数据保存到指定文件
void *SaveFile(char *FileName, void *source, int size)
{
}
//读取数据到指定缓冲区
void *ReadFile(char *FileName, void *source, int size)
{
}