linux一切皆文件之Unix domain socket描述符(二)


一、知識准備

1、在linux中,一切皆為文件,所有不同種類的類型都被抽象成文件(比如:塊設備,socket套接字,pipe隊列)
2、操作這些不同的類型就像操作文件一樣,比如增刪改查等
3、主要用於:運行在同一台機器上的2個進程相互之間的數據通信
4、它們和網絡文件描述符非常相似(比如:TCP socket),他們的通信發生在操作系統內核


二、環境准備

組件 版本
OS CentOS Linux release 7.5.1804

三、Unix domain socket 文件描述符

先准備2個腳本:
server.py主要用於建立客戶端的連接請求,並且接收客戶端傳來的數據,然后將收到的數據回傳給客戶端
client.py每隔1秒向服務端發送一次'hello world'

server.py:

import socket

server_addr = '/tmp/server.sock'

sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.bind(server_addr)
sock.listen(0)

while True:
    conn, clientAddr = sock.accept()
    while True:
        data = conn.recv(100)
        conn.sendall(data)

client.py:

import socket
import time

server_addr = '/tmp/server.sock'

sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(server_addr)

while True:
    message = 'hello world!'
    sock.sendall(message)
    sock.recv(100)
    time.sleep(1)

sock.close()

先看下server.py的狀態:

[root@localhost ~]# python /tmp/server.py &
[1] 2554
[root@localhost ~]# ls -l /proc/2554/fd
total 0
lrwx------ 1 root root 64 Nov  5 02:39 0 -> /dev/pts/0
lrwx------ 1 root root 64 Nov  5 02:39 1 -> /dev/pts/0
lrwx------ 1 root root 64 Nov  5 02:39 2 -> /dev/pts/0
lrwx------ 1 root root 64 Nov  5 02:39 3 -> socket:[28724]
[root@localhost ~]# grep 28724 /proc/net/unix
ffff90d8ba564000: 00000002 00000000 00010000 0001 01 28724 /tmp/server.sock
[root@localhost ~]# lsof -n | grep 28724
python    2554         root    3u     unix 0xffff90d8ba564000       0t0      28724 /tmp/server.sock
[root@localhost ~]# netstat -anp | grep 28724
unix  2      [ ACC ]     STREAM     LISTENING     28724    2554/python         /tmp/server.sock

進程2554創建了打開了unix domain socket描述符(3 -> socket:[19803]),並且通過該描述符,打開了/tmp/server.sock文件,其主要作用是用於監聽

我們運行client.py並觀察狀態

[root@localhost ~]# python /tmp/client.py &
[2] 2555
[root@localhost ~]# ls -l /proc/2555/fd
total 0
lrwx------ 1 root root 64 Nov  5 02:39 0 -> /dev/pts/0
lrwx------ 1 root root 64 Nov  5 02:39 1 -> /dev/pts/0
lrwx------ 1 root root 64 Nov  5 02:39 2 -> /dev/pts/0
lrwx------ 1 root root 64 Nov  5 02:39 3 -> socket:[28728]
[root@localhost ~]# grep 28728 /proc/net/unix
ffff90d8b95b0400: 00000003 00000000 00000000 0001 03 28728
[root@localhost ~]# lsof -n | grep 28728
python    2555         root    3u     unix 0xffff90d8b95b0400       0t0      28728 socket

與server.py的行為差不多。client.py也創建了unix domain socket描述符3 -> socket:[28728],通過socket:[18974],找到一條socket

查看server.py發生的變化:

[root@localhost ~]# ls -l /proc/2554/fd
total 0
lrwx------ 1 root root 64 Nov  5 02:39 0 -> /dev/pts/0
lrwx------ 1 root root 64 Nov  5 02:39 1 -> /dev/pts/0
lrwx------ 1 root root 64 Nov  5 02:39 2 -> /dev/pts/0
lrwx------ 1 root root 64 Nov  5 02:39 3 -> socket:[28724]
lrwx------ 1 root root 64 Nov  5 02:39 4 -> socket:[28725]

server.py新增了一個4 -> socket:[28725],這是剛才client.py連接成功之后server.py新打開的描述符

