命名管道

命名管道是计算机进程间的一种先进先出通信机制。是类Unix系统传统管道的扩展。传统管道属于匿名管道，其生存期不超过创建管道的进程的生存期。但命名管道的生存期可以与操作系统运行期一样长。

## Unix

与传统的无名的shell管道不同，命名管道利用了文件系统。使用`mkfifo()`或`mknod()`创建命名管道。两个进程可以通过管道的名字打开、读写管道。

例如，可以创建管道，让gzip压缩管道传给它的数据：

```bash
 mkfifo my_pipe
 gzip -9 -c < my_pipe > out.gz &
```

在另一个进程shell中，独立地发送数据给管道以被压缩：

```
cat file > my_pipe
```

命名管道可如普通文件那样被删除：

```
rm my_pipe
```

命名管道可用于从一个进程向另一个进程发送信息而不需使用中间临时文件。例如，gzip解压缩数据可以输出到一个命名管道中：

```bash
 mkfifo -m 0666 /tmp/namedPipe
 gzip -d < file.gz > /tmp/namedPipe
```

然后把解压缩数据存入MySQL的表中:

```mysql
 LOAD DATA INFILE '/tmp/namedPipe' INTO TABLE tableName;
```

如果不使用管道，就需要把解压缩数据完整地保存在文件中以上传到MySQL。

PostgreSQL的命令行工具`psql`也支持从命名管道装入数据。

## Windows操作系统

命名管道可以类似于文件那样，使用Win32 SDK函数`CreateFile`, `ReadFile`, `WriteFile`, `CloseHandle`打开、读写、关闭。

