pipe基本使用

一、基本函数介绍
BOOL CreatePipe( //创建管道
PHANDLE hReadPipe, //管道读数据句柄
PHANDLE hWritePipe, //管道写数据句柄
PLSECURITY_ATTRIBUTES lpPipeAttributes, //安全属性,成员bInhertHandle表示子进程是否可继承
DWORD nSize //管道缓冲区大小,0代表默认缓冲区代销
)
安全属性结构体如下:
typedef struct _SECURITY_ATTRBUTES{
DWORD nLength;
LPVOID lpSecurityDescriptor;
BOOL bInhertHandle;
}SECURITY_ATTRBUTES,*PLSECURITY_ATTRIBUTES;

BOOL CreateProcess //创建进程
(
LPCTSTR lpApplicationName,//用来指定可执行模块的字符串
LPTSTR lpCommandLine,//该字符串指定要执行的命令行
LPSECURITY_ATTRIBUTES lpProcessAttributes,//安全属性,NULL表示默认
LPSECURITY_ATTRIBUTES lpThreadAttributes,//线程是否被继承.通常置为NULL
BOOL bInheritHandles,//新进程是否从调用进程处继承了句柄
DWORD dwCreationFlags,//指定附加的、用来控制优先类和进程的创建的标志
LPVOID lpEnvironment,//指向一个新进程的环境块。如果此参数为空,新进程使用调用进程的环境
LPCTSTR lpCurrentDirectory,//这个字符串用来指定子进程的工作路径
LPSTARTUPINFO lpStartupInfo,//指向一个用于决定新进程的主窗体如何显示的STARTUPINFO结构体
LPPROCESS_INFORMATIONlpProcessInformation//指向一个用于决定新进程的主窗体如何显示的STARTUPINFO结构体
);

typedef struct _STARTUPINFO {
DWORD cb;
LPTSTR lpReserved;
LPTSTR lpDesktop;
LPTSTR 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;
} STARTUPINFO, *LPSTARTUPINFO;

typedef struct_PROCESS_INFORMATION{
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
}PROCESS_INFORMATION;

二、匿名管道:只用于父子进程
创建匿名管道

SECURITY_ATTRIBUTES sa;
    sa.bInheritHandle = TRUE;
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.lpSecurityDescriptor = NULL;
    if (!CreatePipe(&hRead, &hWrite, &sa, 0))
    {
        MessageBox(L"创建匿名管道失败");
        return;
    }

    STARTUPINFO sui;
    ZeroMemory(&sui, sizeof(STARTUPINFO));
    sui.cb = sizeof(STARTUPINFO);
    sui.dwFlags = STARTF_USESTDHANDLES;
    sui.hStdInput = hRead;
    sui.hStdOutput = hWrite;
    sui.hStdError = GetStdHandle(STD_ERROR_HANDLE);

    PROCESS_INFORMATION pi;
    if (!CreateProcess(L"..\\Debug\\PipeChild.exe", NULL, NULL
        , NULL, TRUE, 0, NULL, NULL, &sui, &pi))
    {
        CloseHandle(hRead);
        CloseHandle(hWrite);
        hRead = NULL;
        hWrite = NULL;

        CString strTips;
        strTips.Format(L"%d", GetLastError());
        MessageBox(L"创建子进程失败");
        MessageBox(strTips);
        return;
    }
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);

读取数据

char szBuf[100] = { 0 };
    DWORD dwRead;
    if (!ReadFile(hRead, szBuf, 100, &dwRead, NULL))
    {
        MessageBox(L"读取数据失败");
        return;
    }
    CString strData(szBuf);
    MessageBox(strData);

写入数据

char szData[] = "parent:this is a test";
    DWORD dwWrite;
    if (!WriteFile(hWrite, szData, strlen(szData) + 1, &dwWrite, NULL))
    {
        MessageBox(L"写入数据失败");
        return;
    }

Tips:在子进程需要获取父进程创建时指定的管道操作句柄

三、命名管道
创建管道

hPipe = CreateNamedPipe(L"\\\\.\\pipe\\MyPipe", PIPE_ACCESS_DUPLEX
        | FILE_FLAG_OVERLAPPED, 0, 1, 1024, 1024, 0, NULL);
    if (INVALID_HANDLE_VALUE == hPipe)
    {
        MessageBox(L"创建命名管道失败");
        hPipe = nullptr;
        return;
    }
    //创建那么人工重置事件对象
    HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (NULL == hEvent)
    {
        MessageBox(L"创建事件对象失败");
        CloseHandle(hPipe);
        hPipe = nullptr;
        return;
    }

    OVERLAPPED ovlap;
    ZeroMemory(&ovlap, 0, sizeof(OVERLAPPED));
    ovlap.hEvent = hEvent;
    if (!ConnectNamedPipe(hPipe, &ovlap))
    {
        if (ERROR_IO_PENDING != GetLastError())
        {
            MessageBox(L"等待客户端连接失败");
            CloseHandle(hPipe);
            hPipe = nullptr;
            CloseHandle(hEvent);
            return;
        }
    }
    if (WAIT_FAILED == WaitForSingleObject(hEvent, INFINITE))
    {
        MessageBox(L"等待对象失败");
        CloseHandle(hPipe);
        hPipe = nullptr;
        CloseHandle(hEvent);
        return;
    }
    CloseHandle(hEvent);

连接管道

