linux进程间通信–信号量(POSIX 版本)
System V
信号量是通过标识符而不是大多数UNIX I/O 和IPC所采用的文件描述符来引用的。由于这些潜在的缺点,因此POSIX
标准又重新定义了信号量API。
POSIX
标准信号量semaphore
不仅可用于同一进程下的各个线程同步,也可以用于不同进程间同步。主要用于协调进程线程对共享资源的访问。
libc库实现了POSIX和System V两种接口标准的信号量API。这里主要讨论POSIX接口的信号量API.
信号量的原理与使用
:信号量只能进行两种操作,如下:
- P(sv):如果sv的值大于零,它就减一。如果为零,就挂起该进程的执行.P(sv)的函数如sem_wait()。
- V(sv):如果有进程因等待sv而挂起,就让他恢复运行,如果没有,就加1.V(sv)操作的函数如sem_post().
POSIX接口标准中的信号量可分为命名信号量
和未命名信号量
:
未命名信号量
:即信号量没有名称。这种信号量由sem_init()创建。由sem_destroy()销毁。在多个线程中用起来很方便。命名信号量
:即信号量有名称。这种信号量由sem_open()创建。可以通过名字访问,因此可以被任何已知它们名字的进程使用。命名信号量创建后可以在文件系统/dev/shm
下查看到。
与信号量有关的宏:
SEM_NSEMS_MAX
:是一个进程能够拥有的POSIX 信号量的最大数目.标准要求至少支持256个。SEM_VALUE_MAX
:一个POSIX 信号量值能够取的最大值.标准要求至少支持32767个。
信号量相关的函数接口如下:
-
int sem_init(sem_t *sem, int pshared, unsigned value)
- 初始化一个未命名的信号量sem.
- sem:要被初始化的信号量
- pshared:
- if != 0:信号量在进程之间共享。这时要求sem实体内存必须各个进程都能访问到。即位于共享内存区。mmap()映射。
- if == 0:信号量在本进程下的多个线程之间使用。
- value:信号量的初始值。
- return: if true:0,if false:-1.
-
int sem_destroy(sem_t *sem)
- 销毁未命名的信号量sem.
- sem:要销毁的信号量
- return: if true:0,if false:-1.
-
sem_t *sem_open(const char *name, int oflag, ...)
- 将命名信号量和进程之间建立连接。
- name:信号量名
- oflag:
- oflag = 0:如果信号量存在,则连接到该信号量.
- oflag = O_CREAT:如果信号量存在,则连接到该信号量,否则创建该信号量。
- oflag = O_CREAT | O_EXCL:只有当信号不存在时创建,否则报错。
- 可选参数:当oflag & O_CREAT == true时使用。
- mode:表示谁可以访问信号量。即访问权限。
- value:初始化的信号量值。最大为SEM_VALUE_MAX。
- return:if true:信号量指针。if false:SEM_FAILED.
-
int sem_close(sem_t *sem)
- 关闭命名信号量(由sem_open()创建)。不要用于未命名的信号量(sem_init()创建)。如果没有通过调用sem_unlink()来删除该信号量,那么sem_close()对已命名的信号量没有影响。但是,当已命名信号量被完全解除链接时,该信号量将在最后一个任务关闭时消失。
- sem:信号量指针
- return: if true:0,if false:-1.
-
int sem_unlink(const char *name)
- 删除由输入名称参数命名的信号量。如果在调用sem_unlink()时,有一个或多个进程打开了命名的信号量,那么该信号量的销毁将被推迟,直到通过调用sem_close()销毁所有引用为止。
- name:信号量名称。
- return: if true:0,if false:-1.
-
int sem_wait(sem_t *sem)
- 等待获取信号量。一般用于请求访问共享资源。
- return:
- if true:0,
- if false:-1.
- EINVAL:输入参数无效。
- EINTR:信号中断了信号量的等待而非获取到信号量。
-
int sem_timedwait(sem_t *sem, conststructtimespec *abstime)
- 功能类似sem_wait(),带有超时时间。
- abstime:超时等待时间。
- return: if true:0,
- if false:-1.
- EINVAL:输入参数无效。
- ETIMEDOUT:超时
- EDEADLK:检测到死锁情况。
- EINTR:信号中断了信号量的等待而非获取到信号量。
- if false:-1.
-
int sem_trywait(sem_t *sem)
- 尝试去获取信号量。如果未获取到不阻塞。
- return: if true:0,
- if false:-1.
- EINVAL:输入参数无效。
- EAGAIN:表示没有获得信号量。
- if false:-1.
-
int sem_post(sem_t *sem)
- 信号量释放。一般用于当共享资源使用完成后。
- return: if true:0,if false:-1.
-
int sem_getvalue(sem_t *sem, int *sval)
- 获取信号量的值。如果sem被锁定,sem_getvalue()返回的值将为零或负数,其绝对值表示等待该信号量的进程数量
- return: if true:0,if false:-1.
上面的函数中sem_init()
,sem_destroy()
用于未命名信号量的创建与销毁。sem_open()
,sem_close()
,sem_unlink()
用于命名信号量的创建与删除。其他函数用于操作信号量(共用)。
sem_close()函数与sem_unlink()函数的区别:sem_close()函数是关闭一个打开的信号量。关闭后的命名信号量仍然存在。别的进程依然可以访问这个信号量。而sem_unlink()是删除命名信号量,删除后的信号量就不存在了。
使用了POSIX信号量函数,链接时要用到-lpthread
库.
使用例程如下:
#include <sys/types.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <error.h>
#include <stdlib.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <errno.h>
void process_read(void)
{
sem_t *sem_handled = sem_open("/sem",O_CREAT,0666,1);
if(sem_handled == SEM_FAILED){
printf("(%d)sem_open error\n",getpid());
exit(0);
}
for(int i = 0; i < 10; i++){
while(1){
int res = sem_wait(sem_handled);
if(res == 0){
break;
}
if(res == EINTR){
continue;
}
else {
printf("(%d)sem_wait error\n",getpid());
exit(0);
}
}
printf("(%d)正在持有资源=================================\n",getpid());
int res = sem_post(sem_handled);
if(res == -1){
printf("(%d)sem_post error\n",getpid());
}
usleep(2000);
}
}
void process_write(void)
{
sem_t *sem_handled = sem_open("/sem",O_CREAT,0666,1);
if(sem_handled == SEM_FAILED){
printf("(%d)sem_open error\n",getpid());
exit(0);
}
for(int i = 0; i < 10; i++){
while(1){
int res = sem_wait(sem_handled);
if(res == 0){
break;
}
if(res == EINTR){
continue;
}
else {
printf("(%d)sem_wait error\n",getpid());
exit(0);
}
}
printf("(%d)正在持有资源..........................\n",getpid());
int res = sem_post(sem_handled);
if(res == -1){
printf("(%d)sem_post error\n",getpid());
}
usleep(1500);
}
int res = sem_unlink("/sem");
if(res == -1){
printf("(%d)sem_unlink error\n",getpid());
}
}
void main(void)
{
pid_t pid = fork();
if(pid == 0){//子进程
printf("child process runing(%d)\n",getpid());
process_read();
}
else{//父进程
printf("parent process runing(%d)\n",getpid());
process_write();
}
}
关于技术交流
此处后的文字已经和题目内容无关,可以不看。
qq群:825695030
微信公众号:嵌入式的日常
如果上面的文章对你有用,欢迎打赏、点赞、评论。