POSIX IPC
中的xxx_open()
中的一些意义相同参数的取值: xxx可以是mq
(消息队列)、sem
(信号量)、shm
(共享内存)
参数oflag
:
O_RDONLY - 可读
O_WRONLY - 可写
O_RDWR - 可读可写
O_CREAT - 队列不存则创建,需要使用mode和attr参数
如果文件存在,mode和attr参数被忽略
O_EXCL - 如果使用O_CREAT,消息队列存在则创建失败
O_NONBLOCK - 非阻塞模式打开
参数mode
:
S_IRWXU —— 文件拥有者可读可写可执行
S_IRUSR —— 用户可读
S_IWUSR —— 用户可写
S_IXUSR —— 用户可执行
S_IRWXG —— 文件拥有者所在组成员可读可写可执行
S_IRGRP —— 组用户可读
S_IWGRP —— 组用户可写
S_IXGRP —— 组用户可写
S_IRWXO —— 其他用户拥有可读可写可执行权限
S_IROTH —— 其他用户可读
S_IWOTH —— 其他用户可写
S_IXOTH —— 其他用户可执行
消息队列也具备描述符,消息队列的描述符与文件的描述符类似
消息队列的描述符是一个进程级别的句柄
子进程会继承父进程打开的消息队列描述符
#include /* For O_* constants */
#include /* For mode constants */
#include
mqd_t mq_open(const char *name, int oflag);
mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr *attr);
name —— 消息队列名字(消息队列标识)
oflag —— 位掩码,mq_open()操作选项,与文件的open()的flag参数差不多:
O_RDONLY - 可读
O_WRONLY - 可写
O_RDWR - 可读可写
O_CREAT - 队列不存则创建,需要使用mode和attr参数
如果文件存在,mode和attr参数被忽略
O_EXCL - 如果使用O_CREAT,消息队列存在则创建失败
O_NONBLOCK - 非阻塞模式打开
mode —— 消息队列权限掩码,与文件的权限掩码一致,但执行权限无效
attr —— 消息队列的属性,用于指定消息队列的最大数量和最大大小
struct mq_attr {
long mq_flags; /* oflag */
long mq_maxmsg; /* 消息队列消息最大数量 */
long mq_msgsize; /* 每条消息的大小 */
long mq_curmsgs; /* 消息队列中当前存在的消息数量 */
};
RETURN VALUE
成功返回消息队列描述符,失败返回-1
#include
int mq_close(mqd_t mqdes);
mqdes —— 消息队列描述符
RETURN VALUE
成功返回0, 失败返回-1
删除name
标识的消息队列,标记其在所有使用它的进程使用完后销毁该消息队列
#include
int mq_unlink(const char *name);
name —— 消息队列名字
RETURN VALUE
成功返回0,失败返回-1
#include
int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
mqdes —— 消息队列描述符
attr —— 消息队列属性
RETURN VALUE
成功返回0,失败返回-1
#include
int mq_setattr(mqd_t mqdes, const struct mq_attr *newattr, struct mq_attr *oldattr);
mqdes —— 消息队列描述符
newattr —— 用来设置的消息队列属性
oldattr —— 获取消息队列的旧属性
RETURN VALUE
成功返回0,失败返回-1
#include
#include
int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned int msg_prio);
//发送消息数量达到attr字段mq_maxmsg进程将阻塞
int mq_timedsend(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned int msg_prio, const struct timespec *abs_timeout);
mqdes —— 消息队列描述符
msg_ptr —— 发送的消息
msg_len —— 消息的大小
msg_prio —— 消息的优先级,posix的消息队列可以设置优先级,
优先级高的消息永远放在队列靠前的位置
mq_timedsend()的行为与mq_send()类似,
但如果队列已满,并且没有为消息队列描述启用O_NONBLOCK标志,
则将abs_timeout指向指定调用将阻塞多长时间的结构
struct timespec {
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
RETURN VALUE
成功返回0, 失败返回-1
#include
#include
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr,
size_t msg_len, unsigned int *msg_prio);
ssize_t mq_timedreceive(mqd_t mqdes, char *msg_ptr,
size_t msg_len, unsigned int *msg_prio,
const struct timespec *abs_timeout);
mqdes —— 消息队列描述符
msg_ptr —— 发送的消息
msg_len —— 消息的大小,必须大于消息队列属性attr中mq_msgsize字段
msg_prio —— 消息的优先级
mq_timedreceive()的行为与mq_receive()类似,但如果队列是空的,
并且没有为消息队列描述启用O_NONBLOCK标志,
则将abs_timeout指向指定调用将阻塞多长时间的结构
RETURN VALUE
成功返回接收到的数据大小,失败返回-1
/**********************************************************************
* @file: xxx.c
* @author: guangjieMVP
* @version: v1.00.00
* @date: 2020-xx-xx
* @github: https://github.com/guangjieMVP
* @brief:
************************************************************************/
#include /* For O_* constants */
#include /* For mode constants */
#include
#include
#include
#include
#include
#define handler_err(err) \
do{ perror(err); exit(0); } while(0)
char father_buf[] = "I am your father";
int main(int argc, char **argv)
{
struct mq_attr m_attr;
m_attr.mq_maxmsg = 10;
m_attr.mq_msgsize = 40;
mqd_t mq_fd = mq_open("/mq", O_RDWR | O_CREAT, S_IRWXU | S_IRWXG, &m_attr);
if (mq_fd < 0)
{
handler_err("mq_open");
}
struct mq_attr get_attr;
int ret = mq_getattr(mq_fd, &get_attr);
if (ret < 0)
{
mq_close(mq_fd);
handler_err("mq_getattr");
}
printf("maxmsg = %ld, msgsize = %ld\r\n", get_attr.mq_maxmsg, get_attr.mq_msgsize);
pid_t pid = fork();
if (pid == 0)
{
unsigned int msg_prio = 0;
char msg_ptr[100];
while(1)
{
if (mq_receive(mq_fd, msg_ptr, sizeof(msg_ptr), &msg_prio) < 0)//msg_ptr的大小必须大于或等于m_attr.mq_msgsize
{
handler_err("mq_receive");
}
printf("%s\r\n", msg_ptr);
}
}
else
{
while (1)
{
if ( mq_send(mq_fd, father_buf, sizeof(father_buf), 1) < 0)
{
handler_err("mq_send");
}
printf("father is running\r\n");
sleep(1);
}
}
mq_close(mq_fd);
return 0;
}
进程或线程间同步、互斥访问共享资源
信号量属于系统范畴,进程退出也不会自动销毁,需要手动删除释放资源
使用信号量的程序编译时需要链接pthread
库:在编译时加上下面编译选项
-pthread
有名信号量
拥有名字,不同进程或线程之间可以通过名字访问同一信号量
无名信号量
特点:
轻量化、基于内存(不存在于任何文件系统中)
多用于进程内部线程间的同步互斥
没有名字,存放在内存预先指定位置
可以在进程或线程之间共享:
进程之间共享时,必须位于进程共享内存区域中
线程之间共享,信号量存放于线程共享内存区域中
#include /* For O_* constants */
#include /* For mode constants */
#include
sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);
name —— 信号量名字
oflag —— 信号量打开选项掩码
mode —— 信号量的权限
value —— 信号量的初始化值
RETURN VALUE
成功返回信号量的句柄,失败返回SEM_FAILED
#include
int sem_close(sem_t *sem);
sem —— 信号量句柄
RETURN VALUE
成功返回0,失败返回-1
#include
int sem_unlink(const char *name);
name —— 信号量名字
RETURN VALUE
成功返回0; 失败返回-1
#include
int sem_post(sem_t *sem);
sem —— 信号量句柄
RETURN VALUE
成功返回0,失败返回 -1,信号量的值不变
#include
int sem_wait(sem_t *sem);
sem —— 信号量句柄
RETURN VALUE
成功返回0,失败返回 -1,信号量的值不变
#include
int sem_getvalue(sem_t *sem, int *sval);
sem —— 信号量句柄
sval —— 存放获取的信号量值
RETURN VALUE
成功返回0,失败返回 -1,
#include /* For O_* constants */
#include /* For mode constants */
#include
#include
#include
#include
#include
#define handler_err(err) \
do{ perror(err);} while(0)
#define SEM_NAME "/name_sem" //信号量的名字
int main(int argc ,char **argv)
{
sem_t *name_sem = sem_open(SEM_NAME, O_RDWR | O_CREAT, S_IRWXU | S_IRWXG, 0);
if (name_sem == SEM_FAILED)
{
handler_err("sem_open");
exit(0);
}
pid_t pid = fork();
if (pid == 0)
{
int ret;
while (1)
{
ret = sem_wait(name_sem);
if (ret == 0)
{
printf("child is running\r\n");
}
}
}
else
{
char buf[100];
char *ret;
while (1)
{
ret = fgets(buf, sizeof(buf), stdin);
if (ret != NULL)
{
printf("father : %s\r\n", buf);
}
if (strstr(buf, "post") != NULL)
{
sem_post(name_sem); //释放信号量
}
}
}
sem_close(name_sem); //关闭信号量
sem_unlink(SEM_NAME); //删除信号量
return 0;
}
一般多用于进程的线程间通信
#include
int sem_init(sem_t *sem, int pshared, unsigned int value);
sem —— 信号量句柄
pshared —— 无名信号量共享方式:
pshared = 0,同一个进程的线程之间共享
pshared != 0, 不同进程之间之间共享,无名信号量必须存在
共享内存区域(POSIX共享内存、system V共享内存、内存映射)
value —— 信号量的初始化值
RETURN VALUE
成功返回0; 失败返回-1。
不存在进程或者线程等待时才能够安全销毁信号量
#include
int sem_destroy(sem_t *sem);
sem —— 信号量句柄
RETURN VALUE
成功返回0; 失败返回-1。
#include /* For O_* constants */
#include /* For mode constants */
#include
#include
#include
#include
#include
#include
#define handler_err(err) \
do{ perror(err); } while(0)
int main(int argc ,char **argv)
{
sem_t *p_sem = (sem_t *)mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); //MAP_ANONYMOUS 匿名映射
if (p_sem != MAP_FAILED){
printf("sem addr : %p\r\n", p_sem);
}else{
handler_err("mmap");
exit(0);
}
if (sem_init(p_sem, 2, 0) < 0) { //初始化无名信号量
handler_err("sem_open");
exit(0);
}
pid_t pid = fork();
if (pid == 0){
int ret;
int sem_val;
int cnt = 0;
while (1){
ret = sem_wait(p_sem);
if (ret == 0){
printf("child is running\r\n");
}else{
perror("sem_wait");
}
sem_getvalue(p_sem, &sem_val);
printf("sem val : %d\r\n", sem_val);
printf("cnt : %d\r\n", cnt++);
}
}else{
char buf[100];
char *ret;
int sem_val;
while (1){
ret = fgets(buf, sizeof(buf), stdin);
if (ret != NULL){
// printf("father : %s\r\n", buf);
}
if (strstr(buf, "post") != NULL){
if (sem_post(p_sem) < 0){ //释放信号量
perror("sem_post");
}
sem_getvalue(p_sem, &sem_val);
printf("sem val : %d\r\n", sem_val);
}
}
}
sem_close(p_sem);
sem_destroy(p_sem);
return 0;
}
共享内存的使用一般要加上互斥,防止多进程写入数据造成数据践踏
使用步骤:
1、shm_open()
打开一个共享内存对象
2、ftruncate()
设置共享内存对象大小
3、mmap()
使用flags
指定MAP_SHARED
进行内存映射
shm_open()
、mmap()
分别类似system V
共享内存的shmget()
、shmat()
操作
#include
#include
#include
int shm_open(const char *name, int oflag, mode_t mode);
name —— 共享内存对象名字
oflag —— 打开方式选项
mode —— shm权限
RETURN VALUE
成功返回非负的文件描述符,失败返回-1
新创建的共享内存对象长度会被设置为0
,需调用**ftruncate()**函数设置长度,新增加的字节的值被初始化为0
#include
#include
int ftruncate(int fd, off_t length);
fd —— 描述符(文件描述符、共享内存描述符等)
length —— 设置的长度
RETURN VALUE
成功返回0,失败返回-1
#include
#include
#include
int shm_unlink(const char *name);
name —— 共享内存对象名字
RETURN VALUE
成功返回0,失败返回-1
/**********************************************************************
* @file: xxx.c
* @author: guangjieMVP
* @version: v1.00.00
* @date: 2020-xx-xx
* @github: https://github.com/guangjieMVP
* @brief:
************************************************************************/
#include
#include /* For mode constants */
#include /* For O_* constants */
#include
#include
#include
#include
#include
#include
#define handler_err(err) \
do{ perror(#err); } while(0)
#define SHM_NAME "/shm_test"
#define SEM_NAME "/name_sem"
int main(int argc ,char **argv)
{
int shm_fd = shm_open(SHM_NAME, O_RDWR | O_CREAT, S_IRWXU | S_IRWXG); //打开共享内存
if (shm_fd < 0){
handler_err(shm_open);
exit(0);
}
if (ftruncate(shm_fd, 1024) < 0){ //设置共享内存大小
handler_err(ftruncate);
exit(0);
}
struct stat shmstat;
int ret = fstat(shm_fd, &shmstat); //获取共享内存信息
if (ret < 0){
handler_err(fstat);
exit(0);
}
printf("shm size : %ld\r\n", shmstat.st_size);
//内存映射
char *p = mmap(NULL, shmstat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (p == MAP_FAILED){
handler_err(mmap);
exit(0);
}
sem_t *name_sem = sem_open(SEM_NAME, O_RDWR | O_CREAT, S_IRWXU | S_IRWXG, 0);//打开信号量
if (name_sem == SEM_FAILED){
handler_err(sem_open);
exit(0);
}
pid_t pid = fork();
if (pid == 0){
while(1){
ret = sem_wait(name_sem);
if (ret == 0) {
printf("child is running\r\n");
}
printf("%s\r\n", p);
}
}else{
int cnt = 0;
// char buf[40];
while(1){
sprintf(p, "Hello, Linux %d\r\n", cnt++);
sem_post(name_sem); //释放信号量
sleep(1);
}
}
if ( shm_unlink(SHM_NAME) < 0){
handler_err(shm_unlink);
}
return 0;
}