13.无名管道通讯编程
1.进程间的通信:
Linux作为典型的多进程操作系统,进程与进程之间肯定需要信息交流,这就需要进程通信。
2.进程通信的目的:
1.数据传输:一个进程需要将数据发送给另一个进程。
2.资源共享:多个进程之间共享同样的资源。
3.通知事件:一个进程需要向另一个/组进程发送信息,通知他们发生了某些事情。
4.进程控制:有些进程希望完全控制另一个进程的执行,例如Debug进程。此时控制进程希望能够拦截另一个进程的所有操作,并能够及时知道他的状态改变。
3.通讯发展:
Linux进程间通信(IPC:Interprocess Communication)由以下几个部分发展而来:
-
UNIX进程间通信。
-
基于System V进程间通信
-
POSIX进程间通信。
4.通讯发展-posix:
POSIX(Portable Operating System Interface)表示可移植操作系统接口。电气和电子工程师协会IEEE最初开发 POSIX 标准,是为了提高 UNIX 环境下应用程序的可移植性。然而,POSIX 并不局限于 UNIX,许多其它的操作系统,例如Microsoft Windows都支持POSIX 标准。
5.通讯方式:
Linux进程间通讯的主要方式有:
1、无名管道(pipe)
2、有名管道(FIFO)
3、信号(signal)
4、消息队列
5、共享内存
6、信号量
7、套接字(socket)
6.管道通信:
一个进程在管道的尾部写入数据,另一个进程从管道的头部读出数据。管道包括无名管道和有名管道两种,前者只能用于父进程和子进程间的通信,后者可用于运行于同一系统中的任意两个进程间的通信。
7.管道通信的特点:
1. 管道通讯是单向的,有固定的读端和写端。
2. 数据被进程从管道读出后,在管道中该数据就不存在了。
3. 当进程去读取空管道的时候,进程会阻塞。
4. 当进程往满管道写入数据时,进程会阻塞。
5. 管道容量为64KB
8.无名管道:
在Linux系统中,无名管道一旦创建完成后,操作无名管道等同于操作文件。无名管道的读端被视作一个文件,要读出无名管道的数据,需要找到读端文件;无名管道的写端也被视作一个文件,要往一个无名管道写数据,就是操作写端文件。
一、无名管道
在Linux系统中,无名管道一旦创建完成后,操作无名管道等同于操作文件。因此可以使用read,write,close等函数来访问无名管道。
无名管道:pipe
函数的原型:man 2 pile:
NAME
pipe, pipe2 - create pipe
SYNOPSIS
#include <unistd.h>
int pipe(int pipefd[2]);
#define _GNU_SOURCE
#include <unistd.h>
int pipe2(int pipefd[2], int flags);
DESCRIPTION
pipe() creates a pipe, a unidirectional data channel that can be used for
interprocess communication. The array pipefd is used to return two file
descriptors referring to the ends of the pipe. pipefd[0] refers to the read
end of the pipe. pipefd[1] refers to the write end of the pipe. Data written
to the write end of the pipe is buffered by the kernel until it is read from
the read end of the pipe. For further details, see pipe(7).
If flags is 0, then pipe2() is the same as pipe(). The following values can be
bitwise ORed in flags to obtain different behavior:
O_NONBLOCK Set the O_NONBLOCK file status flag on the two new open file
descriptions. Using this flag saves extra calls to fcntl(2) to
achieve the same result.
O_CLOEXEC Set the close-on-exec (FD_CLOEXEC) flag on the two new file
descriptors. See the description of the same flag in open(2) for
reasons why this may be useful.
RETURN VALUE
On success, zero is returned. On error, -1 is returned, and errno is set
appropriately.
ERRORS
EFAULT pipefd is not valid.
EINVAL (pipe2()) Invalid value in flags.
EMFILE Too many file descriptors are in use by the process.
ENFILE The system limit on the total number of open files has been reached.
VERSIONS
pipe2() was added to Linux in version 2.6.27; glibc support is available start-
ing with version 2.9.
CONFORMING TO
pipe(): POSIX.1-2001.
pipe2() is Linux-specific.
EXAMPLE
The following program creates a pipe, and then fork(2)s to create a child pro-
cess; the child inherits a duplicate set of file descriptors that refer to the
same pipe. After the fork(2), each process closes the descriptors that it
doesn't need for the pipe (see pipe(7)). The parent then writes the string
contained in the program's command-line argument to the pipe, and the child
reads this string a byte at a time from the pipe and echoes it on standard out-
put.
#include <sys/wait.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int
main(int argc, char *argv[])
{
int pipefd[2];
pid_t cpid;
char buf;
assert(argc == 2);
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Child reads from pipe */
close(pipefd[1]); /* Close unused write end */
while (read(pipefd[0], &buf, 1) > 0)
write(STDOUT_FILENO, &buf, 1);
write(STDOUT_FILENO, " ", 1);
close(pipefd[0]);
_exit(EXIT_SUCCESS);
} else { /* Parent writes argv[1] to pipe */
close(pipefd[0]); /* Close unused read end */
write(pipefd[1], argv[1], strlen(argv[1]));
close(pipefd[1]); /* Reader will see EOF */
wait(NULL); /* Wait for child */
exit(EXIT_SUCCESS);
}
}
SEE ALSO
fork(2), read(2), socketpair(2), write(2), popen(3), pipe(7)
COLOPHON
This page is part of release 3.22 of the Linux man-pages project. A descrip-
tion of the project, and information about reporting bugs, can be found at
http://www.kernel.org/doc/man-pages/.
函数的原型:
int pipe(int pipefd[2]);
该函数的功能是创建无名管道。需要的头文件是unistd.h。返回值:
成功返回0,失败返回-1.
该函数有两个参数:
参数是一个数组,该数组有两个元素,一个读端,一个写端,返回值是两个文件描述符。
Pipefd[0]是读端的fd,pipefd[1]是写端的fd。
实例:创建一个管道用于父子进程间的通信:
pipe.c:
#include <unistd.h>
#include <stdio.h>
void main(){
pid_t pid=0;
int pipefd[2];
char my_buf[12];
//before fork() create a pipe
pipe(pipefd);
//create a child process
pid = fork();
if(pid>0){
//father write something into pipe
write(pipefd[1],"forfish",8);
wait();//wait child process
close(pipefd[1]);//close pipe
exit(0);
}
if(pid == 0){
//son
read(pipefd[0],my_buf,8);
printf("child read %s ",my_buf);
close(pipefd[0]);
exit(0);
}
}
运行结果:
我们看到上面的无名管道操作是基于父子进程的操作,因为管道一旦创建,就是两个文件,一个读端文件,一个写端文件。我们操作管道就是操作这两个文件。