进程间通信详解 - 匿名管道实现

进程间通信--匿名管道 管道是Unix中最古老的进程间通信的形式。 阅读详情

引子

前面的两篇博文分别介绍了通过剪贴板和通过邮槽来实现进程之间的通信,

其中剪贴板呢,是用来实现本地两个进程之间的通信,

而邮槽则既可以实现本地两个进程之间的通信,也可以实现本地进程和服务器进程的通信,

当然,上面这两种方式都是有局限性的,比如剪贴板就只能实现本地进程之间的通信,

而在邮槽中,虽然是既可以实现本地进程之间的通信,又能实现本地进程和远程服务器进程的通信,

但是使用邮槽的话,服务端只能接收数据,不能发送数据,而客户端只能发送数据而不能接收数据。

而本篇博文介绍的这个通过匿名管道来实现进程之间的通信的话,

局限性就显得更加严重了,

第一:匿名管道只能实现本地进程之间的通信,不能实现跨网络之间的进程间的通信。

第二:匿名管道只能实现父进程和子进程之间的通信,而不能实现任意两个本地进程之间的通信。

           

            

匿名管道概述

既然是匿名管道的话,自然,就是没有名字的管道了,还有一种管道呢,叫做命名管道,

命名管道的功能是很强大的,匿名管道在命名管道面前,功能那是简陋的不行的,

至于命名管道的话,会留到下一篇博文中介绍的,

匿名管道正因为提供的功能很单一,所以它所需要的系统的开销也就比命名管道小很多,

在本地机器上可以使用匿名管道来实现父进程和子进程之间的通信,

这里需要注意两点,第一就是在本地机器上,这是因为匿名管道不支持跨网络之间的两个进程之间的通信,

第二就是实现的是父进程和子进程之间的通信,而不是任意的两个进程。

然后得话还顺便介绍匿名管道的另外一种功能,其通过匿名管道可以实现子进程输出的重定向,

何为输出重定向呢?还请听下面详解:

比如我现在建立一个 Win32 的 Console 程序,然后在其中使用如下代码来输出一些信息:

#include <iostream>
using namespace std;
 
int main(int argc, char * argv)
{
    cout<<"Zachary  XiaoZhen "<<endl<<endl;
    cout<<"Happy  New   Year"<<endl<<endl;
    
    system("pause");
}

那么在默认下,编译运行上面的代码时,Windows 会弹出一个黑框框,并且在这个黑框框中显示一些信息,

image

为什么一定要将输出的信息显示在这个黑框框中呢?有没有办法让其显示在我们自己定义的文本框中呢?

而后我们再看一幅截图:

QQ截图未命名

上面画了很多红线的这个区域中的信息来自那里呢?为什么会在这个文本框中输出呢?

其实这就可以通过匿名管道来实现,

在卸载 QQ 游戏这幅截图中呢,其实运行了两个进程,

一个就是我们看到的这个输出了图形界面的进程,我们称之为卸载表象进程(父进程),

而另外一个用来执行真正意义上的卸载的进程我们称之为卸载实质进程(子进程)。

其实该卸载表象进程在其执行过程中创建了卸载实质进程来执行真正的卸载操作,

而后,卸载实质进程会输出上面用红色矩形标记的区域中的信息,

如果我们使用默认的输出的话,卸载实质进程会将上面红色区域标记中的信息输出到默认的黑框框中,

但是我们可以使用匿名管道来更改卸载实质进程的输出,

让其将输出数据输入到匿名管道中,而后卸载表象进程从匿名管道中读取到这些输出数据,

然后再将这些数据显示到卸载表象进程的文本框中就可以了。

而上面的这种用来更改卸载实质进程的输出的技术就称之为输出重定向。

当然与之相对的还有输入重定向的。

我们可以让一个进程的输入来自于匿名管道,而不是我们在黑框框中输入数据。

话说到这份上呢,也可以点出一点东东了,

上面的这个重定向不就是利用匿名管道实现的父进程和子进程之间的通信嘛。

             

          

匿名管道的使用

匿名管道主要用于本地父进程和子进程之间的通信,

在父进程中的话,首先是要创建一个匿名管道,

在创建匿名管道成功后,可以获取到对这个匿名管道的读写句柄,

然后父进程就可以向这个匿名管道中写入数据和读取数据了,

但是如果要实现的是父子进程通信的话,那么还必须在父进程中创建一个子进程,

同时,这个子进程必须能够继承和使用父进程的一些公开的句柄,

为什么呢?

因为在子进程中必须要使用父进程创建的匿名管道的读写句柄,

通过这个匿名管道才能实现父子进程的通信,所以必须继承父进程的公开句柄。

同时在创建子进程的时候,

必须将子进程的标准输入句柄设置为父进程中创建匿名管道时得到的读管道句柄,

将子进程的标准输出句柄设置为父进程中创建匿名管道时得到的写管道句柄。

然后在子进程就可以读写匿名管道了。

               

             

