网上有的分七种,有的五种,其实都是一样的.
其实大多数就是这五种,分细一点就可以认为是七种。
有的还有命令流管道s_pipe,和内存映射,这两种可能用的比较少,也很少看到,所以就没有写。
无名管道:管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程)。
函数原型:
SYNOPSIS
#include
int pipe(int pipefd[2]);
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include /* Obtain O_* constant definitions */
#include
int pipe2(int pipefd[2], int flags);
#include
#include
int main()
{
int fd[2];
pipe(fd);
char buf[128];
write(fd[1], "Hello world", 12);
sleep(2);
read(fd[0], buf, 128);
printf("read = %s\n",buf);
close(fd[0]);
close(fd[1]);
return 0;
}
$ gcc noNamePipe.c
$ ./a.out noNamePipe.c
read = Hello world
#include
#include
#include
#include
int main()
{
int fd[2];
pipe(fd);
char buf[128];
if(fork() != 0)
{
write(fd[1],"Hello world\n",12);
printf("father pid input\n");
}
else
{
read(fd[0],buf,128);
printf("child pid read = %s\n",buf);
}
close(fd[0]);
close(fd[1]);
return 0;
}
$ gcc noNamePipe_1.c
$ ./a.out noNamePipe_1.c
father pid input
child pid read = Hello world
因为无名管道只有亲缘关系才能通信,所以创建了有名管道来解决这个问题
实现一个有名管道实际上就是实现一个FIFO文件,有名管道一旦建立,之后它的读,以及关闭操作都与普通管道完全相同。虽然FIFO文件的inode节点在磁盘上,但仅是一个节点而已,文件的数据还是存在内核缓冲页面上,和普通管道相同。有名管道的文件仅仅是作为传输数据的通道,它并不存放传输的数据。
先写读端
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MYFIFO "myfifo"
int main()
{
int fd;
char r_buf[128];
/*判断管道是否存在*/
if(mkfifo(MYFIFO,0666) < 0) {
printf("error mkfifo\n");
return -1;
}
printf("make myfifo_r success\n");
/*只读权限打开*/
fd = open(MYFIFO, O_RDONLY);
if (fd == -1) {
printf("open myfif0 error\n");
return -2;
}
while(1) {
/*memset初始化这个数组*/
memset(r_buf,0,sizeof(r_buf));
if(read(fd, r_buf, sizeof(r_buf))) {
printf("buf read success\nr_buf: %s\n",r_buf);
}
}
close(fd);
return 0;
}
$ gcc namePipe_read.c -o read
$ ./read
make myfifo_r success
buf read success
r_buf: 456789
buf read success
r_buf: lalala
再写写端
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MYFIFO "myfifo"
int main(int argc, char *argv[])
{
int fd;
char w_buf[128]={0};
if(argc < 1) {
printf("error argc");
return -1;
}
fd = open(MYFIFO, O_WRONLY);
if (fd == -1) {
printf("open myfifo error\n");
return -2;
}
printf("Write:>");
sscanf(argv[1],"%s",w_buf);
if(write(fd, w_buf, sizeof(w_buf))) {
if(strcmp(buf, "end") == 0){
close(fd);
exit -1;
}
printf("w_buf write success\nw_buf: %s\n",w_buf);
}
close(fd);
return 0;
}
$ ./write 456789
Write:>w_buf write success
w_buf: 456789
$ ./write lalala
Write:>w_buf write success
w_buf: lalala
消息队列是随着内核的存在而存在的,每个消息队列在系统范围内对应唯一的键值,这个键的数据类型为key_t,通常在头文件
消息队列的优势在于,1、消息队列也可以独立于发送和接收进程而存在,从而消除了在同步命名管道的打开和关闭时可能产生的困难。2、同时通过发送消息还可以避免命名管道的同步和阻塞问题,不需要由进程自己来提供同步方法。3、接收程序可以通过消息类型有选择地接收数据
消息队列主要是用于:异步处理,应用解耦,流量削锋和消息通讯这四个模块
异步处理:
当我们注册app时,手机发送一条短信,很快会返回一条验证码,提高处理效率。
应用解耦:
当我们用淘宝下单的时候,可能商家已经休息不可能会及时处理,就会把我们下的订单的信息存储在消息队列里面,然后商家工作就会刷新拉取信息,此时商家就会看到对应的订单消息。
流量削锋:
其实消息队列我们也经常遇到,比如说在手机首发的时候,会提示只有一千个名额,就需要我们去抢,服务器不可能一下子处理那么多事情,所以就按照前后顺序存放到这个消息队列里面,当存满了过后,剩下的人继续去抢,就会返回错误页面,或则其余的页面。
消息通讯:
就类似我们的qq和微信,发送了消息对方可以选择看与不看,更是在的就是企业微信和钉钉会提示对方看了没有。
这些都是上层很明显的事列,我们也可以直观的看到。
函数原型:
1 #include <sys/msg.h>
2 // 创建或打开消息队列:成功返回队列ID,失败返回-1
3 int msgget(key_t key, int flag);
4 // 添加消息:成功返回0,失败返回-1
5 int msgsnd(int msqid, const void *ptr, size_t size, int flag);
6 // 读取消息:成功返回消息数据的长度,失败返回-1
7 int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);
8 // 控制消息队列:成功返回0,失败返回-1
9 int msgctl(int msqid, int cmd, struct msqid_ds *buf);
代码实现:
read.c
#include
#include
#include
#include
#include
struct msgbuf{
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
};
int main()
{
struct msgbuf sendbuf={999,"888 message already received"};
struct msgbuf readbuf;
int msgid = 0;
key_t key;
key = ftok(".",'z');//获取键值
printf("key=%x\n",key);
msgid=msgget(key,IPC_CREAT|0777);//在内核中打开或建立键值为key的,权限为0777的消息队列
if(msgid == -1){
printf("create msgq failure\n");
}
msgrcv(msgid,&readbuf,sizeof(readbuf.mtext),888,0);//从队列中获取888类型的数据,如果队列中未出现888类型的数据,则程序>阻塞在这里
printf("read from que:%s\n",readbuf.mtext);
msgsnd(msgid,&sendbuf,strlen(sendbuf.mtext),0);//往队列id为msgid的队列写入sendbuf(类型为999)数据
msgctl(msgid,IPC_RMID,NULL);//将队列从系统内核中删除
return 0;
}
#include
#include
#include
#include
#include
struct msgbuf{
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
};
int main()
{
struct msgbuf sendbuf={888,"this is message from que"};
struct msgbuf readbuf;
int msgid= 0;
key_t key;
key = ftok(".",'z');//获取键值
printf("key=%x\n",key);
msgid=msgget(key,IPC_CREAT|0777);//在内核中打开或建立键值为key的,权限为0777的消息队列
if(msgid == -1){
printf("create msgq failure\n");
}
msgsnd(msgid,&sendbuf,strlen(sendbuf.mtext),0);//往队列id为msgid的队列写入sendbuf(类型为888)数据
msgrcv(msgid,&readbuf,sizeof(readbuf.mtext),999,0);//从队列中获取999类型的数据,如果队列中未出现999类型的数据,则程序>阻塞在这里
printf("%s\n",readbuf.mtext);
msgctl(msgid,IPC_RMID,NULL);//将队列从系统内核中删除
return 0;
}