在linux进程是系统调度的基本单位,一个进程完成一定的任务,但进程在执行任务时有时也需要其他进程提供信息,我们把它称为进程之间的通信。那进程是采取何种方式来进行通信呢?带着这个问题我们来学习吧!
进程间的通信分有很多方式,今天来让我们看看进程是如何采用管道来进行通信的。
为此我们写了一段简单的代码来进行说明,毕竟用代码描述起来更容易使人明白,我们将会对下面的代码进行分析以来阐明其中的道理。
一.首先进行名词解释:
a.pipe:管道,常用"|" 进行表示,我们可以理解成这样 "======",一段长长的管子,一段输入,一段输出,进程1在左边输入后,进程2就可以在右边取出,这样就实现了进程1和进程2之间的通信,呵呵,是不是很简单!
b.FIFO的意思是命名管道。因为普通的管道pipe在进程中创建后会随着进程的消亡而消亡,但命名管道不会,它建立后会永久存在,除非是你把它给删除了。
二.源代码分析
我们主要以注释的形式来分析代码
//
parentchildfifo.c
// =======================================================================
#include < errno.h >
#include < fcntl.h >
#include < stdio.h >
#include < string .h >
#include < unistd.h >
#include < sys / types.h >
#include < sys / wait.h >
#include < sys / stat.h >
#include " dofifo.h "
int main( int argc, char * argv[])
... {
pid_t childpid;
if (argc != 2) ...{
fprintf(stderr, "Usage: %s pipename ", argv[0]);
return -1;
}
// 创建命名管道管道的名字就是你输入的参数argv[1]
if (mkfifo(argv[1], FIFO_PERM) == -1) ...{
if (errno != EEXIST) ...{
fprintf(stderr, "[%ld]: failed to create named pipe %s: %s ",
(long)getpid(), argv[1], strerror(errno));
return -1;
}
}
// 创建2个进程,即父进程和子进程
if ((childpid = fork()) == -1) ...{
perror("Failed to fork");
return -1;
}
// 在下面argv[1],就是我们在上面建立的命名管道
if (childpid == 0)
// 子进程走这个分支 ,子进程向命名管道写入了"this was written by the child"字符串
return dofifochild(argv[1], "this was written by the child");
else
// 父进程走这个分支 ,父进程在子进程写完后读出那个字符串
return dofifoparent(argv[1]);
}
// =======================================================================
/**/ /*
*可见正如以上描述,子进程在管道的左边写入,而父进程在管道的右边读出,这样确实实现了进程之间
*的通讯。接着我们来看看dofifochild()和dofifoparent又是如何实现的。
*/
// dofifo.c
// =======================================================================
#include < errno.h >
#include < fcntl.h >
#include < stdio.h >
#include < string .h >
#include < unistd.h >
#include < sys / types.h >
#include < sys / stat.h >
#define BUFSIZE 256
// 子进程的实现代码
int dofifochild( const char * fifoname, const * idstring)
... {
char buf[BUFSIZE];
int fd;
int rval;
ssize_t strsize;
fprintf(stderr, "[%ld]:(child) about to open FIFO %s... ",
(long)getpid(), fifoname);
// 打开命名管道,如果被信号中断则再次打开,知道成功为止
while (((fd = open(fifoname, O_WRONLY)) == -1) && (errno == EINTR)) ;
if (fd == -1) ...{
fprintf(stderr, "[%ld]:failed to open named pipe %s for write:%s ",
(long)getpid(), fifoname, strerror(errno));
return -1;
}
// 生成要写入管道的字符串,即上面的"this was written by the child"加上自己的pid
rval = snprintf(buf, BUFSIZE, "[%ld]:%s ", (long)getpid(), idstring);
if (rval < 0) ...{
fprintf(stderr, "[%ld]:failed to make the string: ", (long)getpid());
return -1;
}
strsize = strlen(buf)+1;
fprintf(stderr, "[%ld]:about to write... ", (long)getpid());
// 向命名管道写入上面生成的字符串
rval = write(fd, buf, strsize);
if (rval != strsize) ...{
fprintf(stderr, "[%ld]:failed to write to pipe:%s ",
(long)getpid(), strerror(errno));
return -1;
}
fprintf(stderr, "[%ld]:finishing... ", (long)getpid());
return 0;
}
// 父进程的实现代码
int dofifoparent( const char * fifoname)
... {
char buf[BUFSIZE];
int fd;
int rval;
fprintf(stderr, "[%ld]:(parent)about to open FIFO %s... ",
(long)getpid(), fifoname);
// 打开命名管道,如果被信号中断则再次打开,知道成功为止
while(((fd = open(fifoname, O_RDONLY))) == -1 && (errno == EINTR)) ;
if (fd == -1) ...{
fprintf(stderr, "[%ld]:failed to open named pipe %s for read: %s ",
(long)getpid(), fifoname, strerror(errno));
return -1;
}
fprintf(stderr, "[%ld]:about to read... ", (long)getpid());
//读出子进程写入管道的字符串
rval = read(fd, buf, BUFSIZE);
if (rval == -1) ...{
fprintf(stderr, "[%ld]:failed to read from pipe: %s ",
(long)getpid(), strerror(errno));
return -1;
}
fprintf(stderr, "[%ld]:read %.*s ", (long)getpid(), rval, buf);
return 0;
}
// =======================================================================
// dofifo.h
#ifndef __DO_FILO__
#define __DO_FILO__
int dofifochild( const char * fifoname, const char * idstring);
int dofifoparent( const char * fifoname);
#endif
// =======================================================================
#include < errno.h >
#include < fcntl.h >
#include < stdio.h >
#include < string .h >
#include < unistd.h >
#include < sys / types.h >
#include < sys / wait.h >
#include < sys / stat.h >
#include " dofifo.h "
int main( int argc, char * argv[])
... {
pid_t childpid;
if (argc != 2) ...{
fprintf(stderr, "Usage: %s pipename ", argv[0]);
return -1;
}
// 创建命名管道管道的名字就是你输入的参数argv[1]
if (mkfifo(argv[1], FIFO_PERM) == -1) ...{
if (errno != EEXIST) ...{
fprintf(stderr, "[%ld]: failed to create named pipe %s: %s ",
(long)getpid(), argv[1], strerror(errno));
return -1;
}
}
// 创建2个进程,即父进程和子进程
if ((childpid = fork()) == -1) ...{
perror("Failed to fork");
return -1;
}
// 在下面argv[1],就是我们在上面建立的命名管道
if (childpid == 0)
// 子进程走这个分支 ,子进程向命名管道写入了"this was written by the child"字符串
return dofifochild(argv[1], "this was written by the child");
else
// 父进程走这个分支 ,父进程在子进程写完后读出那个字符串
return dofifoparent(argv[1]);
}
// =======================================================================
/**/ /*
*可见正如以上描述,子进程在管道的左边写入,而父进程在管道的右边读出,这样确实实现了进程之间
*的通讯。接着我们来看看dofifochild()和dofifoparent又是如何实现的。
*/
// dofifo.c
// =======================================================================
#include < errno.h >
#include < fcntl.h >
#include < stdio.h >
#include < string .h >
#include < unistd.h >
#include < sys / types.h >
#include < sys / stat.h >
#define BUFSIZE 256
// 子进程的实现代码
int dofifochild( const char * fifoname, const * idstring)
... {
char buf[BUFSIZE];
int fd;
int rval;
ssize_t strsize;
fprintf(stderr, "[%ld]:(child) about to open FIFO %s... ",
(long)getpid(), fifoname);
// 打开命名管道,如果被信号中断则再次打开,知道成功为止
while (((fd = open(fifoname, O_WRONLY)) == -1) && (errno == EINTR)) ;
if (fd == -1) ...{
fprintf(stderr, "[%ld]:failed to open named pipe %s for write:%s ",
(long)getpid(), fifoname, strerror(errno));
return -1;
}
// 生成要写入管道的字符串,即上面的"this was written by the child"加上自己的pid
rval = snprintf(buf, BUFSIZE, "[%ld]:%s ", (long)getpid(), idstring);
if (rval < 0) ...{
fprintf(stderr, "[%ld]:failed to make the string: ", (long)getpid());
return -1;
}
strsize = strlen(buf)+1;
fprintf(stderr, "[%ld]:about to write... ", (long)getpid());
// 向命名管道写入上面生成的字符串
rval = write(fd, buf, strsize);
if (rval != strsize) ...{
fprintf(stderr, "[%ld]:failed to write to pipe:%s ",
(long)getpid(), strerror(errno));
return -1;
}
fprintf(stderr, "[%ld]:finishing... ", (long)getpid());
return 0;
}
// 父进程的实现代码
int dofifoparent( const char * fifoname)
... {
char buf[BUFSIZE];
int fd;
int rval;
fprintf(stderr, "[%ld]:(parent)about to open FIFO %s... ",
(long)getpid(), fifoname);
// 打开命名管道,如果被信号中断则再次打开,知道成功为止
while(((fd = open(fifoname, O_RDONLY))) == -1 && (errno == EINTR)) ;
if (fd == -1) ...{
fprintf(stderr, "[%ld]:failed to open named pipe %s for read: %s ",
(long)getpid(), fifoname, strerror(errno));
return -1;
}
fprintf(stderr, "[%ld]:about to read... ", (long)getpid());
//读出子进程写入管道的字符串
rval = read(fd, buf, BUFSIZE);
if (rval == -1) ...{
fprintf(stderr, "[%ld]:failed to read from pipe: %s ",
(long)getpid(), strerror(errno));
return -1;
}
fprintf(stderr, "[%ld]:read %.*s ", (long)getpid(), rval, buf);
return 0;
}
// =======================================================================
// dofifo.h
#ifndef __DO_FILO__
#define __DO_FILO__
int dofifochild( const char * fifoname, const char * idstring);
int dofifoparent( const char * fifoname);
#endif
至此我们就知道了进程之间的通信方法。如果你还是不太了解,那就拷贝代码来实践吧!看看结果相信你就会明白的!