匿名管道的创建

BOOL WINAPI CreatePipe(
          __out   PHANDLE hReadPipe,
          __out   PHANDLE hWritePipe,
          __in    LPSECURITY_ATTRIBUTES lpPipeAttributes,
          __in    DWORD nSize );

参数 hReadPipe 为输出参数,该句柄代表管道的读取句柄。

参数 hWritePipe 为输出参数,该句柄代表管道的写入句柄。

参数 lpPipeAttributes 为一个输入参数,指向一个 SECURITY_ATTRIBUTES 的结构体指针,

其检测返回的句柄是否能够被子进程继承,如果此参数为 NULL ,则表明句柄不能被继承,

在匿名管道中,由于匿名管道要在父子进程之间进行通信,

而子进程如果想要获得匿名管道的读写句柄,则其只能通过从父进程继承获得,

当一个子进程从其父进程处继承了匿名管道的读写句柄以后,

子进程和父进程之间就可以通过这个匿名管道的读写句柄进行通信了。

所以在这里必须构建一个 SECURITY_ATTRIBUTES 的结构体,

并且该结构体的第三个结构成员变量 bInheritHandle 参数必须设置为 TRUE ,

从而让子进程可以继承父进程所创建的匿名管道的读写句柄。

typedef struct _SECURITY_ATTRIBUTES {
 
    DWORD nLength;
 
    LPVOID lpSecurityDescriptor;
 
    BOOL bInheritHandle;
 
} SECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;

参数 nSize 用来指定缓冲区的大小,

如果此参数设置为 0 ,则表明系统将使用默认的缓冲区大小。一般将该参数设置为 0 即可。

         

              

子进程的创建

BOOL  CreateProcess( 
        LPCWSTR pszImageName,  LPCWSTR pszCmdLine, 
        LPSECURITY_ATTRIBUTES psaProcess, 
        LPSECURITY_ATTRIBUTES psaThread, 
        BOOL fInheritHandles,  DWORD fdwCreate, 
        LPVOID pvEnvironment,  LPWSTR pszCurDir, 
        LPSTARTUPINFOW psiStartInfo, 
        LPPROCESS_INFORMATION pProcInfo );

参数 pszImageName 是一个指向 NULL 终止的字符串,用来指定可执行程序的名称。

参数 pszCmdLine 用来指定传递给新进程的命令行字符串,一般做法是在 pszImageName 中传递可执行文件的名称,

在 pszCmdLine 中传递命令行参数。

参数 psaProcess 即代表当 CreateProcess 函数创建进程时,需要给进程对象设置一个安全性。

参数 psaThread 代表当 CreateProcess 函数创建新进程后,需要给该进程的主线程对象设置一个安全性。

参数 fInheritHandles 用来指定父进程随后创建的子进程是否能够继承父进程的对象句柄,

如果该参数设置为 TRUE ,则父进程的每一个可继承的打开句柄都将被子进程所继承,

继承的句柄与原始的句柄拥有同样的访问权。

在匿名管道的使用中,因为子进程需要使用父进程中创建的匿名管道的读写句柄,

所以应该将这个参数设置为 TRUE ,从而可以让子进程继承父进程创建的匿名管道的读写句柄。

参数 fdwCreate 用来指定控件优先级类和进程创建的附加标记。

如果只是为了启动子进程,则并不需要设置它创建的标记,可以将此参数设置为 0,

对于这个参数的具体取值列表可以参考 MSDN 。

参数 pvEnvironment 代表指向环境块的指针,

如果该参数设置为 NULL ,则默认将使用父进程的环境。通常给该参数传递 NULL。

参数 pszCurDir 用来指定子进程当前的路径,

这个字符串必须是一个完整的路径名,其包括驱动器的标识符,

如果此参数设置为 NULL ,那么新的子进程将与父进程拥有相同的驱动器和目录。

参数 psiStartInfo 指向一个 StartUpInfo 的结构体的指针,用来指定新进程的主窗口如何显示。

typedef struct _STARTUPINFOA {
 
    DWORD cb;
 
    LPSTR lpReserved;
 
    LPSTR lpDesktop;
 
    LPSTR lpTitle;
 
    DWORD dwX;
 
    DWORD dwY;
 
    DWORD dwXSize;
 
    DWORD dwYSize;
 
    DWORD dwXCountChars;
 
    DWORD dwYCountChars;
 
    DWORD dwFillAttribute;
 
    DWORD dwFlags;
 
    WORD wShowWindow;
 
    WORD cbReserved2;
 
    LPBYTE lpReserved2;
 
    HANDLE hStdInput;
 
    HANDLE hStdOutput;
 
    HANDLE hStdError;
 
} STARTUPINFOA, *LPSTARTUPINFOA;

对于 dwFlags 参数来说,如果其设置为 STARTF_USESTDHANDLES ,

则将会使用该 STARTUPINFO 结构体中的 hStdInput , hStdOutput , hStdError 成员,

