POSIX信号量是一种用于同步进程之间对共享资源访问的机制。它允许进程在访问共享资源之前进行互斥和同步操作,以确保数据的一致性和正确性。POSIX信号量通常由一个整数值表示,可以进行原子增减操作,以及等待和通知操作。
有名信号量(Named Semaphore):
无名信号量(Unnamed Semaphore):
sem_init
函数进行初始化,使用 sem_destroy
函数进行销毁。创建或打开有名信号量
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);
参数:
返回值:
其中入参的mode选择如下:
S_IRUSR
:用户读权限S_IWUSR
:用户写权限S_IRGRP
:组读权限S_IWGRP
:组写权限S_IROTH
:其他用户读权限S_IWOTH
:其他用户写权限关闭有名信号量
int sem_close(sem_t *sem);
参数:
返回值:
初始化无名信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数:
返回值:
销毁无名信号量
int sem_destroy(sem_t *sem);
参数:
返回值:
从系统中删除有名信号量
int sem_unlink(const char *name);
参数:
返回值:
等待信号量减小,如果信号量的值大于0,则将其减小1并立即返回,否则会阻塞当前线程直到信号量变为大于0为止
int sem_wait(sem_t *sem);
参数:
返回值:
尝试等待信号量减小的非阻塞版本。如果信号量的值大于0,则将其减小1并立即返回,否则会立即返回,并且不会阻塞当前线程
int sem_trywait(sem_t *sem);
参数:
返回值:
增加信号量,信号量值+1
int sem_post(sem_t *sem);
参数:
返回值:
函数允许设置一个超时时间,如果在指定的时间内未能获得信号量,函数将返回一个特定的错误码。
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
参数:
返回值:
struct timespec {
time_t tv_sec; // 秒
long tv_nsec; // 纳秒
};
获取当前信号量的值
int sem_getvalue(sem_t *sem, int *sval);
参数:
返回值:
函数 | 适用 |
---|---|
sem_open |
有名信号量 |
sem_close |
有名信号量 |
sem_init |
无名信号量 |
sem_destroy |
无名信号量 |
sem_unlink |
有名信号量 |
sem_wait |
两者皆可 |
sem_trywait |
两者皆可 |
sem_post |
两者皆可 |
sem_timedwait |
两者皆可 |
sem_getvalue |
两者皆可 |
测试代码如下:
#include
#include
#include
#include
#include
#include
#include
#include
// 打印时分秒的宏
#define PRINT_MIN_SEC do { \
time_t t = time(NULL); \
struct tm *tm_ptr = localtime(&t); \
printf("%02d:%02d:%02d:", tm_ptr->tm_hour, tm_ptr->tm_min, tm_ptr->tm_sec);\
} while (0);printf
// 打印当前信号量的值
void printfValue(sem_t *sem)
{
// 打印当前值
int value;
sem_getvalue(sem, &value);
PRINT_MIN_SEC("Current value of semaphore: %d\n", value);
}
int main(int argc, char *argv[])
{
sem_t *sem;
// 命令行参数
// 第一个参数 I表示初始化 W表示等待 P表示增加信号量的值
if (argc != 2)
{
printf("Usage: %s I|W|P", argv[0]);
return 0;
}
if (!strcmp(argv[1], "I"))
{
PRINT_MIN_SEC("Init sem ...\n");
if((sem = sem_open("sem_p", O_CREAT, 0644, 0)) == SEM_FAILED)
{
perror("sem_open err");
return 0;
}
PRINT_MIN_SEC("Init sem OK\n");
}
else if (!strcmp(argv[1], "W"))
{
if((sem = sem_open("sem_p", 0)) == SEM_FAILED)
{
perror("sem_open err");
return 0;
}
PRINT_MIN_SEC("Wait sem ...\n");
sem_wait(sem);
PRINT_MIN_SEC("Wait sem OK\n");
}
else if (!strcmp(argv[1], "P"))
{
if((sem = sem_open("sem_p", 0)) == SEM_FAILED)
{
perror("sem_open err");
return 0;
}
PRINT_MIN_SEC("Post sem ...\n");
sem_post(sem);
PRINT_MIN_SEC("Post sem OK\n");
}
else
{
printf("Usage: %s I|W|P", argv[0]);
return 0;
}
printfValue(sem);
sem_close(sem);
return 0;
}
通过命令行不同参数实现不同功能I表示初始化 W表示等待 P表示增加信号量的值,编译的时候需要加-pthread,开启一个控制台,执行初始化和等待:
另起一个控制台,发起新的进程执行post操作,阻塞的进程可以成功执行完毕,完成进程间通信:
在/dev/shm目录下可以查看到信号量对应的文件:
测试代码如下:
#include
#include
#include
#include
#include
#include
#include
#include
#include
// 打印时分秒的宏
#define PRINT_MIN_SEC do { \
time_t t = time(NULL); \
struct tm *tm_ptr = localtime(&t); \
printf("%02d:%02d:%02d:", tm_ptr->tm_hour, tm_ptr->tm_min, tm_ptr->tm_sec);\
} while (0);printf
sem_t mutex;
void* thread_function_1(void* arg)
{
PRINT_MIN_SEC("In thread_function_1\n");
while(1)
{
sleep(3);
sem_post(&mutex);
}
}
void* thread_function_2(void* arg)
{
PRINT_MIN_SEC("In thread_function_2\n");
while(1)
{
PRINT_MIN_SEC("thread_function_2 sem_wait ...\n");
sem_wait(&mutex);
PRINT_MIN_SEC("thread_function_2 sem_wait OK\n");
}
}
int main(int argc, char *argv[])
{
sem_init(&mutex, 0, 0);
pthread_t thread;
pthread_create(&thread, NULL, thread_function_1, NULL);
pthread_create(&thread, NULL, thread_function_2, NULL);
pthread_join(thread, NULL);
sem_destroy(&mutex);
return 0;
}
测试无名信号线程间通信,发起两个线程,线程1每隔3秒执行一次sem_post,线程2一直等待获取,测试结果如下:
本文阐述了进程间通信之信号量(POSIX)的定义、应用场景、优缺点等,列举了编程中使用的接口,编写了测试用例测试相关功能。