与Unix不同，在Windows的命令行界面上不能使用命名管道，除了PowerShell终端模拟器。另一点不同是Windows命名管道是易失的，如果对命名管道的引用为0就会自动被关闭。第三点不同是Windows命名管道被安装在命名管道文件系统（named pipe filesystem，NPFS），安装路径是`\\.\pipe\`。例如管道名字"foo"的全路径名是`\\.\pipe\foo`)。

匿名管道实际上是采用随机名字的命名管道。

命名管道可继承安全上下文；可跨计算机做进程间通信；可全双工或半双工通信；可以是面向字节协议或分组交换；可靠通信；阻塞或非阻塞读写；标准设备I/O句柄；可用名字来创建句柄；广域网通信效率低（与TCP/IP滑动窗口相比，使用明确的数据请求）；可窥探式读（读数据但不从输入缓冲区中删除）。

命名管道可以同时创建多个实例，只要不超过nMaxInstances。一个实例只能连接一个客户端，若想连接其它的客户端，需要先调用DisconnectNamedPipe关闭现有的管道连接。另外创建命名对象时可以为参数dwOpenMode指定FILE_FLAG_FIRST_PIPE_INSTANCE，指定这个参数后，若创建的命名管道对象不是第一个实例，会报错ERROR_ACCESS_DENIED。

.NET Framework 3.5增加了命名管道支持。命名管道可用作Microsoft SQL Server.的客户端点（endpoint）。

命名管道也是服务器讯息区块(SMB)支持的一种网络协议。Windows NT的整个NT Domain服务协议是基于命名管道之上的DCE/RPC服务。Exchange 5.5 Administrative应用也是如此。

### API

- CreateNamedPipe：创建命名管道
- ConnectNamedPipe：服务器端等待客户端连接命名管道。若调用此函数之前，客户端已经调用CreateFile连接到了此实例，ConnectNamedPipe返回FALSE，并且GetLastError返回ERROR_PIPE_CONNECTED，这是一个正常的连接，虽然函数返回了FALSE。
- WaitNamedPipe：客户端阻塞式连接命名管道。如果
- DisconnectNamedPipe：服务器端强制关闭命名管道的客户端；客户进程仍然需要调用CloseHandle。服务器端可以进一步再与其他客户端连接
- PeekNamedPipe：获取但不删除输入缓冲区的数据
- SetNamedPipeHandleState
- GetNamedPipeInfo
- CallNamedPipe：连接命名管道，读、写各一次，然后断连命名管道
- TransactNamedPipe：典型用于客户端，完成读、写各一次。
- GetNamedPipeClientComputerName：获得客户端的计算机名字
- GetNamedPipeClientProcessId：获得客户端进程ID
- GetNamedPipeClientSessionId：获得客户端会话ID
- GetNamedPipeServerProcessId
- GetNamedPipeServerSessionId
- ImpersonateNamedPipeClient：服务端接受客户端连接后，必须接收到客户端的消息才能模拟客户端安全上下文

### 例子

```cpp
    //命名管道服务器
    #include <windows.h>  
    #include <stdio.h>  
      
    int main(void)  
    {  
        HANDLE PipeHandle;  
        DWORD BytesRead;  
        CHAR buffer[256];  
      
        if ((PipeHandle = CreateNamedPipe("\\\\.\\Pipe\\Jerry",  
                                           PIPE_ACCESS_DUPLEX, //open mode  
                                           PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, //  pipe mode 
                                           1,//Num. of MaxInstances: between 1 and PIPE_UNLIMITED_INSTANCES  
                                           0, // out buffer size  
                                           0, // in buffer size  
                                           1000, //timeout  
                                           NULL //Security descriptor  
             )) == INVALID_HANDLE_VALUE)  
        {  
            printf("CreateNamedPipe failed with error %d\n",  
                GetLastError());  
            return 0;  
        }  
      
        printf("Server is now running\n");  
      
        if (ConnectNamedPipe(PipeHandle, NULL) == 0)  
        {  
            printf("ConnectNamedPipe failed with error %d\n", GetLastError());  
            CloseHandle(PipeHandle);  
            return 0;  
        }  
      
        if (ReadFile(PipeHandle, buffer, sizeof(buffer), &BytesRead,  NULL) <= 0)  
        {  
            printf("ReadFile failed with error %d\n", GetLastError());  
            CloseHandle(PipeHandle);  
            return 0;  
        }  
      
        printf("%.*s\n", BytesRead, buffer);  
      
        if (DisconnectNamedPipe(PipeHandle) == 0)  
        {  
            printf("DisconnectNamedPipe failed with error %d\n",  
                GetLastError());  
            return 0;  
        }  
      
        CloseHandle(PipeHandle);  
        return 0;  
    }  
```

```cpp
    //客户程序
    #include <windows.h>  
    #include <stdio.h>  
      
    #define PIPE_NAME "\\\\.\\Pipe\\Jerry"  
      
    void main(void) {  
      
        HANDLE PipeHandle;  
        DWORD BytesWritten;  
      
        if (WaitNamedPipe(PIPE_NAME, NMPWAIT_WAIT_FOREVER) == 0)  
        {  
            printf("WaitNamedPipe failed with error %d\n",  
                GetLastError());  
            return;  
        }  
      
        // Open the named pipe file handle  
        if ((PipeHandle = CreateFile(PIPE_NAME,  
            GENERIC_READ | GENERIC_WRITE, 0,  
            (LPSECURITY_ATTRIBUTES) NULL, OPEN_EXISTING,  
            FILE_ATTRIBUTE_NORMAL,  
            (HANDLE) NULL)) == INVALID_HANDLE_VALUE)  
        {  
            printf("CreateFile failed with error %d\n", GetLastError());  
            return;  
        }  
      
        if (WriteFile(PipeHandle, "This is a test", 14, &BytesWritten,   
            NULL) == 0)  
        {  
            printf("WriteFile failed with error %d\n", GetLastError());  
            CloseHandle(PipeHandle);  
            return;  
        }  
      
        printf("Wrote %d bytes", BytesWritten);  
      
        CloseHandle(PipeHandle);  
    }  
```

## 外部链接

- The Linux Programmer's Guide: Named Pipes
- mkfifo
- Linux Journal: Introduction to Named Pipes
- MSDN Library: Named Pipes