信号量分两种,一种是无名信号量,一种是有名信号量。无名信号量一般用于线程间同步或互斥,而有名信号量一般用于进程间同步或互斥。它们的区别和管道及命名管道的区别类似,无名信号量则直接保存在内存中,而有名信号量要求创建一个文件。
函数
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
/* 创建一个有名信号量 */
// 1. 当有名信号量存在时使用:
sem_t *sem_open(const char *name, int oflag);
// 2. 当有名信号量不存在时使用:
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);
/* 参数:
name:信号量文件名。注意,不能指定路径名。因为有名信号量,默认放在/dev/shm 里,如下图:
flags:sem_open() 函数的行为标志。
mode:文件权限(可读、可写、可执行)的设置。
value:信号量初始值。
*/
/* 返回值:
成功:信号量的地址
失败:SEM_FAILED
*/
#include <semaphore.h>
/* 关闭有名信号量。 */
int sem_close(sem_t *sem);
/* 参数:
sem:指向信号量的指针。
*/
/* 返回值:
成功:0
失败:-1
*/
#include <semaphore.h>
/* 删除有名信号量的文件。 */
int sem_unlink(const char *name);
/* 参数:
name:有名信号量文件名。
*/
/* 返回值:
成功:0
失败:-1
*/
有名信号量的 P, V 操作和无名信号量一样,使用 int sem_wait(sem_t *sem); 和 int sem_post(sem_t *sem); 进行减操作和加操作。
用于进程间互斥
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <wait.h>
#include <semaphore.h>
void function1(sem_t* sem, char* str)
{
sem_wait(sem); //检测sem是否非零,非零则不阻塞,并进行减操作
while(*str != '\0')
{
putchar(*str);
fflush(stdout);
str++;
sleep(1);
}
putchar('\n');
sem_post(sem); //对sem进行加操作
}
int main()
{
sem_t* sem = NULL;
pid_t pid = fork();
if(pid < 0)
{
perror("fork");
_exit(-1);
}
else if(pid == 0)
{/* 子进程 */
/* 打开一个信号量,没有就创建 */
sem = sem_open("nameSem", O_CREAT | O_RDWR, 0666, 1);
if(sem == SEM_FAILED)
{
perror("sem_open");
_exit(-1);
}
char* child_str = "Hello";
function1(sem,child_str);
/* 关闭信号量 */
sem_close(sem);
_exit(0);
}
else
{/* 父进程 */
/* 打开一个信号量,没有就创建 */
sem = sem_open("nameSem", O_CREAT | O_RDWR, 0666, 1);
if(sem == SEM_FAILED)
{
perror("sem_open");
_exit(-1);
}
char* parent_str = "World";
function1(sem,parent_str);
/* 关闭信号量 */
sem_close(sem);
wait(NULL);
}
/* 删除有名信号量 */
sem_unlink("nameSem");
return 0;
}
如果不使用互斥,执行如下。两个单词会乱序打印。
╭─lingyun@manjaro ~/Document/codes
╰─➤ gcc exam.c -lpthread
╭─lingyun@manjaro ~/Document/codes
╰─➤ ./a.out
WHoerllldo
使用信号量互斥的情况,执行后则如下。可见,会完整输出一个单词,再输出另一个单词。
╭─lingyun@manjaro ~/Document/codes
╰─➤ gcc exam.c -lpthread
╭─lingyun@manjaro ~/Document/codes
╰─➤ ./a.out
World
Hello
用于进程间同步
// exam.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
void function1(sem_t* sem1, sem_t* sem2, char* str)
{
sem_wait(sem1); //检测sem是否非零,非零则不阻塞,并进行减操作
while(*str != '\0')
{
putchar(*str);
fflush(stdout);
str++;
sleep(1);
}
putchar('\n');
sem_post(sem2); //对sem进行加操作
}
int main()
{
sem_t* sem1 = NULL;
sem_t* sem2 = NULL;
sem1 = sem_open("nameSem1", O_CREAT, 0777, 1); // sem1 设为 1
sem2 = sem_open("nameSem2", O_CREAT, 0777, 0); // sem2 设为 0,使 sem_wait(sem2) 阻塞
function1(sem1,sem2,"Hello");
return 0;
}
// exam2.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
void function2(sem_t* sem1, sem_t* sem2, char* str)
{
sem_wait(sem2); //检测sem是否非零,非零则不阻塞,并进行减操作
while(*str != '\0')
{
putchar(*str);
fflush(stdout);
str++;
sleep(1);
}
putchar('\n');
sem_post(sem1); //对sem进行加操作
}
int main()
{
sem_t* sem1 = NULL;
sem_t* sem2 = NULL;
sem1 = sem_open("nameSem1", O_CREAT, 0777, 1); // sem2 设为 1
sem2 = sem_open("nameSem2", O_CREAT, 0777, 0); // sem2 设为 0,使 sem_wait(sem2) 阻塞
function2(sem1,sem2,"world");
return 0;
}
先执行 exam2,程序阻塞了,并未打印任何字符:
╭─lingyun@manjaro ~/Document/codes
╰─➤ gcc exam2.c -lpthread -o exam2
╭─lingyun@manjaro ~/Document/codes
╰─➤ ./exam2
再执行 exam1,打印出了 “Hello” :
╭─lingyun@manjaro ~/Document/codes
╰─➤ gcc exam.c -lpthread -o exam1
╭─lingyun@manjaro ~/Document/codes
╰─➤ ./exam1
Hello
在 exam1 执行完后,exam2 才会打印出 “world” :
╭─lingyun@manjaro ~/Document/codes
╰─➤ gcc exam2.c -lpthread -o exam2
╭─lingyun@manjaro ~/Document/codes
╰─➤ ./exam2
world