来设置新创建的进程的标准输入,标准输出,标准错误句柄。

参数 pProcInfo 为一个输出参数,

指向一个 PROCESS_INFORMATION 结构体的指针,用来接收关于新进程的标识信息。

typedef struct _PROCESS_INFORMATION 
{             
    HANDLE hProcess;             
    HANDLE hThread;             
    DWORD dwProcessId;              
    DWORD dwThreadId; 
 
}PROCESS_INFORMATION;

其中 hProcess 和 hThread 分别用来标识新创建的进程句柄和新创建的进程的主线程句柄。

dwProcessId 和 dwThreadId 分别是全局进程标识符和全局线程标识符。

前者可以用来标识一个进程,后者用来标识一个线程。

        

               

示例:匿名管道实现父子进程间通信

父进程实现:(简单 MFC 程序)

项目结构:

image

消息以及成员函数和成员变量的声明:

public:
    //创建匿名管道
    afx_msg void OnBnClickedBtnCreatePipe();
    //写匿名管道
    afx_msg void OnBnClickedBtnWritePipe();
    //读匿名管道
    afx_msg void OnBnClickedBtnReadPipe();
 
    //定义父进程读匿名管道的成员函数
    void ParentReadPipe(void);
    //定义父进程写匿名管道的成员函数
    void ParentWritePipe(void);
    //创建 SECURITY_ATTRIBUTES 结构的成员函数
    void CreateSecurityAttributes(PSECURITY_ATTRIBUTES pSa);
    //创建 STARTUPINFO 结构的成员函数
    void CreateStartUpInfo(LPSTARTUPINFO lpStartUpInfo);
    //创建匿名管道的成员函数
    void CreateNoNamedPipe(void);
 
    //分别代表要从匿名管道中读的数据和要写到匿名管道中的数据
    CString m_CStrReadPipe;
    CString m_CStrWritePipe;
 
    //保存创建匿名管道后所得到的对匿名管道的读写句柄
    HANDLE hPipeRead;
    HANDLE hPipeWrite;
    
    //保证匿名管道只创建一次
    BOOL m_PipeIsCreated;

消息映射表定义:

const int        dataLength    = 100;
 
CNoNamedPipeParentDlg::CNoNamedPipeParentDlg(CWnd* pParent /*=NULL*/)
    : CDialogEx(CNoNamedPipeParentDlg::IDD, pParent)
    , m_CStrReadPipe(_T(""))
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
 
    m_PipeIsCreated = FALSE;
}
 
void CNoNamedPipeParentDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    DDX_Text(pDX, IDC_EDIT_WRITE_PIPE, m_CStrWritePipe);
    DDX_Text(pDX, IDC_EDIT_READ_PIPE, m_CStrReadPipe);
}
 
BEGIN_MESSAGE_MAP(CNoNamedPipeParentDlg, CDialogEx)
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_BN_CLICKED(IDC_BTN_CREATE_PIPE, 
                &CNoNamedPipeParentDlg::OnBnClickedBtnCreatePipe)
    ON_BN_CLICKED(IDC_BTN_WRITE_PIPE, 
                &CNoNamedPipeParentDlg::OnBnClickedBtnWritePipe)
    ON_BN_CLICKED(IDC_BTN_READ_PIPE, 
                &CNoNamedPipeParentDlg::OnBnClickedBtnReadPipe)
END_MESSAGE_MAP()

消息处理函数:

