Unix domain socket

一、 概述
UNIX Domain Socket是在socket架構(gòu)上發(fā)展起來的用于同一臺主機的進程間通訊(IPC),它不需要經(jīng)過網(wǎng)絡協(xié)議棧,不需要打包拆包、計算校驗和、維護序號和應答等,只是將應用層數(shù)據(jù)從一個進程拷貝到另一個進程。UNIX Domain Socket有SOCK_DGRAM或SOCK_STREAM兩種工作模式,類似于UDP和TCP,但是面向消息的UNIX Domain Socket也是可靠的,消息既不會丟失也不會順序錯亂。
UNIX Domain Socket可用于兩個沒有親緣關系的進程,是全雙工的,是目前使用最廣泛的IPC機制,比如X Window服務器和GUI程序之間就是通過UNIX Domain Socket通訊的。
二、工作流程
UNIX Domain socket與網(wǎng)絡socket類似,可以與網(wǎng)絡socket對比應用。
上述二者編程的不同如下:

  • address family為AF_UNIX
  • 因為應用于IPC,所以UNIXDomain socket不需要IP和端口,取而代之的是文件路徑來表示“網(wǎng)絡地址”。這點體現(xiàn)在下面兩個方面。
  • 地址格式不同,UNIXDomain socket用結(jié)構(gòu)體sockaddr_un表示,是一個socket類型的文件在文件系統(tǒng)中的路徑,這個socket文件由bind()調(diào)用創(chuàng)建,如果調(diào)用bind()時該文件已存在,則bind()錯誤返回。
    UNIX Domain Socket客戶端一般要顯式調(diào)用bind函數(shù),而不象網(wǎng)絡socket一樣依賴系統(tǒng)自動分配的地址??蛻舳薭ind的socket文件名可以包含客戶端的pid,這樣服務器就可以區(qū)分不同的客戶端。

UNIX Domain socket的工作流程簡述如下(與網(wǎng)絡socket相同)。
服務器端:創(chuàng)建socket—綁定文件(端口)—監(jiān)聽—接受客戶端連接—接收/發(fā)送數(shù)據(jù)—…—關閉
客戶端:創(chuàng)建socket—綁定文件(端口)—連接—發(fā)送/接收數(shù)據(jù)—…—關閉

在connect對應socket的時候,會檢查connect的進程是否有對應socket文件的讀寫權(quán)限。如果沒有權(quán)限,會拋出IOException Permission Denied異常。
Android中,init進程啟動zygote進程后,會創(chuàng)建一個名為“zygote”的socket文件并監(jiān)聽來自AMS的創(chuàng)建進程的請求。init.rc中,指定socket名為zygote(使用regular namespace,對應一個file,可以使用linux file的權(quán)限管理來禁止其他非授權(quán)進程的連接請求),type為stream,權(quán)限為660(即rw-rw---,擁有者和組內(nèi)用戶擁有讀寫權(quán)限,其他用戶沒有權(quán)限),文件屬主為root,文件所屬組為system。該socket文件對應/dev/socket/zygote下的文件

socket zygote stream 660 root system #init.rc

之后,zygote進程使用該文件的描述符創(chuàng)建LocalServerSocket,監(jiān)聽創(chuàng)建進程的請求。

Linux Abstract Socket Namespace

LocalSocket LocalServerSocket(使用abstract namespace)

Linux has a special feature: if the pathname for a UNIX domain socket begins with a null byte \0, its name is not mapped into the filesystem. Thus it won’t collide with other names in the filesystem. Also, when a server closes its UNIX domain listening socket in the abstract namespace, its file is deleted; with regular UNIX domain sockets, the file persists after the server closes it.

File permissions control who can connect

For UNIX domain sockets, file and directory permissions restrict which processes on the host can open the file, and thus communicate with the server. Therefore, UNIX domain sockets provide an advantage over Internet sockets (to which anyone can connect, unless extra authentication logic is implemented).

關于Abstract和regular namespace socket的比較

  • abstract namespace的socket不對應一個filesystem的socket文件。需要提供一個唯一的socket名字,其他進程只需要知道socket name就可以連接,所以需要實現(xiàn)額外的權(quán)限檢查(可以通過LocalSocket.getPeerCredentials)來獲取連接的客戶端的身份信息。

The address of ordinary Unix domain sockets for servers is the file name of a socket file that actually appears in the filesystem. This is pleasantly Unix-y on the surface but winds up requiring you to do a bunch of bureaucracy to manage these socket files, and the socket files by themselves don't actually do anything that would make it useful for them to be in the filesystem; you can't interact with them and the server behind them with normal Unix file tools, for example.
Linux offers you a second choice. Rather than dealing with socket files in the filesystem, you can use names in an abstract (socket) namespace. Each name must be unique, but the namespace is otherwise flat and unstructured, and you can call your server socket whatever you want. Conveniently and unlike socket files, abstract names vanish when the socket is closed (either by you or because your program exited).
Apart from being Linux-only, the abstract socket namespace suffers from two limitations: you have to find a way to get a unique name and it has no permissions. With regular socket files you can use regular Unix file and directory permissions to insure that only you can talk to your server socket. With abstract socket names, anyone who knows or can find the name can connect to your server. If this matters you will have to do access control yourself.
(One approach is to use getsockopt()
with SO_PEERCRED
to get the UID and so on of the client connecting to you. SO_PEERCRED
is Linux specific as far as I know, but then so is the abstract socket namespace.)
Lsof and other tools conventionally represent socket names in the abstract socket namespace by putting an @
in front of them. This is not actually how they're specified at the C API level, but it's a distinct marker and some higher level tools follow it for, eg, specifying socket names.
(The Go net
package is one such piece of software.)
As far as picking unique names goes, one trick many programs seem to use is to use whatever filename they would be using if they didn't have the abstract socket namespace available. This gives you a convenient way of expressing, eg, per-user sockets; you can just give it a name based on the user's home directory. Other programs use a hierarchical namespace of their own; Ubuntu's upstart
listens on the abstract socket name '/com/ubuntu/upstart
', for example.
(For personal hacks, you can of course just make up your own little short names. Little hacks don't need a big process; that's the whole attraction of the abstract namespace.)
Now that I've poked around this, I'm going to use it for future little Linux-only hacks because checking permissions (if it's even necessary) is a lot more convenient than the whole hassle of dealing with socket files. For things I write that are intended to be portable, I don't see much point; portable code has to deal with socket files so I might as well use regular Unix domain socket names and socket files all the time.

參考##

Android中LocalSocket(套接字)使用
Android中LocalSocket使用
深刻理解Linux進程間通信(IPC)
Linux下的IPC-UNIX Domain Socket
UNIX Domain Socket IPC
unix domain socket

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容