1.管道pipe
#include <unistd.h>
int pipe(int fd[2]);
管道的创建, fd[0]:从管道读;fd[1]:向管道写
管道是单个进程创建的,它却很少在单个进程内使用。管道的典型用途是:两个进程(一个父进程,一个子进程)提高进程间通信的手段。
步骤:
首先,父进程创建一个管道,fork一个自进程(子进程中也有该管道,管道相当与文件,子进程可以共享父进程的?);
然后,父进程关闭管道的读出端,子进程关闭同一个管道的写入端。
这样父子进程间变形成了一个单向的数据流,父进程写入,子进程读出。
2.popen和pclose函数
管道的通常用法是:创建一个管道连接到一个进程,然后读其输出或者向其中发送数据,为此,标准I/O库提供了两个函数popen和pclose函数。
#include <stdio.h>
FILE *popen(const char *cmdstring, const char *type);
int pclose(FILE *fp);
popen会先fork的一个子进程,然后exec 执行cmdstring, 如果type="r",则从子进程中读取,如果type=“w”,则通过管道向子进程中写
3.FIFO
管道没有名字,它只能用于有一个共同祖先进程的各个进程之间。两个无亲缘关系的进程间是不能通过管道进行IPC的。
在unix中,FIFO类似于管道,它是一个单向数据流,不同与管道的是,每个FIFO有一个路径名与之关联,从而无亲缘关系的进程访问同一个FIFO,可以进行IPC,
因此FIFO也称为有名管道。
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);创建一个新的FIFO,可以用open或者fopen打开pathname,要么为read,要么为write.
pathname是一个普通的unix路径名,它是该FIFO的名字。
管道或者FIFO的write总是往末尾追加数据,read则总是从开头返回数据。如果对管道或FIFO调用lseek,将会错误。
FIFO的使用
#define FIFO1 "/tmp/fifo.1"
#define FIFO2 "/tmp/fifo.2"
int main() {
int readfd, writefd;
pid_t childpid;
if(mkfifo(FIFO1, FILE_MODE) < 0) && (errno != EEXIST))
err_sys("can't create");
if(mkfifo(FIFO2, FILE_MODE) < 0) && (errno != EEXIST))
err_sys("can't create");
if((childpid = Fork()) == 0) {
readfd = open(FIFO1, O_RDONLY, 0);
writefd = open(FIFO2, O_WRONLY, 0);
server();...
exit(0);
}
writefd = open(FIFO1, O_WRONLY, 0);
readfd = open(FIFO2, O_RDONLY, 0);
...
}
通过mkfifo创建FIFO的文件,然后打开文件的一些管段,关闭文件的一些端口,便可以进行数据的流通
总结:
管道和FIFO是许多应用程序的基本构建模块。管道普遍用于shell中,不过也可以从程序中使用,往往是用于父子进程之间回传信息。使用管道涉及(pipe, fork, close, exec, waitpid)可通过使用popen和pclose来避免,由它们处理具体细节并激活一个shell
FIFO与管道类似,但它们是用mkfifo创建的,之后需用open打开。
管道和FIFO的特征之一是,它们的数据是字节流,类似与TCP链接。
4.消息队列(posix消息队列和system V消息队列)
消息队列可认为是一个消息链表。每个消息都是一个记录,在某个进程往一个队列写入消息之前,并不需要另外某个进程在该队列上等待消息的到达。对管道和FIFO,除非读者已存在,否则先有写入者是没有意义的
队列中的每个消息有如下属性:
1.一个无符号整数优先级(posix)或者一个长整型类型(System V)
2.消息的数据部分长度
3.数据本身
1.创建一个新的消息队列或打开一个已存在的消息队列
#include <mqueue.h>
mqd_t mq_open(const char *name, int oflag, ...);
mqd_t mq_close(mqd_t mqdes);
mq_open()返回消息队列描述符
mq_close()其功能与关闭和打开文件的close()类似:调用进程可以不再使用该描述符,但其消息队列并不从系统中删除。
#incude <mqueue.h>
int mq_unlink(const char *name);
从系统中删除用作mq_open第一个参数的某个name,使用mq_unlink(name)
往一个队列中放置消息使用mq_send,从一个队列中读取出消息使用mq_receive