POSIX信号量有两种:有名信号量和无名信号量,无名信号量也被称作基于内存的信号量。有名信号量通过IPC名字进行进程间的同步,而无名信号量如果不是放在进程间的共享内存区中,是不能用来进行进程间同步的,只能用来进行线程同步,线程同步将在后面讲到,这里我们重点研究有名信号量。
3.1.1 创建一个信号量
创建的过程还要求初始化信号量的值。根据信号量取值(代表可用资源的数目)的不同,POSIX信号量还可以分为:
二值信号量:信号量的值只有0和1,这和互斥量很类型,若资源被锁住,信号量的值为0,若资源可用,则信号量的值为1;
计数信号量:信号量的值在0到一个大于1的限制值(POSIX指出系统的最大限制值至少要为32767)。该计数表示可用的资源的个数。
sem_t *sem_open(const char *name,int oflag, mode_t mode , int value); /* 返回:若成功则为指向信号量的指针,有名字的信号量作为文件被创建在/dev/shm里。若出错则为SEM_FAILED, name参数:mq_open、sem_open、shm_open这三个函数的name参数都使用“Posix IPC名字”,它可能某个文件系统中的一个真正的路径名,也可能不是。 oflag参数:可以是0、O_CREAT或O_CREAT|O_EXCL。如果指定O_CREAT标志而没有指定O_EXCL,那么只有当所需的信号量尚未存在时才初始化它。但是如果在所需的信号量存在的情况下指定O_CREAT|O_EXCL却会报错。 mode参数:指定权限位。 value参数:指定信号量的初始值。该初始值不能超过SEM_VALUE_MAX(这个常值必须至少为32767)。二值信号量的初始值通常为1,计数信号量的初始值则往往大于1。 */ int sem_close(sem_t *sem); /* 一个进程终止时,内核会对其上打开着的所有有名信号量自动执行关闭操作,不论该进程是自愿终止还是非自愿终止。 关闭一个信号量并没有将它从系统中删除。Posix有名信号量是随内核持续的。即使当前没有进程打开着某个信号量, 他的值仍然保持。 */ int sem_unlink(const char *name); /* 删除创建的信号量文件 */
3.1.2 等待一个信号量
该操作会检查信号量的值,如果其值小于或等于0,那就阻塞,直到该值变成大于0,然后等待进程将信号量的值减1,进程获得共享资源的访问权限。
函数
sem_wait()
用来阻塞当前线程直到信号量sem的值大于0,解除阻塞后将sem的值减去1,表明公共资源经使用后减少,其函数原型是:
int sem_wait(sem_t *sem);
成功返回0,错误返回-1,设置errno
函数sem_trywait()与函数sem_wait()的区别是当信号量的值等于0时,sem_trywait()函数不会阻塞当前线程。
int sem_trywait(sem_t *sem);
成功返回0,错误返回-1,设置errno,当没等到资源的时候,errno为EAGAIN
3.1.3 释放一个信号量
该操作将信号量的值加1,如果有进程阻塞着等待该信号量,那么其中一个进程将被唤醒。
函数
sem_post()
用来增加信号量的值,其函数原型是:
int sem_post(sem_t* sem);
3.1.4 综合例子
void error_print(const char* msg) { perror(msg); exit(-1); } int main(int argc, char** argv) { int ret; sem_t* sem = sem_open("/mysem", O_CREAT, 0666, 1); if(sem == NULL) error_print("sem_open"); puts("wait semahpore...\n"); sem_wait(sem); puts("get"); sleep(5); puts("post"); sem_post(sem); sem_close(sem); return 0; }
编译时需要加上-lpthread:
gcc semtest.c -o semtest -lpthread
运行两次改程序,观察现象。