//创建匿名管道按钮的消息处理函数
void CNoNamedPipeParentDlg::OnBnClickedBtnCreatePipe()
{
    if(m_PipeIsCreated == FALSE)
    {
        this->CreateNoNamedPipe();
    }
}
 
 
//写入数据到匿名管道中按钮的消息处理函数
void CNoNamedPipeParentDlg::OnBnClickedBtnWritePipe()
{
    this->ParentWritePipe();
}
 
 
//从匿名管道中读取数据按钮的消息处理函数
void CNoNamedPipeParentDlg::OnBnClickedBtnReadPipe()
{
    this->ParentReadPipe();
}
 
 
//接收数据
void CNoNamedPipeParentDlg::ParentReadPipe(void)
{
    DWORD            dwRead;
    char *            pReadBuf;
    CString            cStrRecvData;
 
    pReadBuf = new char[dataLength];
    memset(pReadBuf, 0, dataLength);
 
    if(!ReadFile(hPipeRead, pReadBuf, dataLength, &dwRead, NULL))
    {
        MessageBox(TEXT("   从匿名管道接收数据失败 ..."), 
            TEXT("提示"), MB_ICONERROR);
        return;
    }
 
    cStrRecvData = "   从匿名管道接收数据成功:    ";
    cStrRecvData += pReadBuf;
 
    this->m_CStrReadPipe.Empty();
 
    this->m_CStrReadPipe = pReadBuf;
    UpdateData(FALSE);
 
    MessageBox(cStrRecvData, TEXT("提示"), MB_ICONINFORMATION);
}
 
 
//发送数据
void CNoNamedPipeParentDlg::ParentWritePipe(void)
{
    UpdateData();
 
    if(!this->m_CStrWritePipe.IsEmpty())
    {
        char *            pSendData;
        DWORD            dwWrite;
        CString            cStrSendData;
 
        //在这里需要将 Unicode 字符集转换为 ASCII 字符集
        pSendData = new char[this->m_CStrWritePipe.GetLength() + 1];
        memset(pSendData, 0, this->m_CStrWritePipe.GetLength() + 1);
        for(int i=0;i<this->m_CStrWritePipe.GetLength();i++)
        {
            pSendData[i] = (char)this->m_CStrWritePipe.GetAt(i);
        }
 
        if(!WriteFile(hPipeWrite, pSendData, 
            this->m_CStrWritePipe.GetLength() + 1, &dwWrite, NULL))
        {
            MessageBox(TEXT("   给匿名管道发送数据失败 ..."), 
                TEXT("提示"), MB_ICONERROR);
            return;
        }
 
        cStrSendData = "   给匿名管道发送数据成功:    ";
        cStrSendData += this->m_CStrWritePipe;
 
        this->m_CStrWritePipe.Empty();
        UpdateData(FALSE);
 
        MessageBox(cStrSendData, TEXT("提示"), MB_ICONINFORMATION);
    }
    else
    {
        MessageBox(TEXT("   请先输入要发送给匿名管道的数据 ..."), 
            TEXT("提示"), MB_ICONERROR);
    }
}
 
 
//创建 SECURITY_ATTRIBUTES 结构
void CNoNamedPipeParentDlg::CreateSecurityAttributes(PSECURITY_ATTRIBUTES pSa)
{
    //这里必须将 bInheritHandle 设置为 TRUE,
    //从而使得子进程可以继承父进程创建的匿名管道的句柄
    pSa->bInheritHandle = TRUE;
    pSa->lpSecurityDescriptor = NULL;
    pSa->nLength = sizeof(SECURITY_ATTRIBUTES);
}
 
 
//用来初始化新进程的 STARTUPINFO 成员
void CNoNamedPipeParentDlg::CreateStartUpInfo(LPSTARTUPINFO lpStartUpInfo)
{
    memset(lpStartUpInfo, 0, sizeof(STARTUPINFO));
 
    lpStartUpInfo->cb = sizeof(STARTUPINFO);
    lpStartUpInfo->dwFlags = STARTF_USESTDHANDLES;
 
    //子进程的标准输入句柄为父进程管道的读数据句柄
    lpStartUpInfo->hStdInput = hPipeRead;
 
    //子进程的标准输出句柄为父进程管道的写数据句柄
    lpStartUpInfo->hStdOutput = hPipeWrite;
 
    //子进程的标准错误处理句柄和父进程的标准错误处理句柄一致
    lpStartUpInfo->hStdError = GetStdHandle(STD_ERROR_HANDLE);
}
 
 
//创建匿名管道
void CNoNamedPipeParentDlg::CreateNoNamedPipe(void)
{
    SECURITY_ATTRIBUTES                sa;
    PROCESS_INFORMATION                processInfo;
    STARTUPINFO                        startUpInfo;
 
    CreateSecurityAttributes(&sa);
    if(!CreatePipe(&hPipeRead, &hPipeWrite, &sa, 0))
    {
        MessageBox(TEXT("   创建匿名管道失败 ..."), 
            TEXT("提示"), MB_ICONERROR);
        return;
    }
 
    CreateStartUpInfo(&startUpInfo);
    if(!CreateProcess(TEXT("NoNamedPipeChild.exe"), 
        NULL, NULL, NULL, TRUE, 
        CREATE_NEW_CONSOLE, NULL, NULL, 
        &startUpInfo, &processInfo))
    {
        CloseHandle(hPipeRead);
        CloseHandle(hPipeWrite);
 
        hPipeWrite = NULL;
        hPipeRead = NULL;
 
        MessageBox(TEXT("   创建子进程失败 ..."), 
            TEXT("提示"), MB_ICONERROR);
        return;
    }
    else
    {
        m_PipeIsCreated = TRUE;
 
        //对于 processInfo.hProcess 和 processInfo.hThread 
        //这两个句柄不需要使用,所以释放资源
        CloseHandle(processInfo.hProcess);
        CloseHandle(processInfo.hThread);
    }
}

子进程实现:(简单 MFC 程序)

项目结构:

image

消息以及成员函数和成员变量的声明:

// 实现
protected:
    HICON m_hIcon;
 
    // 生成的消息映射函数
    virtual BOOL OnInitDialog();
    afx_msg void OnPaint();
    afx_msg HCURSOR OnQueryDragIcon();
    DECLARE_MESSAGE_MAP()
