命名管道

命名管道是電腦進程間的一種先進先出通信機制。是類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