Windows的處理序間通訊(三)

來源:互聯網
上載者:User

轉自:http://hi.baidu.com/_achillis/blog/item/bdaa0d943d4b130e7bf48080.html

5. 具名管道(Named Pipe)和信箱(Mail Slot)

前面提到,如果從字面上理解,那麼處理序間通訊也可以通過磁碟檔案而實現。但是,把資訊寫入某個磁碟檔案,再由另一個進程從磁碟檔案讀出,在速度上是很慢的。固然,由於檔案緩衝區(Cache)的存在,對磁碟檔案的寫和讀未必都經過磁碟,但是那並沒有保證。再說,普通的檔案操作也沒有提供進程間同步的手段。所以通過普通的磁碟檔案實現處理序間通訊是不太現實的。但是這也提示我們,如果能實現一種特殊檔案,使得對檔案的讀寫只在緩衝區中進行(而不寫入磁碟),並且實現進程間的同步,那倒是個不壞的主意。具名管道就是這樣一種特殊檔案。實際上,具名管道還不僅是這樣的特殊檔案,它還是一種網路通訊的機制,只是當通訊的雙方存在於同一台機器上時,才落入本文所說的處理序間通訊的範疇。
既然具名管道是一種特殊檔案,它的建立、開啟、讀寫等等操作就基本上都可以利用檔案系統中的有關資源加以實現。當然,這畢竟是一種特殊檔案,對於使用者來說,最大的特殊之處在於這是一個“先進先出”的位元組流,不能對其執行lseek()一類的操作。
先看具名管道的建立,Windows的Win32 API上提供了一對庫函數CreateNamedPipeA()和CreateNamedPipeW(),前者用於ASCII碼字串,後者用於“寬字元”即Unicode的字串,實際上前者只是把8位字元轉換成Unicode以後再調用後者。對CreateNamedPipeW()的調用大致如下:

[code] Handle = CreateNamedPipeW(L"[A]\\\\.\\pipe\\MyControlPipe[/url]",
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
. . . . . .);[/code]

這裡的管道名實際上是“\\.\pipe\MyControlPipe”,前面的“\\.”表示本機,而“\pipe\MyControlPipe”則是路徑名。如果把“.”換成主機名稱,那就可以是在另一台機器上的對象。參數PIPE_ACCESS_DUPLEX表示要建立的是“雙工”、即可以雙向通訊的管道。另一個參數是一些標誌位,表示要建立的管道採用“報文(message)”的方式、而不是位元組流的方式讀寫,並且是阻塞方式、即有等待的讀寫。
CreateNamedPipeW()內部通過系統調用NtCreateNamedPipeFile()完成具名管道的建立,這是Windows核心為具名管道提供的唯一的系統調用:

[code]NTSTATUS STDCALL
NtCreateNamedPipeFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock,
ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions,
ULONG NamedPipeType, ULONG ReadMode, ULONG CompletionMode,
ULONG MaximumInstances, ULONG InboundQuota, ULONG OutboundQuota,
PLARGE_INTEGER DefaultTimeout)
{
NAMED_PIPE_CREATE_PARAMETERS Buffer;

. . . . . .
if (DefaultTimeout != NULL)
{
Buffer.DefaultTimeout.QuadPart = DefaultTimeout->QuadPart;
Buffer.TimeoutSpecified = TRUE;
}
else
{
Buffer.TimeoutSpecified = FALSE;
}
Buffer.NamedPipeType = NamedPipeType;
Buffer.ReadMode = ReadMode;
Buffer.CompletionMode = CompletionMode;
Buffer.MaximumInstances = MaximumInstances;
Buffer.InboundQuota = InboundQuota;
Buffer.OutboundQuota = OutboundQuota;

return IoCreateFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock,
NULL, FILE_ATTRIBUTE_NORMAL, ShareAccess, CreateDisposition,
CreateOptions, NULL, 0, CreateFileTypeNamedPipe, (PVOID)&Buffer,
0);
}[/code]
在Windows中,檔案系統是I/O子系統的一部分,檔案的建立是由IoCreateFile()完成的,參數CreateFileTypeNamedPipe是個檔案類型代碼,它決定了所建立的是作為特殊檔案的具名管道。至於IoCreateFile()如何建立此種特殊檔案,那已經不在本文要討論的範圍內。
一般,具名管道是由“服務端”線程建立的,建立以後還要對其調用一個Win32 API庫函數ConnectNamedPipe(),以等待“用戶端”線程開啟這個具名管道、從而使雙方建立起點對點的串連(注意這裡的Connect與Socket機制中的Connect意思完全不同)。

