共享内存
特点
1)共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝。
2)为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程
将其映射到自己的私有地址空间。进程就可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高的效率。
3) 由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等
使用步骤
(1) 创建key值: ftok
(2) 创建或打开共享内存: shmget
(3) 映射共享内存到用户空间: shmat
(4) 撤销映射: shmdt
(5) 删除共享内存: shmctl
进程间通信:
命令
ipcs -m: 查看系统中的共享内存
ipcrm -m shmid:删除共享内存
ps: 可能不能直接删除掉还存在进程使用的共享内存,这时候可以用ps -ef对进程进行查看,kill掉多余的进程后,再使用ipcs查看。
练习:两个进程实现通信,一个进程循环从终端输入,另一个进程循环打印,当输入quit时结束
这两个标志在两个进程里,是不共享的,所以为了共享标志位可以和buf封装到一个结构体里作为共享内存。
struct msg
{
int flag;
char buf[32];
};
头文件
typedef struct shared
{
int flag;
char buf[32];
} shared_t, *shared_p;
输入端
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>
#include "shared.h"
int main(int argc, char const *argv[])
{
key_t key;
key = ftok("bus.c", 'a');
if (key < 0)
{
perror("key err");
return -1;
}
printf("key:%#x\n", key);
int shmid = shmget(key, sizeof(shared_t), IPC_CREAT | IPC_EXCL | 0777);
if (shmid <= 0)
{
if (errno == EEXIST)
{
shmid = shmget(key, sizeof(shared_t), 0777);
}
else
{
perror("shmget err");
return -1;
}
}
printf("shmid:%d\n", shmid);
shared_p p = (shared_p)shmat(shmid, NULL, 0);
if (p == (shared_p)-1)
{
perror("shmat err");
return -1;
}
p->flag = 0;
while (1)
{
if (p->flag == 0)
{
scanf("%s", p->buf);
if (strcmp(p->buf, "quit") == 0)
break;
p->flag++;
}
}
shmdt(p);
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
输出端
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include "shared.h"
int main(int argc, char const *argv[])
{
key_t key;
key = ftok("bus.c", 'a');
if (key < 0)
{
perror("key err");
return -1;
}
printf("key:%#x\n", key);
int shmid = shmget(key, sizeof(shared_t), IPC_CREAT | IPC_EXCL | 0777);
if (shmid <= 0)
{
if (errno == EEXIST)
{
shmid = shmget(key, sizeof(shared_t), 0777);
}
else
{
perror("shmget err");
return -1;
}
}
printf("shmid:%d\n", shmid);
shared_p p = (shared_p)shmat(shmid, NULL, 0);
if (p == (shared_p)-1)
{
perror("shmat err");
return -1;
}
while (1)
{
sleep(1);
if (strcmp(p->buf, "quit") == 0)
exit(0);
if (p->flag == 1)
{
printf("%s\n", p->buf);
p->flag--;
}
}
shmdt(p);
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
信号灯集
特点
信号灯(semaphore),也叫信号量,信号灯集是一个信号灯的集合。它是不同进程间或一个给定进程内部不同线程间同步的机制;
而Posix信号灯指的是单个计数信号灯:无名信号灯、有名信号灯。(咱们学的是无名信号灯)
System V的信号灯是一个或者多个信号灯的一个集合。其中的每一个都是单独的计数信号灯。
通过信号灯集实现共享内存的同步操作
使用步骤
(1) 创建键值:ftok
(2) 创建或打开信号灯集:semget
(3) 初始化信号灯:semctl
(4) pv操作:semop
(5) 删除信号灯集:semctl
命令
ipcs -s:查看信号灯集
ipcrm -s semid:删除信号灯集
注意:有时候可能会创建失败,或者semid为0,所以用命令看看,删了重新创建就可以了。
练习:两个进程实现通信,一个进程循环从终端输入,另一个进程循环打印,把信号灯集加到共享内存实现同步,输入输出quit结束
头文件
union semun
{
int val;
};
key_t keyfun(char buf[32], char ch);
int shmcreate(key_t key);
int semcreate(key_t key, int num);
void seminit(int semid, int val, int num);
void sempv(int semid, int num, int op);
功能函数
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>
#include "sem.h"
key_t keyfun(char buf[32], char ch)
{
key_t key;
key = ftok(buf, ch);
if (key < 0)
{
perror("ftok err");
return -1;
}
printf("key:%#x\n", key);
return key;
}
int shmcreate(key_t key)
{
int shmid = shmget(key, sizeof(str), IPC_CREAT | IPC_EXCL | 0777);
if (shmid <= 0)
{
if (errno == EEXIST)
shmid = shmget(key, sizeof(str), 0777);
else
{
perror("shmget err");
return -1;
}
}
printf("shmid: %d\n", shmid);
return shmid;
}
int semcreate(key_t key, int num)
{
int semid = semget(key, num, IPC_CREAT | IPC_EXCL | 0777);
if (semid <= 0)
{
if (errno == EEXIST)
{
semid = semget(key, num, 0777);
}
else
{
perror("semget err");
return -1;
}
}
else
{
seminit(semid, 0, 0);
seminit(semid, 1, 1);
}
printf("semid:%d\n", semid);
return semid;
}
void seminit(int semid, int num, int val)
{
union semun mysem;
mysem.val = val;
semctl(semid, num, SETVAL, mysem);
}
void sempv(int semid, int num, int op)
{
struct sembuf buf; // sembuf结构体人家写好的直接拿来用就可以
// PV操作
buf.sem_num = num;
buf.sem_op = op;
buf.sem_flg = 0; // 阻塞
semop(semid, &buf, 1); // 对num号灯进行op操作申请资源
}
输入端
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>
#include "sem.h"
int main(int argc, char const *argv[])
{
key_t key = keyfun("sem.h", 'a');
int semid = semcreate(key, 2);
int shmid = shmcreate(key);
// 映射共享内存
char *p = (char *)shmat(shmid, NULL, 0);
if (p == (char *)-1)
{
perror("shmat err");
return -1;
}
while (1)
{
sempv(semid, 1, -1);
scanf("%s", p);
sempv(semid, 0, 1);
if (strcmp(p, "quit") == 0)
break;
}
printf("0:%d\n", semctl(semid, 0, GETVAL));
printf("1:%d\n", semctl(semid, 1, GETVAL));
shmdt(p);
// semctl(semid, 0, IPC_RMID);
return 0;
}
输出端
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <errno.h>
#include <string.h>
#include "sem.h"
int main(int argc, char const *argv[])
{
key_t key = keyfun("sem.h", 'a');
int semid = semcreate(key, 2);
int shmid = shmcreate(key);
// 映射共享内存
char *p = (char *)shmat(shmid, NULL, 0);
if (p == (char *)-1)
{
perror("shmat err");
return -1;
}
while (1)
{
sempv(semid, 0, -1);
if (strcmp(p, "quit") == 0)
break;
printf("%s\n", p);
sempv(semid, 1, 1);
}
printf("0:%d\n", semctl(semid, 0, GETVAL));
printf("1:%d\n", semctl(semid, 1, GETVAL));
shmdt(p);
// semctl(semid, 0, IPC_RMID);
return 0;
}
消息队列
特点
消息队列是IPC对象(活动在内核级别的一种进程间通信的工具)的一种
一个消息队列由一个标识符 (即队列ID)来标识
消息队列就是一个消息的列表。用户可以在消息队列中添加消息、读取消息等
消息队列可以按照类型(自己设一个值作为类型)来发送/接收消息
使用步骤
(1) 创建key值:ftok
(2) 创建或打开消息队列:msgget
(3) 添加消息:按照消息的类型把消息添加到已经打开的消息队列末尾msgsnd
(4) 读取消息:可以按照消息类型把消息从消息队列中取走msgrcv
(5) 删除消息队列:msgctl
命令
ipcs -q:查看消息队列
ipcrm -q msgid:删除消息队列