下面几节,将分别温习下linux进程进通信的几种机制
1:管道
管道是比较古老的进程间的通信方式。主要有有名管道和无名管道两种。
2:无名管道
它的特点就是:
1:只能使用在具有亲缘关系的进程之间的通信(父子进程或者兄弟进程之间)。因为只有具有亲缘关系的进程才能继承其创建的文件描述符。
2:是一个半双工的通信模式,具有固定的读端和写段。
3:可以将管道看着特殊的文件,也可以调用read()和write()操作。
管道是基于文件描述符的通信方式,当一个管道建立时,它会创建两个文件描述符fd[0]和fd[1],其中fd[0]是固定用于读管道, 而fd[1]是固定用于写管道,构成一个半双工通道。
当一个管道共享多对文件描述符时,若将其中的一对读写文件描述符都删除,则该管道将失效。
上面插图来自《linux应用程序开发标准教程》中,从图中可以看到当fork()后 , 子进程会继承父进程创建的管道,这样父子进程都拥有了自己的读写通道, 从而实现了父子之间的读写,只需要把无关的读端或写段的文件描述符关闭即可。
例如上图中,父进程负责读段,则把它的写段fd[1]关闭, 子进程负责写,则把它的读段fd[0]关掉。这样就建立起来一条“子进程写父进程读”的通道。
下面来看一下无名管道的实例,直接上代码:
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#define MAX_DATA_LEN 128
#define DELAY_TIME 1
int main()
{
pid_t pid;
int pipe_fd[2];
char buf[MAX_DATA_LEN] = {0};
const char data[] ="Pipe Test Program";
int real_read, real_write;
if( pipe(pipe_fd) < 0)
{
printf("Pipe create error\n");
exit(1);
}
if((pid = fork()) == 0)
{
/* clode write fd */
close(pipe_fd[1]);
sleep(DELAY_TIME *3);
if ((real_read = read(pipe_fd[0], buf, MAX_DATA_LEN)) > 0)
{
printf("Read data from pipe is %s\n", buf);
}
close(pipe_fd[0]);
exit(0);
} else if (pid > 0)
{
/*close read fd*/
close(pipe_fd[0]);
sleep(DELAY_TIME);
if ((real_write = write(pipe_fd[1], data, strlen(data))) != -1)
{
printf("Write to pipe is: %s\n", data);
}
{
/* clode write fd */
close(pipe_fd[1]);
sleep(DELAY_TIME *3);
if ((real_read = read(pipe_fd[0], buf, MAX_DATA_LEN)) > 0)
{
printf("Read data from pipe is %s\n", buf);
}
close(pipe_fd[0]);
exit(0);
} else if (pid > 0)
{
/*close read fd*/
close(pipe_fd[0]);
sleep(DELAY_TIME);
if ((real_write = write(pipe_fd[1], data, strlen(data))) != -1)
{
printf("Write to pipe is: %s\n", data);
}
close(pipe_fd[1]);
waitpid(pid, NULL, 0);
exit(0);
}
return 0;
}
运行结果:
Write to pipe is: Pipe Test Program
Read data from pipe is Pipe Test Program
3:有名管道
有名管道突破了无名管道的限制,可以使两个不相关的进程实现彼此之间的通信。相当于创建一个文件,建立管道之后,两个进程可以把它当做普通文件一样进行读写操作。严格遵循先进先出规则,对管道及FIFO的读写总是从开始出返回数据,对它们的写则把数据添加到末尾,需要指出的是不支持lseek()等文件定位操作。
可以采用mkdfifo()函数来创建一个命名管道,创建成功后就可以使用open, read, write函数进行操作。
mkfifo函数原型:
int mkfifo(const char *filename, mode_t mode)
传参:
filename为要创建的管道,包括路径名
mode只创建的管道权限,一般有O_RDONLY(读管道), O_WRONLY(写管道),O_RDWR(读写管道),O_NONBLOCK:非堵塞管道。 O_CREAT(如果文件不存在,则新建文件),O_EXCL:可以测试文件是否存在。
下面来看一个有名管道的例子,为了方便看代码,直接将命名管道应用在父子进程间通信:
这#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#define MYFIFO "myfifo"
#define MAX_DATA_LEN 128
#define DELAY_TIME 1
int main()
{
pid_t pid;
int pipe_fd[2];
char buf[MAX_DATA_LEN] = {0};
const char data[] ="Pipe Test Program";
int real_read, real_write;
int fd = 0;
// memset((void *)buf, 0, sizeof(buf));
if(access(MYFIFO, F_OK) == -1)
{
if ((mkfifo(MYFIFO, 0666) < 0) )
printf("Can not create fifo file\n");
exit(1);
}
}
if((pid = fork()) == 0)
{
/* open write fifo */
fd= open(MYFIFO,O_WRONLY);
if ( fd == -1)
{
printf("Open write file error\n");
exit(1);
}
if(write(fd, data, strlen(data)) > 0)
{
printf("Write %s to FIFO\n", data);
}
close(fd);
exit(0);
} else if (pid > 0)
{
/*open write fifo */
fd = open(MYFIFO, O_RDONLY);
if (fd == -1)
{
printf("Open fifo error\n");
exit(1);
}
if(read(fd, buf, MAX_DATA_LEN) > 0)
{
printf("Read %s from FIFO\n", buf);
}
close(fd);
exit(0);
}
return 0;
}
运行结果:
Write Pipe Test Program to FIFO
Read Pipe Test Program from FIFO
下一节将温习信号的使用