[code]BOOL STDCALL
ConnectNamedPipe(HANDLE hNamedPipe,
LPOVERLAPPED lpOverlapped)
{
PIO_STATUS_BLOCK IoStatusBlock;
IO_STATUS_BLOCK Iosb;
HANDLE hEvent;
NTSTATUS Status;

if (lpOverlapped != NULL)
{
lpOverlapped->Internal = STATUS_PENDING;
hEvent = lpOverlapped->hEvent;
IoStatusBlock = (PIO_STATUS_BLOCK)lpOverlapped;
}
else
{
IoStatusBlock = &Iosb;
hEvent = NULL;
}

Status = NtFsControlFile(hNamedPipe, hEvent, NULL, NULL, IoStatusBlock,
FSCTL_PIPE_LISTEN, NULL, 0, NULL, 0);
if ((lpOverlapped != NULL) && (Status == STATUS_PENDING))
return TRUE;

if ((lpOverlapped == NULL) && (Status == STATUS_PENDING))
{
Status = NtWaitForSingleObject(hNamedPipe, FALSE, NULL);
if (!NT_SUCCESS(Status))
{
SetLastErrorByStatus(Status);
return FALSE;
}
Status = Iosb.Status;
}

if ((!NT_SUCCESS(Status) && Status != STATUS_PIPE_CONNECTED) ||
(Status == STATUS_PENDING))
{
SetLastErrorByStatus(Status);
return FALSE;
}
return TRUE;
}[/code]
實際的操作是由NtFsControlFile()完成的,而NtFsControlFile()有點像Linux中的fcntl(),常常用來實現除標準的“讀”、“寫”等等以外的許多不同操作。
“用戶端”線程通過開啟檔案建立與“服務端”線程的串連,所開啟的就是作為特殊檔案的具名管道。Windows沒有特別為此提供系統調用,而是直接利用NtOpenFile(),例如:

[code] Status = NtOpenFile(&FileHandle, FILE_GENERIC_READ, &ObjectAttributes, &Iosb,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_NONALERT);[/code]
這裡使用的參數FILE_SYNCHRONOUS_IO_NONALERT表示對檔案的訪問將是同步的、也即阻塞的。要開啟的檔案名稱在資料結構ObjectAttributes中,所以這裡看不見。當然,這檔案名稱必須與“服務端”所使用的相同。由此可見,“用戶端”線程不必知道這是一個具名管道,甚至不必知道這是特殊檔案,而只把它當成一般的檔案看待。
在最簡單的情況下,這就已經夠了,下面就可以通過已經建立的串連進行通訊了。具體的操作在形式上就跟普通檔案的讀/寫一樣。在讀普通檔案時,當事線程也會被阻塞(如果要讀的內容不是已經在緩衝區中),此時它所等待的主要是磁碟完成其物理的讀出過程,而磁碟完成其讀出過程以後的中斷則會解除它的阻塞,相當於執行了一次V操作。相比之下,對於具名管道,啟動讀操作的線程也會被阻塞(如果尚無已經到達的資料或報文),而此時等待的是管道的對方線程往管道中寫資料。對方往管道中寫資料,就解除了等待方線程的阻塞,實質上構成一次V操作。所以,普通檔案的讀/寫同樣也包含了同步機制,只不過那一般是進程與外部裝置(或外部物理過程)之間的同步,而具名管道所需的是進程與進程之間的同步,但是這並沒有本質上的不同。事實上,讀者以後會看倒,在裝置驅動程式中常常要用到NtWaitForSingleObject()之類的等待機制,而且這正是裝置驅動得以實現的基礎。
一般而言,如果在管道中尚無資料的時候啟動讀操作,當事線程就會被阻塞。但是,為避免被阻塞,也可以先通過PeekNamedPipe()查詢一下是否已經有報文到達。

[code]BOOL STDCALL
PeekNamedPipe(HANDLE hNamedPipe, LPVOID lpBuffer,
DWORD nBufferSize, LPDWORD lpBytesRead,
LPDWORD lpTotalBytesAvail, LPDWORD lpBytesLeftThisMessage)
{
PFILE_PIPE_PEEK_BUFFER Buffer;
IO_STATUS_BLOCK Iosb;
ULONG BufferSize;
NTSTATUS Status;

BufferSize = nBufferSize + sizeof(FILE_PIPE_PEEK_BUFFER);
Buffer = RtlAllocateHeap(RtlGetProcessHeap(),0, BufferSize);

Status = NtFsControlFile(hNamedPipe, NULL, NULL, NULL, &Iosb,
FSCTL_PIPE_PEEK, NULL, 0, Buffer, BufferSize);
if (Status == STATUS_PENDING)
{
Status = NtWaitForSingleObject(hNamedPipe, FALSE, NULL);
if (NT_SUCCESS(Status)) Status = Iosb.Status;
}
if (Status == STATUS_BUFFER_OVERFLOW)
{
Status = STATUS_SUCCESS;
}

. . . . . .

if (lpTotalBytesAvail != NULL)
{
*lpTotalBytesAvail = Buffer->ReadDataAvailable;
}
if (lpBytesRead != NULL)
{
*lpBytesRead = Iosb.Information - sizeof(FILE_PIPE_PEEK_BUFFER);
}
if (lpBytesLeftThisMessage != NULL)
{
*lpBytesLeftThisMessage = Buffer->MessageLength -
(Iosb.Information - sizeof(FILE_PIPE_PEEK_BUFFER));
}
if (lpBuffer != NULL)
{
memcpy(lpBuffer, Buffer->Data,
min(nBufferSize, Iosb.Information - sizeof(FILE_PIPE_PEEK_BUFFER)));
}
RtlFreeHeap(RtlGetProcessHeap(),0, Buffer);

return(TRUE);
}[/code]
同樣,實際的查詢是由NtFsControlFile()完成的,只是用了另一個“命令碼”。
除具名管道外,還有“信槽(MailSlot)”也是類似的特殊檔案,只是信槽所提供的是不需連線的通訊機制。一般把這樣不需連線的通訊稱為“資料報”機制,而具名管道所提

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.