美文网首页
Unix domain socket

Unix domain socket

作者: chandarlee | 来源:发表于2017-04-06 15:11 被阅读1082次

    一、 概述
    UNIX Domain Socket是在socket架构上发展起来的用于同一台主机的进程间通讯(IPC),它不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。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与网络socket类似,可以与网络socket对比应用。
    上述二者编程的不同如下:

    • address family为AF_UNIX
    • 因为应用于IPC,所以UNIXDomain socket不需要IP和端口,取而代之的是文件路径来表示“网络地址”。这点体现在下面两个方面。
    • 地址格式不同,UNIXDomain socket用结构体sockaddr_un表示,是一个socket类型的文件在文件系统中的路径,这个socket文件由bind()调用创建,如果调用bind()时该文件已存在,则bind()错误返回。
      UNIX Domain Socket客户端一般要显式调用bind函数,而不象网络socket一样依赖系统自动分配的地址。客户端bind的socket文件名可以包含客户端的pid,这样服务器就可以区分不同的客户端。

    UNIX Domain socket的工作流程简述如下(与网络socket相同)。
    服务器端:创建socket—绑定文件(端口)—监听—接受客户端连接—接收/发送数据—…—关闭
    客户端:创建socket—绑定文件(端口)—连接—发送/接收数据—…—关闭

    在connect对应socket的时候,会检查connect的进程是否有对应socket文件的读写权限。如果没有权限,会抛出IOException Permission Denied异常。
    Android中,init进程启动zygote进程后,会创建一个名为“zygote”的socket文件并监听来自AMS的创建进程的请求。init.rc中,指定socket名为zygote(使用regular namespace,对应一个file,可以使用linux file的权限管理来禁止其他非授权进程的连接请求),type为stream,权限为660(即rw-rw---,拥有者和组内用户拥有读写权限,其他用户没有权限),文件属主为root,文件所属组为system。该socket文件对应/dev/socket/zygote下的文件

    socket zygote stream 660 root system #init.rc

    之后,zygote进程使用该文件的描述符创建LocalServerSocket,监听创建进程的请求。

    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就可以连接,所以需要实现额外的权限检查(可以通过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

    相关文章

      网友评论

          本文标题:Unix domain socket

          本文链接:https://www.haomeiwen.com/subject/urtqattx.html