无名管道是Linux中管道通信的一种原始方法;优点类似硬件中的串口,不过其具有一定的局限性:
- 无名管道属于半双工的通信方式
- 只有具有“亲缘关系”的进程才能使用这种通信方式,也就是父进程和子进程之间
- 管道可以看成是一种特殊的文件,对于它的读写可以使用普通的read(),write()等文件IO操作函数接口,但是它不属于任何文件系统,并且只存在于内存中
- 通常使用时,首先创建一个管道,然后调用fork函数创建一个子进程,该子进程会继承父进程所创建的管道
- 只有在管道的读端存在时,向管道写入数据才有意义,否则向管道写入的数据的进程将收到内核发送过来的SIGPEPE信号
- 向管道写入数据时,linux不保证写入的原子性,管道缓冲区只要有空间,写进程就会试图向管道写入数据,如果管道缓冲区已满,那么写操作将会一直阻塞
无名管道通信首先需要使用pipe函数创建管道之后,两个进程之间才能通信
#include
#include
#include
#include
//进程读函数
void read_data(int *);
//进程写函数
void write_data(int *);
int main(int argc,char *argv[])
{
int pipes[2],rc;
pid_t pid;
rc = pipe(pipes); //创建管道
if(rc == -1){
perror("\npipes\n");
exit(1); //异常退出
}
pid = fork(); //创建进程
switch(pid){
case -1:
perror("\nfork\n");
exit(1);
case 0:
read_data(pipes); //相同的pipes
default:
write_data(pipes); //相同的pipes
}
}
void read_data(int pipes[])
{
int c,rc;
//由于此函数只负责读,因此将写描述符关闭
close(pipes[1]);
//阻塞,等待从管道读取数据
while((rc = read(pipes[0],&c,1)) > 0){
putchar(c);
}
exit(0); //正常退出
}
//进程写函数
void write_data(int pipes[])
{
int c,rc;
//关闭读描述符
close(pipes[0]);
while((c = getchar()) > 0){
rc = write(pipes[1],&c,1); //写入管道
if(rc == -1){
perror("parent: write");
close(pipes[1]);
exit(1); //异常退出
}
}
close(pipes[1]);
exit(0);
}
有名管道fifo
无名管道只能用于有亲缘关系的进程之间的通信,有名管道可以实现无亲缘关系的通信
有名管道FIFO给文件系统提供一个路径,这个路径和管道关联,只要知道这个管道路径就可以进行文件访问,FIFO是指先进先出,也就是先写入的数据先读出来
creatc.c:
#include
#include
#include
#include
#include
#include
void filecopy(FILE *,char *);
int main(void)
{
FILE *fp1;
long int i = 100000;
char buf[] = "I want to study Linux!\n";
char *file1 = "data.txt";
printf("begin!\n");
//打开文件路径,如果文件不存在,立刻创建文件
if((fp1 = fopen(file1,"a+")) == NULL ){
printf("can't open %s\n",file1);
}
while(i--) //执行100000次复制buf内容到file1文件中
filecopy(fp1,buf);
fclose(fp1); //关闭文件
printf("over!\n");
return 0;
}
void filecopy(FILE *ifp,char *buf)
{
char c;
int i,j;
j = 0;
i = strlen(buf)-1;
while(i--){
putc(buf[j],ifp);
j++;
}
putc('\n',ifp);
}
writepipe.c:
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
const char *fifo_name = "my_fifo";
char *file1 = "data.txt";
int pipe_fd = -1;
int data_fd = -1;
int res = 0;
const int open_mode = O_WRONLY;
int bytes_sent = 0;
char buffer[PIPE_BUF + 1];
if(access(fifo_name, F_OK) == -1)
{
//管道文件不存在
//创建命名管道
res = mkfifo(fifo_name, 0777);
if(res != 0)
{
fprintf(stderr, "Could not create fifo %s\n", fifo_name);
exit(EXIT_FAILURE);
}
}
printf("Process %d opening FIFO O_WRONLY\n", getpid());
//以只写阻塞方式打开FIFO文件,以只读方式打开数据文件
pipe_fd = open(fifo_name, open_mode);
data_fd = open(file1, O_RDONLY);
printf("Process %d result %d\n", getpid(), pipe_fd);
if(pipe_fd != -1)
{
int bytes_read = 0;
//向数据文件读取数据
bytes_read = read(data_fd, buffer, PIPE_BUF);
buffer[bytes_read] = '\0';
while(bytes_read > 0)
{
//向FIFO文件写数据
res = write(pipe_fd, buffer, bytes_read);
if(res == -1)
{
fprintf(stderr, "Write error on pipe\n");
exit(EXIT_FAILURE);
}
//累加写的字节数,并继续读取数据
bytes_sent += res;
bytes_read = read(data_fd, buffer, PIPE_BUF);
buffer[bytes_read] = '\0';
}
close(pipe_fd);
close(data_fd);
}
else
exit(EXIT_FAILURE);
printf("Process %d finished\n", getpid());
exit(EXIT_SUCCESS);
}
readpipe.c:
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
const char *fifo_name = "my_fifo";
int pipe_fd = -1;
int data_fd = -1;
int res = 0;
int open_mode = O_RDONLY;
char buffer[PIPE_BUF + 1];
int bytes_read = 0;
int bytes_write = 0;
//清空缓冲数组
memset(buffer, '\0', sizeof(buffer));
printf("Process %d opening FIFO O_RDONLY\n", getpid());
//以只读阻塞方式打开管道文件,注意与writepipe.c文件中的FIFO同名
pipe_fd = open(fifo_name, open_mode);
//以只写方式创建保存数据的文件
data_fd = open("DataFormFIFO.txt", O_WRONLY|O_CREAT, 0644);
printf("Process %d result %d\n",getpid(), pipe_fd);
if(pipe_fd != -1)
{
do
{
//读取FIFO中的数据,并把它保存在文件DataFormFIFO.txt文件中
res = read(pipe_fd, buffer, PIPE_BUF);
bytes_write = write(data_fd, buffer, res);
bytes_read += res;
}while(res > 0);
close(pipe_fd);
close(data_fd);
}
else
exit(EXIT_FAILURE);
printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);
exit(EXIT_SUCCESS);
}
消息队列msg
消息队列就是一个消息的链表。可以把消息看作一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向其中按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读走消息
消息队列主要有两个函数msgrcv和msgsnd,一个接收一个发送,可通过man进行查看