[root@localhost ~]# lsof -n | grep -E '28728|28724|28725'
python    2554         root    3u     unix 0xffff90d8ba564000       0t0      28724 /tmp/server.sock
python    2554         root    4u     unix 0xffff90d8b95b0000       0t0      28725 /tmp/server.sock
python    2555         root    3u     unix 0xffff90d8b95b0400       0t0      28728 socket
[root@localhost ~]# netstat -anp | grep unix | grep -E '28728|28724|28725'
unix  2      [ ACC ]     STREAM     LISTENING     28724    2554/python          /tmp/server.sock
unix  3      [ ]         STREAM     CONNECTED     28725    2554/python          /tmp/server.sock
unix  3      [ ]         STREAM     CONNECTED     28728    2555/python

到目前為止,整個unix domain socket的通信過程已經比較清晰的展現了:
● server.py啟動之后,打開監聽的描述符,等待來自客戶端的連接請求
● client.py啟動之后,與server連接成功,打開一個描述符用於與server.py通信
● server.py會再打開一個描述符用於與client.py進行數據通信

但是目前還有2個問題:
(1)/tmp/server.sock到底作用是什么
(2)server與client是怎么進行數據通信的

問題(1)

● /tmp/server.sock是操作系統的實體文件,擁有一個全局的文件系統描述符,這個描述符在操作系統中是唯一的
● server.py啟動時打開了server.sock,就聲名了與server.py建立連接就只能通過server.sock文件
● 這就相當於TCP socket中四元組中的兩元(server_ip:server_port

問題(2)

我們來使用strace命令看看server.py的內核調用

[root@localhost tmp]# strace -p 2554
strace: Process 2554 attached
recvfrom(4, "hello world!", 100, 0, NULL, NULL) = 12
sendto(4, "hello world!", 12, 0, NULL, 0) = 12
recvfrom(4, "hello world!", 100, 0, NULL, NULL) = 12
sendto(4, "hello world!", 12, 0, NULL, 0) = 12

recvfrom(4, "hello world!", 100, 0, NULL, NULL) = 12
sendto(4, "hello world!", 12, 0, NULL, 0) = 12
server.py在接收客戶端數據的時候,使用了 4 -> socket:[28725]這個文件描述符

再看client.py的內核調用

[root@localhost tmp]# strace -p 2555
strace: Process 2555 attached
select(0, NULL, NULL, NULL, {0, 996991}) = 0 (Timeout)
sendto(3, "hello world!", 12, 0, NULL, 0) = 12
recvfrom(3, "hello world!", 100, 0, NULL, NULL) = 12
select(0, NULL, NULL, NULL, {1, 0})     = 0 (Timeout)
sendto(3, "hello world!", 12, 0, NULL, 0) = 12

recvfrom(3, "hello world!", 100, 0, NULL, NULL) = 12
sendto(3, "hello world!", 12, 0, NULL, 0) = 12
client.py在與server.py通信的時候使用了 3 -> socket:[28728]

結論:
● server.py與client.py連接建立成功之后,都會各自在自己的進程下打開unix domain socket描述符,該描述符來指向對應的socket內存空間(下面簡稱s_mem
● client.py通過3 -> socket:[28728],找到s_mem,然后寫入數據hello world!
● server.py通過4 -> socket:[28725],找到s_mem,讀取數據hello world!,並且原封不動的發送這串數據給client.py
● client.py通過讀取s_mem,獲取從server.py傳來的數據
● 循環往復

           client.py                         server.py
           +---------------+                 +---------------+
           |pid:2555       |                 |pid:2554       |
           |    +-----+    |                 |    +-----+    |
           |    |fd:3 |    |                 |    |fd:4 |    |
           |    +-----+    |                 |    +-----+    |
           +---------------+                 +---------------+
                   |                                 |
user space         |                                 |
+---------------------------------------------------------------------+
kernel space       |                                 |
                   |                                 |
                   v                                 v
            +--------------+                  +--------------+
            |socket:[28728]|                  |socket:[28725]|
            +------+-------+                  +------+-------+
                   |                                 |
                   |                                 |
                   v                                 v
                 +------------------------------------+
                 |              socket                |
                 +------------------------------------+


四、小結

● /tmp/server.sock作為建立unix domain socket連接的唯一標識符
● unix domain socket連接建立完成之后在內存開辟一塊空間,而server與client在這塊內存空間中進行數據傳輸
● 在同一台機器上的進程通信,unix domain socket比tcp socket更快,因為它不需要網絡協議棧,不需要打包拆包、計算校驗和、維護序號和應答等等過程


五、參考資料

https://en.wikipedia.org/wiki/Unix_domain_socket



至此,本文結束
在下才疏學淺,有撒湯漏水的,請各位不吝賜教...


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM