进程间通信:IPC
IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等,其中Socket和Streams支持不同主机上的两个进程IPC。
一、管道
管道,通常指无名管道,是UNIX系统IPC最古老的形式
管道特点:
(1)它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端
(2)它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)
(3)它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write等函数,但是它 不是普通的文件,并不属于其他任何文件系统,并且只存在内存中
(4)同一时间,数据只能是单向的(只能一个写一个读)
(5)管道中的数据,读走就没了
(1)无名管道
#include <unistd.h>
int pipe(int fd[2]) //返回值:若成功返回0,若失败返回-1
当一个管道建立时,它会创建两个文件描述符:fd[0]为读而打开,fd[1]为写而打开,
要关闭管道只需要将这两个文件描述符关闭即可
无名管道代码如下:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main(){
int fd[2];
int pid;
char *buf = "hello from father";
char readBuf[1024] = {0};
if(pipe(fd) == -1){ //创建一个管道
printf("creat pipe failed\n");
}
pid = fork(); //创建一个父子进程
if(pid < 0){
printf("creat pid failed\n");
}else if(pid > 0){
printf("this is father\n");
close(fd[0]); //关闭读端
write(fd[1],buf,strlen(buf));
wait(); //等待子进程的返回值
}else{
printf("this is child\n");
close(fd[1]); //关闭写端
read(fd[0],readBuf,strlen(buf));
printf("read from :%s\n",readBuf);
exit(0);
}
return 0;
}
执行结果:
(2)命名管道
FIFO,也称为命名管道,它是一种文件类型
特点:
(1)FIFO可以在无关的进程之间交换数据,与无名管道不同
(2)FIFO有路径名与之关联,它以一种特殊设备文件形式存在于文件系统中
命名管道原型
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);//返回值:成功返回0,出错返回-1
const char *pathname:是被创建的文件名称
mode_t mode: 表示将在该文件上设置的权限位和将被创建的文件类型
其中的 mode 参数与 open 函数中的 mode 相同。一旦创建了一个 FIFO,就可以用一般的文件 I/O 函数操作它。
当 open 一个 FIFO 时,是否设置非阻塞标志(O_NONBLOCK)的区别:
若没有指定 O_NONBLOCK(默认),只读 open 要阻塞到某个其他进程为写而打开此 FIFO。类似的,只写 open 要阻塞到某个其他进程为读而打开它。
若指定了 O_NONBLOCK,则只读 open 立即返回。而只写 open 将出错返回 -1 如果没有进程已经为读而打开该 FIFO,其 errno 置 ENXIO。
创建读文件的命名管道:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
// int mkfifo(const char *pathname, mode_t mode);
int main()
{
int nread;
char buf[30] = {'\0'};
if(mkfifo("myfifo",0600) == -1 && errno != EEXIST)//创建命名管道
{
printf("mkfifo failed\n");
perror("why");
}
int fd = open("./myfifo",O_RDONLY);//以只读的形式打开管道,程序阻塞在这,直到有另一个进程对其执行写操作
if(fd < 0)
{
printf("read open failed\n");
}else
{
printf("read open successn\n");
}
while(1)
{
nread = read(fd,buf,sizeof(buf));
printf("read %d byte,context is:%s\n",nread,buf);
}
close(fd);
return 0;
}
创建写文件的命名管道:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
// int mkfifo(const char *pathname, mode_t mode);
int main()
{
int nread;
char buf[30] = "message from myfifo";
if(mkfifo("myfifo",0600) == -1 && errno != EEXIST)//创建命名管道
{
printf("mkfifo failed\n");
perror("why");
}
int fd = open("./myfifo",O_WRONLY);//打开管道,程序阻塞在这,直到其他进程为读而打开它
if(fd < 0)
{
printf("write open failed\n");
}
else
{
printf("write open success\n");
}
while(1)
{
sleep(1);
write(fd,buf,strlen(buf));
}
close(fd);
return 0;
}
执行结果:
二、消息队列
消息队列提供了一种从一个进程向另外一个进程发送一个数据块的方法。 每一个数据块都被认为含有一个类型,接收进程能够独立地接收含有不一样类型的数据结构。咱们能够经过发送消息来避免命名管道的同步和阻塞问题。可是消息队列与命名管道同样,每一个数据块都有一个最大长度的限制。
消息队列,是消息的链接表,存放在内核之中。一个消息队列由一个标识符(即队列ID)来标识。
用户进程可以向消息队列添加消息,也可以向消息队列读取消息。
消息队列的特点:
1.消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级;
2.消息队列是独立于发送和接收进程的,进程终止时,消息队列及其内容并不会被删除;
3.消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。
创建消息队列:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
获得消息队列标识符或建立一个消息队列对象
int msgget(key_t key, int msgflg);
key_t key: 创建新的消息队列
int msgflg: 赋予新消息队列的权限,如:IPC_CREAT|0777
将消息写入到消息队列:
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
int msqid: 消息队列标识符
const void *msgp: msgp是一个任何类型的结构体,是消息队列的地址
size_t msgsz: 要发送消息的大小,不含消息类型占用的4个字节,即mtext的长度
int msgflg: 默认写0:当消息队列满时,msgsnd将会阻塞,直到消息能写进消息队列
const void *msgp的结构体如下:
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
};
将消息写入到消息队列代码:
msgSend.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
// int msgget(key_t key, int msgflg);
// int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
// ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
struct msgbuf{
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
};
int main()
{
struct msgbuf sendbuf={888,"message from send"};
struct msgbuf readbuf;
key_t key;
if((key = ftok(".",'z')) < 0){
printf("ftok error\n");
}
int msgId = msgget(key,IPC_CREAT|0777);
if(msgId == -1){
printf("get quen failed\n");
}
msgsnd(msgId,&sendbuf,strlen(sendbuf.mtext),0);
printf("send over\n");
msgrcv(msgId,&readbuf,sizeof(readbuf.mtext),999,0);
printf("read from get is:%s\n",readbuf.mtext);
msgctl(msgId,IPC_RMID,NULL);
return 0;
}
从消息队列读取消息 :
msgGet.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
// int msgget(key_t key, int msgflg);
// int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
// ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
struct msgbuf{
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
};
int main()
{
struct msgbuf readbuf;
memset(readbuf.mtext, '\0', sizeof(readbuf.mtext));
struct msgbuf sendbuf={999,"thank for your reach"};
key_t key;
//获取key值
if((key = ftok(".",'z')) < 0){
printf("ftok error\n");
}
int msgId = msgget(key,IPC_CREAT|0777);
if(msgId == -1){
printf("get quen failed\n");
perror("why");
}
msgrcv(msgId,&readbuf,sizeof(readbuf.mtext),888,0);
printf("read from send is:%s\n",readbuf.mtext);
msgsnd(msgId,&sendbuf,strlen(sendbuf.mtext),0);
msgctl(msgId,IPC_RMID,NULL);
return 0;
}
执行结果: