以下三种类型的ipc合称为posix ipc:
posix ipc在访问它们的函数和描述它们的信息上有一些类似点,主要包括:
下表汇总了所有posix ipc函数。
信号量 | 消息队列 | 共享内存 | |
---|---|---|---|
头文件 | semaphore.h | mqueue.h | sys/mman.h |
创建、打开或删除ipc的函数 |
sem_open sem_close sem_unlink sem_init sem_destroy |
mq_open mq_close mq_unlink |
shm_open shm_unlink |
控制ipc操作的函数 |
|
mq_getattr mq_setattr |
ftruncate fstat |
ipc操作函数 |
sem_wait sem_trywait sem_post sem_getvalue |
mq_send mq_receive mq_notify |
mmap munmap |
除了posix无名信号量,其余三种类型的posix ipc都使用"posix ipc"名字进行标识,它可能是文件系统中真实存在的一个路径名,也可能不是。posix.1是这么描述的:
因此,为了便于代码移植,通常在实际项目中遵循下面两条规则:
以下是三种posix ipc的创建与打开函数:
sem_open
用于创建或打开一个posix有名信号量mq_open
用于创建或打开一个posix消息队列shm_open
用于创建或打开一个posix共享内存这三个函数的第二个参数都是oflag,作用是指定ipc的读写权限与创建标志,下表给出了可组合构成该参数的所有常值。
说 明 | sem_open | mq_open | shm_open |
---|---|---|---|
只读 只写 读写 |
o_rdonly o_wronly o_rdwr |
o_rdonly o_rdwr |
|
若不存在则创建 排他性创建 |
o_creat o_excl |
o_creat o_excl |
o_creat o_excl |
非阻塞模式 若已存在则截短 |
o_nonblock o_excl |
o_trunc |
前三行指定读写权限:只读、只写、读写,从表中可以看出:
后面四行指定创建标志:
o_creat
:若函数第一个参数指定的ipc不存在,则进行创建,此时至少需要第三个参数mode指定用户访问权限(详见后续)o_excl
:如果和o_creat一起指定,那么当ipc已存在且指定了o_creat | o_excl标志时,会出错返回eexisto_nonblock
:仅适用于posix消息队列,作用是队列为空时的读操作和队列为满时的写操作不会阻塞创建一个新的posix ipc时,需要使用第三个参数指定用户访问权限,它是由下表所示常值按位或构成的,常值的格式为s_irxxx和s_iwxxx,其中xxx代表访问用户。
常 值 | 说 明 |
---|---|
s_irusr s_iwusr |
用户读 用户写 |
s_irgrp s_iwgrp |
组成员读 组成员写 |
s_iroth s_iwoth |
其他用户读 其他用户写 |
ipc对象的持续性,指的是该类型的一个对象一直存在多长时间,ipc的持续性有三类:
在默认情况下,除了posix无名信号量是随进程持续,其余所有posix ipc和system v ipc都是随内核持续。
信号量是一种用于进程间同步或线程间同步的机制,共有三种类型的信号量ipc:
按信号量值的范围,可分为:
posix有名信号量由ipc路径名标识,因此它天生既可用于线程同步,又可用于进程同步,相关api在头文件<semaphore.h>
中,编译时需要指定链接-lrt
或-pthread
。
sem_open
用于创建一个新的信号量或打开一个已存在的信号量。
//成功返回信号量指针,失败返回sem_failed,链接时需指定 -lrt or -pthread sem_t *sem_open(const char *name, int oflag, ... /*mode_t mode, unsigned int value*/);
函数参数说明在概述中基本都有介绍,这里不再赘述,只强调两点:
在linux中,创建的posix有名信号量存放在/dev/shm/
目录下,可通过ls命令查看:
#include <semaphore.h> #include <fcntl.h> /* for o_* constants */ #include <sys/stat.h> /* for mode constants */ #include <stdio.h> #define posix_sem_name "sem_test" int main() { sem_t *sem = sem_open(posix_sem_name, o_creat, 0666, 1); if (sem != sem_failed) { printf("sem_open() success\n"); } return 0; }
//两个函数返回值:成功返回0,失败返回-1 int sem_close(sem_t *sem); int sem_unlink(const char *name);
sem_close
用于关闭已经打开的有名信号量sem_unlink
用于从系统中删除有名信号量进程终止时,会自动关闭所有已打开的ipc对象(包括有名信号量、消息队列和共享内存),但关闭不等于删除,因为它们都至少具有随内核的持续性,这一点从上面示例代码的执行结果也可以看出来——进程已终止,但/dev/shm/目录下刚刚创建的信号量依然存在。事实上,所有以路径名标识的posix ipc都有一个引用计数:
#include <semaphore.h> #include <fcntl.h> /* for o_* constants */ #include <sys/stat.h> /* for mode constants */ #include <stdio.h> #define posix_sem_name "sem_test" int main() { sem_t *sem = sem_open(posix_sem_name, o_creat, 0666, 1); if (sem != sem_failed) { printf("sem_open() success\n"); printf("before sem_unlink()\n"); system("ls /dev/shm/"); sem_close(sem); sem_unlink(posix_sem_name); printf("after sem_unlink()\n"); system("ls /dev/shm/"); } return 0; }
//两个函数返回值:成功返回0,失败返回-1 int sem_wait(sem_t *sem); int sem_post(sem_t *name);
sem_wait
用于等待有名信号量:
sem_post
用于挂出有名信号量,该函数把信号量的值加1,然后阻塞于sem_wait等待该信号量的线程就能够被唤醒。
#include <pthread.h> #include <semaphore.h> #include <fcntl.h> #include <sys/stat.h> #include <stdio.h> #include <stdlib.h> #define posix_sem_name "sem_test" pthread_t tid[2]; sem_t *sem; /*thread0先处理自己的工作,之后调用sem_post将信号量的值加1,通知thread1可以执行了*/ void *thread0(void *arg) { int value; while (1) { /* do work thread0 */ sem_post(sem); sem_getvalue(sem, &value); printf("thread 0: sem value is %d\n", value); sleep(2); } } /*thread1等待时间比thread0少,但也必须等待thread0调用sem_post将信号量的值加1,才能继续执行*/ void *thread1(void *arg) { int value; while (1) { sem_wait(sem); sem_getvalue(sem, &value); printf("thread 1: sem value is %d\n", value); sleep(1); } } int main() { sem = sem_open(posix_sem_name, o_creat, 0666, 0); pthread_create(&tid[0], null, thread0, null); pthread_create(&tid[1], null, thread1, null); sleep(10); pthread_cancel(tid[0]); pthread_join(tid[0], null); pthread_cancel(tid[1]); pthread_join(tid[1], null); sem_close(sem); sem_unlink(posix_sem_name); return 0; }
//成功返回0,失败返回-1 int sem_getvalue(sem_t *sem, int *sval);
sem_getvalue
用于获取信号量sem的当前值,该值通过参数sval返回。如果有线程或进程正阻塞于sem_wait,posix.1-2001允许通过sval返回两种结果:
posix无名信号量是基于内存的信号量,也就是说它没有ipc路径名,而是像普通变量一样创建在内存中。
sem_init
初始化,由sem_destroy
销毁//两个函数返回值:成功返回0,失败返回-1 int sem_init(sem_t *sem, int shared, unsigned int value); int sem_destroy(sem_t *sem);
sem_init的sem参数指向要初始化的信号量,shared参数用于指定信号量在线程间共享还是在进程间共享:
一般来说,线程间同步使用有名信号量和无名信号量都可以,而进程间同步直接使用有名信号量就可以了,除非对通讯速度有特殊需求,才考虑shared ≠ 0的无名信号量。
把第3章的示例代码改为使用shared = 0的无名信号量,只有main函数发生了变动,如下所示:
int main() { sem = (sem_t *)malloc(sizeof(sem_t)); //这里使用动态分配,也可以使用静态分配sem,然后给sem_init传&sem sem_init(sem, 0, 0); pthread_create(&tid[0], null, thread0, null); pthread_create(&tid[1], null, thread1, null); sleep(10); pthread_cancel(tid[0]); pthread_join(tid[0], null); pthread_cancel(tid[1]); pthread_join(tid[1], null); free(sem); sem_destroy(sem); return 0; }
posix定义了两个信号量限制:
sem_nsems_max
:一个进程可同时打开的最大信号量个数,该值至少为256sem_value_max
:信号量的最大值,该值至少为32767
如对本文有疑问, 点击进行留言回复!!
linux下文本编辑器vim的使用方法(复制、粘贴、替换、行号、撤销、多文件操作)
网友评论