if (!WaitNamedPipe(L"\\\\.\\pipe\\MyPipe", NMPWAIT_WAIT_FOREVER))
    {
        MessageBox(L"当前无可使用的命名管道");
        return;
    }
    hPipe = CreateFile(L"\\\\.\\pipe\\MyPipe", GENERIC_READ
        | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

    if (INVALID_HANDLE_VALUE == hPipe)
    {
        MessageBox(L"打开命名管道失败");
        hPipe = nullptr;
        return;
    }

管道读写和匿名管道保持一致

<think> 我们被要求提供pipe使用示例。根据引用,pipe在不同语言和环境中用法不同。用户之前的问题是关于SDF_InternalSign_ECC的,但当前问题转向了pipe,因此我们需要提供pipe的示例。 引用[1]是Java NIO Pipe的示例,引用[2]是Linux C语言中pipe函数的示例,引用[3]是C#命名管道的示例。 用户没有指定具体语言,所以我们提供多种常见语言的pipe使用示例。 注意:根据指令,我们需要按照要求使用LaTeX格式,但pipe示例代码中一般不含数学公式,所以主要注意代码块和引用。 同时,我们需要在每个引用处添加引用标识。 我们将分为三个示例:Java NIO Pipe、Linux C的pipe函数、C#命名管道。 注意:用户的问题描述中引用了三个示例,因此我们分别展示这三种。 步骤: 1. 介绍每种pipe基本概念。 2. 提供代码示例。 3. 简要解释代码。 最后,生成相关问题。 </think> 以下是不同编程语言中管道(`pipe`)的使用示例,涵盖进程间通信和线程间数据传输场景: ### 一、Java NIO 管道(线程间通信) ```java import java.nio.ByteBuffer; import java.nio.channels.Pipe; public class JavaPipeDemo { public static void main(String[] args) throws Exception { // 1. 创建管道 Pipe pipe = Pipe.open(); // 创建双向通道[^1] // 2. 写入数据线程 new Thread(() -> { try (Pipe.SinkChannel sink = pipe.sink()) { ByteBuffer buf = ByteBuffer.allocate(128); buf.put("Hello from Java Pipe!".getBytes()); buf.flip(); // 切换读写模式 while (buf.hasRemaining()) sink.write(buf); } catch (Exception e) { e.printStackTrace(); } }).start(); // 3. 读取数据线程 new Thread(() -> { try (Pipe.SourceChannel source = pipe.source()) { ByteBuffer rxBuf = ByteBuffer.allocate(128); source.read(rxBuf); rxBuf.flip(); byte[] bytes = new byte[rxBuf.remaining()]; rxBuf.get(bytes); System.out.println("Received: " + new String(bytes)); } catch (Exception e) { e.printStackTrace(); } }).start(); } } ``` **执行流程**: 1. `Pipe.open()`创建双向通道 2. `SinkChannel`写入数据(需手动`flip()`切换缓冲区模式) 3. `SourceChannel`读取数据 **特点**:线程安全,适用于同一个JVM内的线程通信[^1] --- ### 二、Linux C 管道(进程间通信) ```c #include <stdio.h> #include <unistd.h> int main() { int fd[2]; // 文件描述符数组 pipe(fd); // fd[0]读端, fd[1]写端 if (fork() == 0) { // 子进程 close(fd[0]); // 关闭读端 dup2(fd[1], STDOUT_FILENO); // 重定向标准输出到管道 execlp("ls", "ls", "-l", NULL); // 执行命令 } else { // 父进程 close(fd[1]); // 关闭写端 char buffer[512]; ssize_t count; while ((count = read(fd[0], buffer, sizeof(buffer))) > 0) write(STDOUT_FILENO, buffer, count); // 打印结果 close(fd[0]); } return 0; } ``` **执行流程**: 1. `pipe()`创建匿名管道 2. `fork()`创建子进程 3. 子进程重定向`stdout`到管道写端 4. 父进程从管道读端获取命令输出 **特点**:内核级通信,最大传输量受`PIPE_BUF`限制(通常4KB)[^2] --- ### 三、C# 命名管道(跨进程通信) ```csharp // 服务端 using (NamedPipeServerStream server = new NamedPipeServerStream("testpipe")) { server.WaitForConnection(); byte[] data = Encoding.UTF8.GetBytes("Server message"); server.Write(data, 0, data.Length); } // 客户端 using (NamedPipeClientStream client = new NamedPipeClientStream(".", "testpipe")) { client.Connect(); byte[] buffer = new byte[256]; int count = client.Read(buffer, 0, buffer.Length); Console.WriteLine(Encoding.UTF8.GetString(buffer, 0, count)); } ``` **关键参数**: - `"."`:表示本地主机 - `"testpipe"`:管道标识符(需两端一致) - `PipeDirection.InOut`:双向通信模式 **特点**:支持网络通信,通过名称标识管道[^3] --- ### 管道通信对比 | 类型 | 适用场景 | 通信范围 | 特点 | |---------------|--------------------------|----------------|---------------------------| | Java NIO Pipe | 线程间通信 | 同一JVM进程内 | 无需序列化,零拷贝 | | Linux Pipe | 父子进程通信 | 同一主机 | 内核缓冲区,单向数据流 | | 命名管道 | 任意进程间通信 | 跨主机/跨进程 | 持久化管道,支持双向通信 | > $\text{管道传输效率公式:} \eta = \frac{\text{有效数据量}}{\text{缓冲区大小}} \times \frac{1}{\text{切换次数}}$ > 其中缓冲区大小需满足 $B \geq \text{PIPE\_BUF}$ 避免数据分片[^2]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值