public:
    afx_msg void OnBnClickedBtnWritePipe();
    afx_msg void OnBnClickedBtnReadPipe();
 
    //保存从父进程得到针对于匿名管道的读写句柄
    HANDLE hPipeRead;
    HANDLE hPipeWrite;
 
    //分别代表要从匿名管道中读的数据和要写到匿名管道中的数据
    CString m_CStrWritePipe;
    CString m_CStrReadPipe;
 
    //子进程读取匿名管道
    void ChildReadPipe(void);
    //子进程写匿名管道
    void ChildWritePipe(void);
    //子进程获取从父进程处继承得到的关于匿名管道的读写句柄
    void GetReadWriteHandleFromParent(void);
 
    //只需要获取一次匿名管道的读写句柄即可
    BOOL m_IsGettedParentHandle;

消息映射表定义:

const int        dataLength    = 100;
 
CNoNamedPipeChildDlg::CNoNamedPipeChildDlg(CWnd* pParent /*=NULL*/)
    : CDialogEx(CNoNamedPipeChildDlg::IDD, pParent)
    , m_CStrWritePipe(_T(""))
    , m_CStrReadPipe(_T(""))
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
 
    this->m_IsGettedParentHandle = FALSE;
}
 
void CNoNamedPipeChildDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    DDX_Text(pDX, IDC_EDIT_WRITE_PIPE, m_CStrWritePipe);
    DDX_Text(pDX, IDC_EDIT_READ_PIPE, m_CStrReadPipe);
}
 
BEGIN_MESSAGE_MAP(CNoNamedPipeChildDlg, CDialogEx)
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_BN_CLICKED(ID_BTN_WRITE_PIPE, 
            &CNoNamedPipeChildDlg::OnBnClickedBtnWritePipe)
    ON_BN_CLICKED(ID_BTN_READ_PIPE, 
            &CNoNamedPipeChildDlg::OnBnClickedBtnReadPipe)
END_MESSAGE_MAP()

消息处理函数:

//往匿名管道中写入数据按钮的消息处理函数
void CNoNamedPipeChildDlg::OnBnClickedBtnWritePipe()
{
    //如果子进程还没有获得对匿名管道的读写句柄的话需要先获取句柄
    this->GetReadWriteHandleFromParent();
 
    ChildWritePipe();
}
 
 
//从匿名管道中读取数据按钮的消息处理函数
void CNoNamedPipeChildDlg::OnBnClickedBtnReadPipe()
{
    //如果子进程还没有获得对匿名管道的读写句柄的话需要先获取句柄
    this->GetReadWriteHandleFromParent();
 
    ChildReadPipe();
}
 
//从匿名管道读取数据成员函数
void CNoNamedPipeChildDlg::ChildReadPipe(void)
{
    DWORD            dwRead;
    char *            pReadBuf;
    CString            cStrRecvData;
 
    pReadBuf = new char[dataLength];
    memset(pReadBuf, 0, dataLength);
 
    //读取数据
    if(!ReadFile(hPipeRead, pReadBuf, dataLength, &dwRead, NULL))
    {
        MessageBox(TEXT("   从匿名管道接收数据失败 ..."), 
            TEXT("提示"), MB_ICONERROR);
        return;
    }
 
    cStrRecvData = "   从匿名管道接收数据成功:    ";
    cStrRecvData += pReadBuf;
 
    this->m_CStrReadPipe.Empty();
    this->m_CStrReadPipe = pReadBuf;
    UpdateData(FALSE);
 
    MessageBox(cStrRecvData, TEXT("提示"), MB_ICONINFORMATION);
}
 
//往匿名管道中写入数据
void CNoNamedPipeChildDlg::ChildWritePipe(void)
{
    UpdateData();
 
    if(!this->m_CStrWritePipe.IsEmpty())
    {
        char *                pSendData;
        DWORD                dwWrite;
        CString                cStrSendData;
 
        //在这里需要将 Unicode 字符集转换为 ASCII 字符集
        pSendData = new char[this->m_CStrWritePipe.GetLength() + 1];
        memset(pSendData, 0, this->m_CStrWritePipe.GetLength() + 1);
        for(int i=0;i<this->m_CStrWritePipe.GetLength();i++)
        {
            pSendData[i] = (char)this->m_CStrWritePipe.GetAt(i);
        }
 
        //写入数据
        if(!WriteFile(hPipeWrite, pSendData, 
            this->m_CStrWritePipe.GetLength(), &dwWrite, NULL))
        {
            MessageBox(TEXT("   给匿名管道发送数据失败 ..."), 
                TEXT("提示"), MB_ICONERROR);
            return;
        }
 
        cStrSendData = "给匿名管道发送数据成功:    ";
        cStrSendData += this->m_CStrWritePipe;
 
        this->m_CStrWritePipe.Empty();
        UpdateData(FALSE);
 
        MessageBox(cStrSendData, TEXT("提示"), MB_ICONINFORMATION);
    }
    else
    {
        MessageBox(TEXT("   请先输入要发送给匿名管道的数据 ..."), 
            TEXT("提示"), MB_ICONERROR);
    }
}
 
//需要获取继承自父进程的匿名管道读写句柄
void CNoNamedPipeChildDlg::GetReadWriteHandleFromParent(void)
{
    if(this->m_IsGettedParentHandle == FALSE)
    {
        hPipeRead = GetStdHandle(STD_INPUT_HANDLE);
        hPipeWrite = GetStdHandle(STD_OUTPUT_HANDLE);
 
        this->m_IsGettedParentHandle = TRUE;
    }
}

效果展示:

首先需要将子进程的可执行文件拷贝到父进程所在目录下,否则创建进程时会找不到子进程的可执行文件。 

image

启动父进程可执行文件,并单击创建匿名管道按钮,此时会弹出子进程窗口(新建了进程):

image

再在父进程的左边文本框中输入数据,单击写入数据按钮:

image

再在子进程窗口中单击读取数据按钮:

image

再在子进程窗口左边的文本框中输入数据,单击写入数据按钮:

image

再在父进程窗口中单击读取数据按钮:

image

             

                  

结束语

从上面的效果展示中就可以看出我们确实是实现了父子进程之间通过匿名管道来进行通信,

最后再来总结一下对于匿名管道的使用,

匿名管道一般用于实现本地父子进程之间的通信,其不能实现跨网络进程之间的通信,

同时其也一般只用于实现父进程和子进程之间的通信。

像匿名管道的话,其和邮槽不同,

其可以实现父进程即可以向子进程发送数据,同时父进程又可以从子进程接收到数据。

而且子进程可以接收来自父进程的数据,并且也可以给父进程发送数据。

最后的话,那就是今天是 2011  年的第一天,说实话,太冷了,实在不想写博客的,

不过因为想写完这个进程之间通信的几篇博客,同时也对 2011 有个好的开始,

所以硬着头皮写下了这篇博客,在这里希望自己 2011 会更好,人会更成熟,更上进 !!!

同时也祝各位新年快乐,2011 会更好 !!!

            

Linux:进程间通信(创建匿名管道,父子进程间通信,兄弟进程间通信) 过一遍管道的基础知识: 1.本质:存在于高速缓存区,属于伪文件,不占用磁盘空间。缓冲区大小默认4K,可根据实际情况适当调整 2.读端和写端各自对应一个文件描述符,数据从写端流入,读端流出 3.操作管道的进程被销毁之后,管道占用的内存空间自动被释放 4.管道默认阻塞 5.内部实现方式:循环队列 (先进先出),属于半双工。数据只能读一次,不能重复读取 6.匿名管道创建:int pipe(int pip... 阅读详情

相关推荐

Window系统下搭建GIT本地服务器

这是一篇在window下搭建git本地服务器的教程,带你一步步搭建出本地的git服务器。

qwer971211的博客 2万+

进程间通信 - 匿名管道实现

引子 前面的两篇博文分别介绍了通过剪贴板和通过邮槽来实现进程之间的通信, 其中剪贴板呢,是用来实现本地两个进程之间的通信, 而邮槽则既可以实现本地两个进程之间的通信,也可以实现本地进程和服务器进程的通信, 当然,上面这两种方式都是有局限性的,比如剪贴板就只能实现本地进程之间的通信, 而在邮槽中,虽然是既可以实现本地进程之间的通信,又能实现本地进程和远程服务器进程的通信, 但是使用邮槽的...

weixin_33729196的博客 536

深入解析ffmpeg编码错误:avcodec_send_frame()返回-22的排查与修复

本文深入解析FFmpeg编码中avcodec_send_frame()返回-22(AVERROR(EINVAL))错误的根本原因与系统排查方法。核心问题通常在于编码器未正确打开或上下文传递错误。文章提供了从检查编码器查找、确认avcodec_open2()调用,到验证上下文传递的完整修复指南,并附有可运行的代码示例,帮助开发者快速定位并解决这一常见编码障碍。

weixin_28049429的博客 390

Linux开发(十一):多进程通信与同步---管道通信

管道是Linux环境中历史最悠久的进程间通信方式,本质上,管道就是一个操作方式为文件的内存缓冲区。Linux上的管道分两种类型,分别为匿名管道(无名管道)和有名管道(命名管道)。 目录 一、匿名管道 1、创建匿名管道 2、注意事项 3、代码示例 二、有名管道 1、管道创建 (1) 命令行创建 (2) 函数创建 2、删除命名管道 3、代码示例 一、匿名管道 匿名管道是一种特殊类型的文件,完全由操作系统管理和维护,因为其存储位置只有亲缘关系的进程知道,所以只能用于亲缘关系的进程..

王木木 2474

AI人工智能领域多模态大模型的数字孪生应用

本文旨在系统性地介绍多模态大模型在数字孪生领域的最新应用进展,探讨其技术原理、实现方法和应用价值。内容范围涵盖从基础概念到前沿技术,从理论分析到实践案例。文章首先介绍多模态大模型和数字孪生的基本概念,然后深入分析两者的结合点,接着通过具体案例展示应用实践,最后探讨未来发展趋势和挑战。多模态大模型:能够同时处理和融合多种数据模态(如文本、图像、视频、音频等)的大型人工智能模型数字孪生:物理实体或系统的虚拟副本,通过实时数据同步实现虚实交互跨模态融合:将不同数据模态的信息进行有效整合和关联的技术。

AI云原生与云计算技术学院 1325

避开这些坑!baostock获取股票数据时最容易犯的5个错误(Python实战)

本文针对使用Python和baostock库获取股票历史数据时常见的五大陷阱,提供了实战解决方案。重点剖析了登录状态验证、参数配置、数据解析、批量操作效率及数据一致性校验等关键环节,帮助开发者构建稳健高效的数据获取流程,避免因细节疏忽导致的数据错误或性能瓶颈。

websocket5live的博客 1441

通信 匿名管道通信 消息通信 Linux CentOS

1、匿名管道通信     本实验实现父子进程之间的管道通信。程序中由父进程创建一个匿名管道,并创建了2个子进程。两个子进程都从管道写端(入口)写入一个发给父进程的消息(字符串),父进程则从管道读端(出口)读取发来的两个消息。 下面是程序的部分代码,请完成该程序的设计。

YouxiBug 1392

M2DGR数据集在主流SLAM框架中的实战配置指南:从ORB-SLAM到FAST-LIO的完整流程

本文提供了M2DGR多传感器数据集在主流SLAM框架中的实战配置指南。详细介绍了从ORB-SLAM、VINS-Mono等视觉/视觉惯性框架,到A-LOAM、FAST-LIO等激光/激光惯性框架的完整配置流程、参数调整要点及常见问题解决方案。通过实际测试对比,分析了各算法在M2DGR数据集上的表现,为机器人定位与建图研究提供了宝贵的实战经验。

k9l0m1的博客 481

进程间通信之匿名管道

细谈进程间通信之匿名管道

xbhinsterest11的博客 1311

Zotero on Mac: 高效文献管理与引用指南

本文为Mac用户提供了一份全面的Zotero高效文献管理与引用指南。从安装配置、文献收集管理、PDF深度阅读与笔记,到在Word中无缝插入引用和生成参考文献,详细讲解了核心工作流。同时推荐了ZotFile、Better BibTeX等必备插件,并介绍了利用同步与WebDAV实现跨设备无缝衔接的方法,助力研究者构建个人数字知识库,极大提升学术研究效率。

weixin_29281941的博客 1177

ThingsBoard教程(十六):RPC部件的使用

前言 各位读者好,截止目前,ThingsBoard系列文章已经做了十篇,分别是 ThingsBoard教程(一):ThingBoard介绍及安装 ThingsBoard教程(二):租户,租户配置,部件组模块的使用 ThingsBoard教程(三):系统设置模块的使用 ThingsBoard教程(四):规则链简介与操作 ThingsBoard教程(五):客户,资产管理 ThingsBoard教程(六):设备管理 ThingsBoard教程(七):模拟设备遥测 ThingsBoard教程(八):自定义UI T

知无涯 4580

【从浅学到熟知Linux】进程间通信之匿名管道方式(进程间通信方式汇总、匿名管道的创建、匿名管道实现进程池详解)

什么是通信?所谓通信,就是要实现两个或多个进程实现数据层面的交互。而在操作系统中,为了保持进程的独立性,不允许其他进程访问某个进程的地址空间。正因为进程独立性的存在,导致进程的通信成本比较高。(因为A进程无法直接将通信数据写入B进程的地址空间中,而需要通过在两进程外部创建某个空间来实现进程的通信,故成本较高)。为什么进程需要通信呢?①数据传输:一个进程需要将它的数据发送给另一个进程;②资源共享:多个进程之间共享同样的数据,当前进程需要将共享数据发送给需要该数据的进程;

Jammingpro 1143

回归预测 | MATLAB实现TCN时间卷积神经网络多输入单输出回归预测

回归预测 | MATLAB实现TCN时间卷积神经网络多输入单输出回归预测

机器学习之心的博客,关注并私信文章链接,获取对应文章源码和数据。 2492

【从浅学到熟知Linux】进程间通信之命名管道(含mkfifo命令及函数调用示例、读写阻塞及ONOBLOCK详解、匿名与命名管道区别、命名管道实现Client&Server)

🏠关于专栏:Linux的浅学到熟知专栏用于记录Linux系统编程、网络编程等内容。🎯每天努力一点点,技术变化看得见。

Jammingpro 1231

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

27,28,29进程通信和匿名管道详解

从现在开始就开始用ubuntu和vscode了。

keyipatience的博客 292

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

【Linux系统】进程间通信:匿名管道

1.1 什么是进程间通信进程间通信(Inter-Process Communication, IPC)是指不同进程之间进行数据交换或信息共享的机制。由于每个进程拥有独立的地址空间和资源(如内存、文件描述符),操作系统通过内核提供的特殊方法实现进程间的数据传递。核心原因:进程的独立性导致其无法直接访问彼此的数据(例如,一个进程的全局变量对另一个进程不可见)。实现原理:内核作为中介,开辟一块缓冲区(如管道、共享内存),进程将数据从用户空间拷贝到内核缓冲区,再由目标进程读取。本质。

郭小明的专栏 17

【Linux进程间通信】之匿名管道

当两个进程进行通信时,如果发送方发送的太快,以致于管道缓冲区被写满了,发送方就会阻塞住,或者接收方接收的太快,此时管道中还没有数据,它也会阻塞。这种机制确保了数据在发送方和接收方之间的有序传输,并防止了数据竞争或丢失。

wszhj_的博客 1062

Linux---进程间通信之匿名管道

管道通信既不是system V标准也不是prosix标准,我们说不同的通信方式本质上就是不同的模块提供的公共空间,所以对于管道通信来说就是文件系统给两个进程提供的公共空间。在现实生活中也有许多管道,例如:水管,输油管等。他们一般都会有一个入口和一个出口,用来传输人们所需要的资源。而我们计算机中的管道传输的是数据,也有一个入口和一个出口,对应的就是发送数据的进程和接收数据的进程。

weixin_73919606的博客 1194

【Linux】匿名管道与命名管道,进程池的简易实现

功能:创建一无名管道原型参数:fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端返回值:成功返回0,失败返回错误代码。

m0_74774759的博客 955

【Linux进程通信】匿名管道

管道通信的原理是通过操作系统内核维护的缓冲区,在一个进程写入数据时,将这些数据存储在缓冲区中,然后另一个进程可以从缓冲区中读取这些数据。通常,父进程创建管道并拥有它的写端,而子进程继承了管道的读端,这允许父子进程之间协同工作。这个调用非常简单,它的目的是创建一个用于进程间通信的通道,其中一个进程可以将数据写入通道,而另一个进程可以从通道中读取数据。进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另 一个进程的所有陷入和异常,并能够及时知道它的状态改变。

m0_61476090的博客 330

Linux——进程间通信——管道与共享内存

文章目录进程间通信是什么?一、管道特性本质原理匿名管道匿名管道的模拟实现命名管道总结:管道到底是什么?二、共享内存:用来实现进程间的数据共享本质就是一块物理内存操作接口:共享内存总结 进程间通信是什么? 操作系统为用户提供的用于实现进程之间进行通信的方式。 Notes:进程之间是无法直接通信的,因为每一个进程都有自己独立的虚拟地址空间,所以进程具有独立性,无法直接通信。 进程间通信方式种类:管道、共享内存、消息队列、信号量 一、管道 特性 半双工通信(可以选择方向的单向通信) 管道的生命周期随进程的,进程

xhuyang111的博客 925

进程通信——匿名管道

管道是 SylixOS 进程间通信的一种方式。 管道有两个端口:读端和写端,并且只允许数据从写端流向读端,所以管道是一种流式设备。 管道分为匿名管道 pipe 和命名管道 fifo。 创建一个匿名管道使用 pipe 函数,pipe 函数的输出参数为两个文件描述符:一个为读端文件描述符,一个为写端文件描述符。创建匿名管道后通常使用 posix_spawn 族或 spawn族函数创建一个子进程,由于子进程继承了父进程的文件描述符,所以子进程和父进程均能使用 read 和 write 函数对匿名管道进行

动感超人_Crush 551

【Linux进程间通信】Linux匿名管道详解:构建进程间通信的隐形桥梁

当提及Linux系统中的进程间通信(IPC),管道(Pipes)无疑是最基础且广泛使用的一种机制。作为匿名通信的典范,管道为进程间数据交换提供了一个简单而有效的途径。在这个信息飞速传递的时代,掌握Linux管道的使用不仅是理解操作系统底层通信原理的关键一步,也是提升软件开发效率、构建复杂应用系统的必备技能

EterNity_TiMe_的计算机学习之路 4088

Linux进程间通信【匿名管道】

进程间通信简称为IPC(Interprocess communication),是两个不同进程间进行任务协同的必要基础。进行通信时,首先需要确保不同进程之间构建联系,其次再根据不同的使用场景选择不同的通信解决方案,本文主要介绍的通信解决方案为匿名管道

北 海的博客 1万+

Linux进程间通信之匿名管道

进程间通信介绍 1. 进程间通信的目的 2. 进程间通信发展 什么是管道 匿名管道 1. 什么是匿名管道 2. 匿名管道样例详解 3. 原理 4. 单个父进程与多个子进程管道间通信 建立子进程任务相关的头文件 主文件的建立 执行结果 5. 管道读写规则 6. 管道特点

立志做一个高产且细节博主 2145
上一篇: wave 采集总结
下一篇: 进程间通信详解 - 命名管道实现
雪岢奇
博客等级 码龄17年 310粉丝 60原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值