在之前的博客中linux信号量—互斥与同步谈到无名信号量。无名信号量主要用于线程间的通信,保存在内存中,如果想要在进程间同步就必须把无名信号量放在进程间的共享内存中。而在进程间的通信中同步用的通常是有名信号量。有名信号量一般保存在/dev/shm/ 目录下。像文件一样存储在文件系统中。
无名信号量的操作主要涉及到以下六个函数:
- sem_init 用于创建一个信号量,并能初始化它的值。
- sem_wait 和 sem_trywait 相当于 P 操作,它们都能将信号量的值减一,两者的区别在于若信号量小于零时,sem_wait 将会阻塞进程,而 sem_trywait 则会立即返回。
- sem_post 相当于 V 操作,它将信号量的值加一同时发出信号唤醒等待的进程。
- sem_getvalue 获取信号量的值。
- sem_destroy 删除信号量
有名信号量和无名信号量的区别和联系:
- 无名信号量的创建信号量函数是sem_init,有名信号量的则是sem_open函数。
- 无名信号量的删除信号量函数是sem_destroy,有名信号量的则是用sem_close函数关闭有名信号量,但是想要把信号量从文件系统删除得用sem_unlink函数。
- 其他的PV操作有名信号量是完全和无名信号量一致的。
我们主要来看看有名信号量独特于无名信号量的函数,公共函数就不详细叙述了,请参考博文linux信号量—互斥与同步
sem_open函数
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
sem_t *sem_open(const char *name, int oflag);//打开一个有名信号量,此时有名信号量是已经存在了的。
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);//创建有名信号量
返回值:若成功,返回信号量的地址;若出错,返回SEM_FAILED
参数:
- name:信号量文件名。
- flags:sem_open() 函数的行为标志。
- mode:文件权限,可用八进制表示,如0777.
- value:信号量初始值。
sem_close函数
#include <semaphore.h>
int sem_close(sem_t *sem);//关闭有名信号量
返回值:若成功,返回0;若出错,返回-1
参数:
- sem:指向信号量的指针。
sem_unlink函数
#include <semaphore.h>
int sem_unlink(const char *name);//删除有名信号量文件
返回值:若成功,返回0;若出错,返回-1
参数:
- name:有名信号量文件名。
下文通过6个测试程序,解析有名信号量的各种运用场景:
- name_sem.c 讲的是有名信号量实现亲缘进程间互斥功能。
- sync_name_sem.c 讲的是有名信号量实现亲缘进程间同步功能。
- NameSemWrite_1.c 和 NameSemWrite_2.c 讲的是在两个不同的程序测试有名信号量的同步功能。
- share_memory_name_sem_1.c 和 share_memory_name_sem_2.c 讲的是有名信号量来实现共享内存读写数据的同步。
测试程序:
有名信号量实现亲缘进程间互斥功能:
/* name_sem.c*/
#include <unistd.h>
#include <stdio.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#define FILENAME "name_sem_file"
int main ()
{
sem_t *sem = NULL;
//有名信号量在fork子程序中继承下来
//跟open()打开方式很相似,不同进程只要名字一样,那么打开的就是同一个有名信号量
sem = sem_open("name_sem", O_CREAT|O_RDWR, 0666, 1); //信号量值为 1
if(sem == SEM_FAILED)
{
perror("sem_open");
exit(-1);
}
pid_t pid; //pid表示fork函数返回的值
int count=0;
int fd = open(FILENAME,O_RDWR | O_CREAT,0777);
if(fd < 0)
{
perror("open");
}
if ((pid = fork()) < 0)
{
perror("fork");
exit(-1);
}
else if (pid == 0)
{
char write_buf[] = "1";
int i = 0;
printf("child: pid = %ld,ppid = %ld, fd = %d, count = %d\n",(long)getpid(),(long)getppid(),fd,count);
for(i = 0;i < 2;i++)
{
/*信号量减一,P 操作*/
sem_wait(sem);
for(i = 0;i<5;i++)
{
if (write(fd,write_buf,sizeof(write_buf)))
{
perror("write");
}
count++;
}
printf("in child....and count = %d\n",count);
/*信号量加一,V 操作*/
sem_post(sem);
sleep(2);
}
exit(0);
}
else
{
char write_buf[] = "2";
int i = 0;
printf("parent: pid = %ld,ppid = %ld, fd = %d, count = %d\n",(long)getpid(),(long)getppid(),fd,count);
for(i = 0; i<2; i++)
{
/*信号量减一,P 操作*/
sem_wait(sem);
for(i = 0;i<5;i++)
{
if (write(fd,write_buf,sizeof(write_buf)))
{
perror("write");
}
count++;
}
printf("in father.... count = %d\n",count);
/*信号量加一,V 操作*/
sem_post(sem);
sleep(2);
}
printf("Waiting for the child process to exit\n");
//等待子进程退出
waitpid(pid,NULL,0);
sem_del("name_sem"); //删除信号量文件 name_sem
exit(0