引用: http://blog.163.com/e_rommel/blog/static/187383045201192645544328/
进程间通信的作用
进程间需要数据传输、资源共享和事件通知。
进程间通信的方式
管道通信(无名管道和命名管道)
信号通信
内存资源共享
消息队列
信号量
管道
管道是单向的、先进先出的,它把一个进程的输出和另一个进程的输入连接在一起。一个进程(写进程)在管道的尾部写入数据,另一个进程(读进程)在管道的头部读出数据。数据被一个进程读出后,将被从管道中删除,其它读进程将再不能读到这些数据。管道提供了简单的流控制机制,进程试图读空管道时,进程将阻塞。同样,管道已经满时,进程再试图向管道写入数据,进程将阻塞。
管道创建
管道包括无名管道和有名管道两种,前者用于父进程和子进程之间的通信,后者可用于运行同一系统中任意两个进程间的通信。
无名管道由pipe()函数创建:
int pipe(int pipefiledis[2])
当一个管道创建时,它会建立两个文件描述符:
filedis[0]用于读管道,filedis[1]用于写管道。
管道关闭
管道关闭只需将这两个文件描述符关闭即可,可以使用普通的close函数进行关闭。
管道的读写
1.无名管道(用于父、子进程间的通信)
管道用于不同进程间的通信。通常先创建一个管道,再通过fork函数创建一个子进程,该子进程会继承父进程所创建的管道。
注:必须在系统调用fork()前调用pipe(),否则子进程将不会继承文件描述符。
2.命名管道(用于任意两个进程间的通信)
2.1 创建
#include<sys/types.h>
#include<sys/stat.h>
int mkfifo(const char *pathname,mode_t mode)
pathname:FIFO文件名
mode:属性
一旦创建了FIFO就可以用open打开它,一般的访问函数(close、write、read等)都可以用于FIFO。
实质上来讲命名管道就是一个文件
2.2 操作
当FIFO打开时,非阻塞标志(O_NONBLOCK)
将对以后的读写产生如下的影响:
1.没有使用O_NONBLOCK:访问要求无法满足时进程将阻塞。如试图读取空的FIFO,将导致进程阻塞;
2.使用O_NONBLOCK:访问要求无法满足时不阻塞,立刻出错返回,errno是ENXIO。
相应的,写一个无名管道代码看看:
功能:将ping localhost的结果重定向到当前的stdout
#include <stdio.h>
#include <windows.h>
int main(int argc, char **argv)
{
HANDLE hReadPipe, hWritePipe;//创建读、写通道
SECURITY_ATTRIBUTES saAttr;//用来设置读写通道的属性
PROCESS_INFORMATION piProcInfo; //获取创建线程后返回的线程信息
STARTUPINFO siStartInfo;
char buf[4096];
TCHAR cmd[256] = TEXT("ping localhost");//cmd /c ver
DWORD dwRead;
int ret;
memset(&saAttr, 0, sizeof(saAttr));
memset(&piProcInfo, 0, sizeof(piProcInfo));
memset(&siStartInfo, 0, sizeof(siStartInfo));
//设置管道的属性
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
//创建管道
if (0 == CreatePipe(&hReadPipe, &hWritePipe, &saAttr, 0))
{
printf("CreatePipe error!\n");
return -1;
}
//设置将要创建的线程的属性,输入输出重定向等
siStartInfo.cb = sizeof(STARTUPINFO);//管道大小
siStartInfo.wShowWindow = SW_HIDE;//窗体显示项
siStartInfo.hStdError = hWritePipe;
siStartInfo.hStdOutput = hWritePipe;//输出重定向--写入
siStartInfo.hStdInput = hReadPipe;//输入重定向--读取
siStartInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;//只有设置该项,前边4行才有效
Sleep(1);
ret = CreateProcess(NULL, cmd, NULL, NULL, TRUE, NULL, NULL, NULL, &siStartInfo, &piProcInfo);
//siStartInfo
//指向一个用于决定新进程的主窗体如何显示的STARTUPINFO结构体。
//piProcInfo
//指向一个用来接收新进程的识别信息的PROCESS_INFORMATION结构体。
if (0 == ret)
{
printf("Create process faile[%d].\n", GetLastError());
return -1;
}
//没用,仅仅是一个写入的测试
//这样可以向管道中写数据
memcpy(buf,"-asasa---\n",10);
buf[10]=0;
dwRead = 10;
ret = WriteFile(hWritePipe,buf,dwRead,&dwRead,NULL);
//这样可以读取刚才写进去的数据
ret = ReadFile(hReadPipe, buf, dwRead, &dwRead, NULL);
buf[dwRead] = '\0';
printf("%s", buf);
//之所以现在就关闭写句柄,是因为下边的ReadFile:
//The ReadFile function returns when one of the following conditions occur:
//A write operation completes on the write end of the pipe.
// The number of bytes requested is read.
// An error occurs.
//也就是说,如果这地方不关闭,下边的ReadFile会一直阻塞下去而无法结束,
//但是我下边使用i假设20次还没读取到数据就关闭写入句柄,想达到同样效果,却依然不能停止阻塞,不解啊
if (!CloseHandle(hWritePipe))
{
printf("Closing write handle fail.\n");
return -1;
}
int i = 0;
while(1)
{
memset(buf, 0, 1024);
ret = PeekNamedPipe(hReadPipe, buf, 1024, &dwRead, 0, 0);
if (dwRead > 0)
{
ret = ReadFile(hReadPipe, buf, dwRead, &dwRead, NULL);
if (0 == ret)
{
if (!CloseHandle(hReadPipe))
{
printf("Closing Read handle fail.\n");
return -1;
}
break;
}
buf[dwRead] = '\0';
printf("%s", buf);
i = 0;
else
{
if (i++ > 20)
{
if (!CloseHandle(hWritePipe))
{
printf("Closing write handle fail.\n");
return -1;
}
else
{
printf("Closing write handle success.\n");
}
i=0;
}
printf("++++++++++++ret = %d , dwRead = %d\n",ret,dwRead);
}
Sleep(200);
}
if (!CloseHandle(hReadPipe))
{
printf("Closing Read handle fail.\n");
return -1;
}
return 0;
}
有关阻塞疑问,参考: http://blog.csdn.net/cwb0525/article/details/4423627
知识:
http://blog.chinaunix.net/uid-24148050-id-3022548.html
6个进程通信的例子:http://blog.csdn.net/jmy5945hh/article/details/7367532
深刻理解进程通信:http://www.ibm.com/developerworks/cn/linux/l-ipc/
接下来是有名字的管道的实现: