Linux下的FIFO、pipe、unix domain socket漫谈

在做Linux开发时,经常会接触 管道AF_UNIX 等相关词汇,为了弄清他们之间的关系,查阅了一些资料,将结果整理并记录下来。

1. FIFOpipe 的比较

FIFO可以通过 mkfifo <fifo_file> 在本地创建一个文件用来表示一个管道,数据的交换是在操作系统内核完成的,所以在该文件中没有内容。
FIFO可以看做是一个带有名字的管道,我们通常在命令行中使用的管道(符号 | )是匿名管道。

A FIFO special file (a named pipe) is similar to a pipe, except that it is accessed as part of the file system. It can be opened by multiple processes for reading or writing. When processes are exchanging data via the FIFO, the kernel passes all data internally without writing it to the file system. Thus, the FIFO special file has no contents on the file system; the file system entry merely serves as a reference point so that processes can access the pipe using a name in the file system.

以上内容截取自Linux帮助文档( 在Linux的terminal中输入 man fifo 查看)

2. FIFOunix domain socket 的比较

FIFO与unix domain socket在他们的实现方式上有相似之处,但它们是完全不同的两个概念。
FIFO是更底层的进程间通信方式,允许一端写入数据,另外一端读取数据;unix domain socket与 TCP/IP 的套接字很相似。
socket是双工的,支持多个进程同时访问,一个进程的socket允许多个客户端进程同时接入。在每次有新的 connectaccept 时, 操作系统内核会分配一个新的文件描述符,通信数据包会送达正确的进程。

UNIX domain sockets and FIFO may share some part of their implementation but they are conceptually very different. FIFO functions at a very low level. One process writes bytes into the pipe and another one reads from it. A UNIX domain socket has the same behaviour as a TCP/IP socket.

A socket is bidirectional and can be used by a lot of processes simultaneously. A process can accept many connections on the same socket and attend several clients simultaneously. The kernel delivers a new file descriptor each time connect(2) or accept(2) is called on the socket. The packets will always go to the right process.
On a FIFO, this would be impossible. For bidirectional comunication, you need two FIFOs, and you need a pair of FIFOs for each of your clients. There is no way of writing or reading in a selective way, because they are a much more primitive way to communicate.

Anonymous pipes and FIFOs are very similar. The difference is that anonymous pipes don't exist as files on the filesystem so no process can open(2) it. They are used by processes that share them by another method. If a process opens a FIFOs and then performs, for example, a fork(2), its child will inherit its file descriptors and, among them, the pipe.

The UNIX domain sockets, anonymous pipes and FIFOs are similar in the fact they use shared memory segments. The details of implementation may vary from one system to another but the idea is always the same: attach the same portion of memory in two distinct processes memory mapping to have them sharing data
(edit: that would one obvious way to implement it but that is not how it is actually done in Linux, which simply uses the kernel memory for the buffers, see answer by @tjb63 below).
The kernel then handles the system calls and abstracts the mechanism.

以上内容截取自 unix stackexchange

3. unix-domain-socketip socket 的比较

unix domain socket是一种进程间通信方式,支持跑在同一个机器上的两个进程的双向数据交互。
ip socket是一种网络通信方式,能够让两个进程通过网络完成数据交互。
unix domain socket针对于文件系统限制访问权限,而ip socket在过滤包的级别完成访问控制。
unix domain socket比ip socket的效率更高,因为省去了校验检查、路由等操作步骤。

There are a few differences that might be of interest, in addition to the already pointed out difference that if you start out using IP sockets, you don't have to migrate to them later when you want inter-machine connectivity:

  • UNIX domain sockets use the file system as the address name space. This means you can use UNIX file permissions to control access to communicate with them. I.e., you can limit what other processes can connect to the daemon – maybe one user can, but the web server can't, or the like. With IP sockets, the ability to connect to your daemon is exposed off the current system, so additional steps may have to be taken for security. On the other hand, you get network transparency. With UNIX domain sockets, you can actually retrieve the credential of the process that created the remote socket, and use that for access control also, which can be quite convenient on multi-user systems.
  • IP sockets over localhost are basically looped back network on-the-wire IP. There is intentionally "no special knowledge" of the fact that the connection is to the same system, so no effort is made to bypass the normal IP stack mechanisms for performance reasons. For example, transmission over TCP will always involve two context switches to get to the remote socket, as you have to switch through the netisr, which occurs following the "loopback" of the packet through the synthetic loopback interface. Likewise, you get all the overhead of ACKs, TCP flow control, encapsulation/decapsulation, etc. Routing will be performed in order to decide if the packets go to the localhost. Large sends will have to be broken down into MTU-size datagrams, which also adds overhead for large writes. It's really TCP, it just goes over a loopback interface by virtue of a special address, or discovering that the address requested is served locally rather than over an ethernet (etc).
  • UNIX domain sockets have explicit knowledge that they're executing on the same system. They avoid the extra context switch through the netisr, and a sending thread will write the stream or datagrams directly into the receiving socket buffer. No checksums are calculated, no headers are inserted, no routing is performed, etc. Because they have access to the remote socket buffer, they can also directly provide feedback to the sender when it is filling, or more importantly, emptying, rather than having the added overhead of explicit acknowledgement and window changes. The one piece of functionality that UNIX domain sockets don't provide that TCP does is out-of-band data. In practice, this is an issue for almost noone.

In general, the argument for implementing over TCP is that it gives you location independence and immediate portability – you can move the client or the daemon, update an address, and it will "just work". The sockets layer provides a reasonable abstraction of communications services, so it's not hard to write an application so that the connection/binding portion knows about TCP and UNIX domain sockets, and all the rest just uses the socket it's given. So if you're looking for performance locally, I think UNIX domain sockets probably best meet your need. Many people will code to TCP anyway because performance is often less critical, and the network portability benefit is substantial.

Right now, the UNIX domain socket code is covered by a subsystem lock; I have a version that used more fine-grain locking, but have not yet evaluated the performance impact of those changes. I've you're running in an SMP environment with four processors, it could be that those changes might positively impact performance, so if you'd like the patches, let me know. Right now they're on my schedule to start testing, but not on the path for inclusion in FreeBSD 5.4. The primary benefit of greater granularity would be if you had many pairs of threads/processes communicating across processors using UNIX domain sockets, and as a result there was substantial contention on the UNIX domain socket subsystem lock. The patches don't increase the cost of normal send/receive operations, but due add extra mutex operations in the listen/accept/connect/bind paths.

Robert N M Watson

以上内容摘抄自 freebsd lists


Comments powered by Disqus