本文参考communicate with unix sockets做了一些简单的测试,了解unix socket是如何通信的。

unix socket是常见的一种本地进程通信的方式(IPC), 其他的还有 shared memory, eventfd。

创建一个 STREAM socket

创建一个unix socket,作为server端,命令不会返回,会一直等待:

# nc -U /tmp/demo.sock -l   
## nc, which is short for netcat. 
##-l listen
## -U, --unixsock             Use Unix domain sockets only
##     --vsock                Use vsock sockets only

查看这个socket的类型是STREAM,以及它的状态是listen。关于tcp和udp的socket的区别,不同于网络里面的tcp和udp,参考difference-between-unix-domain-stream-and-datagram-sockets以及communicate-with-unix-sockets。 还有FD,文件描述符,当linux进程要做I/O操作时,它需要通过读写文件描述符来完成操作。常见的例子有stdin, stdout, and stderr,它们分别map到文件描述符为 0, 1, 和 2.

# lsof /tmp/demo.sock
##the lsof command is a utility used to list and give information about files that are in use by processes.
COMMAND    PID USER   FD   TYPE             DEVICE SIZE/OFF    NODE NAME
nc      644501 root    3u  unix 0xffff9b20795e2200      0t0 6951183 /tmp/demo.sock type=STREAM (LISTEN)

Network Sockets using SOCK_STREAM will use TCP, while those using SOCK_DGRAM will use UDP. Since UDP is unreliable by definition, any process that requires reliable data transfer over a network socket should use a network socket of type SOCK_STREAM. However, when it comes to Unix Sockets, both types are reliable. The difference between the two types for Unix Sockets is that the SOCK_DGRAM type preserves message boundaries but is connectionless. In contrast, the SOCK_STREAM type does not preserve message boundaries but is connection-oriented.

第二个终端用nc作为客户端,与这个socket通信:

# nc -U /tmp/demo.sock
test

这两个终端就会像管道一样,一端写回车以后,另一端会同步显示,两边的内容总是一样的,都可以写,都可以收到。此时,第三个终端查看现在的状态已经是connected:

# lsof /tmp/demo.sock
COMMAND    PID USER   FD   TYPE             DEVICE SIZE/OFF    NODE NAME
nc      644501 root    4u  unix 0xffff9b20791eee80      0t0 6951184 /tmp/demo.sock type=STREAM (CONNECTED)

创建一个udp的socket:

# nc -U /tmp/demo.socku  -u -l
## -u代表udp

第二个终端查看,是DGRAM类型的,状态是unconnected:

# lsof /tmp/demo.socku
COMMAND   PID USER   FD   TYPE             DEVICE SIZE/OFF    NODE NAME
nc      36400 root    3u  unix 0xffff9441bcb54c80      0t0 3613087 /tmp/demo.socku type=DGRAM (UNCONNECTED)

第二个终端,使用这个socket开始传输一些数据,跟tcp的一样。

第三个终端,查看socket状态是connected

# lsof /tmp/demo.socku
COMMAND    PID USER   FD   TYPE             DEVICE SIZE/OFF    NODE NAME
nc      644563 root    3u  unix 0xffff9b2068958cc0      0t0 6951191 /tmp/demo.socku type=DGRAM (CONNECTED)

当server 端kill以后,继续输入内容,tcp:

# nc -U /tmp/demo.sock
test
you
sdfsdfsdf
sdfsdf
Ncat: Broken pipe.

当server端kill以后,继续输入内容,udp:

# nc -u  /tmp/demo.socku -U
ste
sdf
Ncat: Connection refused.

和internet socket 比较:

  • unix socket以文件名命令,而internet socket 是用ip和端口号;
  • unix socket本机内进程之间通信,internet socket既可以本机通信也可以跨主机;
  • 本机通信的话,用unix socket 效率更高,不用走TCP/IP协议栈,而且总是可靠传输;
  • 底层的 API 非常相似;

启动一个虚拟机,并查看由qemu-kvm进程启动的unix socket有哪些:

# lsof -U | grep qemu-kvm
qemu-kvm  651492           qemu   12u  unix 0xffff9b18fb4f2ec0      0t0 7275481 type=STREAM (CONNECTED)
qemu-kvm  651492           qemu   22u  unix 0xffff9b18d3031540      0t0 7297553 /var/lib/libvirt/qemu/channel/target/domain-1-rhel/org.qemu.guest_agent.0 type=STREAM (LISTEN)
qemu-kvm  651492           qemu   23u  unix 0xffff9b18d3036e80      0t0 7297554 /var/lib/libvirt/qemu/domain-1-rhel/monitor.sock type=STREAM (LISTEN)
qemu-kvm  651492           qemu   27u  unix 0xffff9b18fb4f5500      0t0 7432217 type=STREAM (CONNECTED)
qemu-kvm  651492           qemu   31u  unix 0xffff9b18d3036600      0t0 7339139 /var/lib/libvirt/qemu/domain-1-rhel/monitor.sock type=STREAM (CONNECTED)
qemu-kvm  651492           qemu  110u  unix 0xffff9b207acfaec0      0t0 7080806 /var/lib/libvirt/qemu/channel/target/domain-1-rhel/org.qemu.guest_agent.0 type=STREAM (CONNECTED)

这里出现了guest agent以及qemu mointor.sock, 但是为什么出现多次?qemu命令行如下:

-chardev socket,id=charmonitor,fd=23,server=on,wait=off \
-mon chardev=charmonitor,id=monitor,mode=control 

-chardev socket,id=charchannel0,fd=22,server=on,wait=off \
-device '{"driver":"virtserialport","bus":"virtio-serial0.0","nr":1,"chardev":"charchannel0","id":"channel0","name":"org.qemu.guest_agent.0"}' \

-chardev socket,id=chrtpm,path=/run/libvirt/qemu/swtpm/1-rhel-swtpm.sock \
-tpmdev emulator,id=tpm-tpm0,chardev=chrtpm \

其中最后的tpm相关的socket是tss进程关联的,为什么也是有两个?是为了有多个连接吗?

# lsof /run/libvirt/qemu/swtpm/1-rhel-swtpm.sock
COMMAND    PID USER   FD   TYPE             DEVICE SIZE/OFF    NODE NAME
swtpm   651475  tss    4u  unix 0xffff9b18cd499dc0      0t0 6999418 /run/libvirt/qemu/swtpm/1-rhel-swtpm.sock type=STREAM (LISTEN)
swtpm   651475  tss    8u  unix 0xffff9b18fb4f0440      0t0 6999420 /run/libvirt/qemu/swtpm/1-rhel-swtpm.sock type=STREAM (CONNECTED)