LocalSocket you may not have heard of

Preface

I believe that everyone Socketis familiar with it, but LocalSocketthey may not know much about what it is. The author is also ignorant. This is the first time I heard about it. Record it. During the exploration process, I found Androidthat it is used in many places in the system, and this is a more in-depth article.

This blog mainly introduces the following content:

  1. What is LocalSocket?
  2. Implement communication process through LocalSocket
  3. Analyze SystemServerhow processes communicate LocalSocketwith processes through implementation from a source code perspectiveZygote

What is LocalSocket?

Let’s review Socketthe concepts first:

SocketOften translated as socket, it is an abstraction of an endpoint for two-way communication between application processes on different hosts in a network. A socket is one end of process communication on the network, providing a mechanism for application layer processes to exchange data using network protocols. In terms of position, the socket is connected to the application process and the network protocol stack is connected down, which is the interface for the application to communicate through the network protocol.

So LocalSocketwhat is it? I think it can be translated into "local socket"

AndroidWhat is it LocalSocketbased UNIX-domain Socketon ? UNIX-domain SocketI'm completely confused. Before I understand one concept, another one comes up.

UNIX-domain SocketIt is Socketan IPC communication mechanism derived on the basis of , it is full-duplex (allowing data to be transmitted in two directions simultaneously), and the API interface has rich semantics.

So LocalSocketthis Localexpression refers to the same host, and it solves the problem of communication between different processes on the same host.

So the question is, socketwhy build one if it can’t also be used for inter-process communication on the same host LocalSocket?

In fact, this is because socketit is designed for network communication. In order to solve the communication between different hosts, it needs to go through the network protocol stack and do more operations to ensure security verification, and the sacrifice behind these is efficiency.

It UNIX domain socketis more efficient to use IPC: no need to go through the network protocol stack, no need to pack and unpack, calculate checksum, maintain sequence number and response, etc., just copy the application layer data from one process to another.

The difference between UNIX sockets and IP sockets:

UNIX sockets are an inter-process communication mechanism that allow bidirectional data exchange between processes running on the same computer. IP sockets (especially TCP/IP sockets) are a mechanism that allow processes to communicate over a network. In some cases, it is possible to use TCP/IP sockets to communicate with a process running on the same computer (by using the loopback interface). But because UNIX domain sockets know that they are executing on the same system, they can avoid some checks and operations (such as routing). This makes UNIX sockets faster and lighter than IP sockets. Therefore, if you plan to communicate with processes on the same host, this is a better choice than IP sockets.

LocalSocket implements the communication process

AndroidThe above use LocalSocketis mainly namedistinguished by , that is to say, the connection between the client and the server must use the same connection name, and nameonly one server can run at the same time. nameIt can only be a string of strings, such as "com.xxx.xx ".

The following uses Java as the server and C as the client to LocalSocketimplement a communication process:

1.Java server code

The server generates the LocalServerSocket object and blocks the current thread by calling the accept method, waiting for the client's connection.

package com.example.localsocketserver;
 
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
 
import android.app.Activity;
import android.net.Credentials;
import android.net.LocalServerSocket;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
 
public class MainActivity extends Activity {
    
    
    private static final String TAG = "MainActivity";
    private ServerThread mThread = null;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        startServer();
    }
 
    @Override
    protected void onPause() {
    
    
        super.onPause();
    }
 
    @Override
    protected void onDestroy() {
    
    
        super.onDestroy();
        stopServer();
    }
 
    private void startServer(){
    
    
        stopServer();
        mThread = new ServerThread();
        mThread.start();
    }
    
    private void stopServer(){
    
    
        if(mThread != null){
    
    
            mThread.exit();
            mThread = null;
        }
    }
    
    private class ServerThread extends Thread{
    
    
        private boolean exit = false;
        private int port = 3333;
 
        public void run() {
    
      
            LocalServerSocket server = null;  
            BufferedReader mBufferedReader = null;  
            PrintWriter os = null;  
            String readString =null;  
            try {
    
      
                server = new LocalServerSocket("com.xxx.localsocket");   
                while (!exit) {
    
      
                    LocalSocket connect = server.accept();  
                    Credentials cre = connect.getPeerCredentials();
                    Log.i(TAG,"accept socket uid:"+cre.getUid()); 
                    new ConnectThread(connect).start();
                }     
            } catch (IOException e) {
    
      
                e.printStackTrace();  
            }finally{
    
    
                try {
    
      
                    mBufferedReader.close();  
                    os.close();  
                    server.close();  
                } catch (IOException e) {
    
      
                    e.printStackTrace();  
                }  
            }  
        } 
 
        public void exit(){
    
    
            exit = true;
            this.interrupt();
            try {
    
    
                this.wait();
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }
    }
 
    class ConnectThread extends Thread{
    
    
        LocalSocket socket = null;
        BufferedReader mBufferedReader = null;  
        InputStream input = null;
        PrintWriter os = null;
        String readString =null;
        public ConnectThread(LocalSocket socket){
    
        
            this.socket = socket;
        }
 
        @Override
        public void run(){
    
    
            try {
    
    
                input = socket.getInputStream();
                byte[] buffer = new byte[1024];
                int len = input.read(buffer);
                Log.d(TAG,"mBufferedReader:"+new String(buffer,0,len)); 
                os = new PrintWriter(socket.getOutputStream());  
                os.println("this is server\0");  
                os.flush();  
                os.close();
                socket.close();
                Log.d(TAG,"server send over");
            } catch (IOException e) {
    
    
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
 
        }
    }
 
}

2.C client code

The c client code mainly calls Androidthe interface:

int socket_local_server(const char *name, int namespaceId, int type)  

The function read and write idis read idto receive data from the server, write idand to send data to the server – the parameter name is the key to connect the client to the server name, namespaceIdgenerally used ANDROID_SOCKET_NAMESPACE_ABSTRACT, typeuseSOCK_STREAM

#include <sys/socket.h>
#include <sys/un.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <cutils/sockets.h>
#define PATH "com.xxx.localsocket"
int main(int argc, char *argv[]) 
{
    
    
    int socketID;
    int ret;
    int i = 0;
    int len = 0;
    for(;i < argc ;i++){
    
    
        len = len + strlen(argv[i]);
    }
    len = len + argc ;
    char *buffer ;
    buffer = ( char *)malloc(len * sizeof( char *));
    if(buffer == NULL){
    
    
        printf("malloc failed\n");
        return 0;
    }
    strcpy(buffer, argv[0]);
    for(i=1;i<argc;i++){
    
    
        strcat(buffer, " "); 
        strcat(buffer, argv[i]); 
    }
    socketID = socket_local_client(PATH, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
    if (socketID < 0)
    {
    
    
        return socketID;
    }
    ret = write(socketID, buffer, strlen(buffer));
    if(ret < 0){
    
    
        printf("send failed\n");
        return ret;
    }
    char buf2[512] = {
    
    0};
    ret = read(socketID,buf2,sizeof(buf2));
    if(ret < 0){
    
    
        printf("recived failed\n");
        return ret;
    }else{
    
    
        printf("c client recived from server: %s\n",buf2);
    }
 
    ret = close(socketID);
    if (ret < 0)
    {
    
    
        return ret;
    }
 
    return 0;
}

The use of LocalSocket in Android system

AndroidThere are many places in the system source code that are used LocalSocketto implement cross-process communication.

For example, the process sends a request to the process to exit the child process SystemServerthrough LocalSocket instead of other cross-process (for example Binder) communication methods .Zygotefork

For another example, PackageManagerServiethe (PKMS for short) service is responsible for the installation and uninstallation of applications and other related work, but the real work is still the installdprocess. After the daemon process installdis started, the upper layer frameworkcan socketcommunicate with the daemon process. What is used here socketisLocalSocket

The following is an introduction to SystemServerhow processes LocalSocketimplement Zygoteprocess communication by analyzing the source code.

1. Entrance

public static void main(String[] argv) {
    
    
        ......
        // ⭐️ 在类ZygoteServer的构造函数中会创建对应的LocalServerSocket
        zygoteServer = new ZygoteServer(isPrimaryZygote);
        ......
        Log.i(TAG, "Accepting command socket connections");

        // 监听客户端Socket请求
        caller = zygoteServer.runSelectLoop(abiList);
    } catch (Throwable ex) {
    
    
        Log.e(TAG, "System zygote died with fatal exception", ex);
        throw ex;
    } finally {
    
    
        if (zygoteServer != null) {
    
    
            zygoteServer.closeServerSocket();
        }
    }
    
    if (caller != null) {
    
    
        caller.run();
    }
}


2.Zygote implementation-server

2.1 ZygoteServer

  ZygoteServer(boolean isPrimaryZygote) {
    
    
      mUsapPoolEventFD = Zygote.getUsapPoolEventFD();

      // 判断当前是否是Zygote进程,该值与init.zygote64.rc中的--socket-name=zygote有关
      if (isPrimaryZygote) {
    
    
          // 创建LocalSocketServer
          mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.PRIMARY_SOCKET_NAME);
          // 创建USAP进程池相关LocalServerSocket
          mUsapPoolSocket =
                  Zygote.createManagedSocketFromInitSocket(
                          Zygote.USAP_POOL_PRIMARY_SOCKET_NAME);
      } else {
    
    
          mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.SECONDARY_SOCKET_NAME);
          mUsapPoolSocket =
                  Zygote.createManagedSocketFromInitSocket(
                          Zygote.USAP_POOL_SECONDARY_SOCKET_NAME);
      }

      mUsapPoolSupported = true;
      fetchUsapPoolPolicyProps();
  }


2.2 createManagedSocketFromInitSocket

createManagedSocketFromInitSocketsocketNameThe method will obtain the corresponding file descriptor based on the passed in , then create LocalServerSocketan object through the created file description object and return it.

static LocalServerSocket createManagedSocketFromInitSocket(String socketName) {
    
    
    int fileDesc;
    // 构造完整的fullSocketName = ANDROID_SOCKET_zygote
    final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;

    try {
    
    
        // 获取对应文件描述符
        String env = System.getenv(fullSocketName);
        fileDesc = Integer.parseInt(env);
    } catch (RuntimeException ex) {
    
    
        throw new RuntimeException("Socket unset or invalid: " + fullSocketName, ex);
    }

    try {
    
    
        // 创建文件描述符对象
        FileDescriptor fd = new FileDescriptor();
        fd.setInt$(fileDesc);
        // 根据文件描述对象创建LocalServerSocket对象
        return new LocalServerSocket(fd);
    } catch (IOException ex) {
    
    
        throw new RuntimeException(
            "Error building socket from file descriptor: " + fileDesc, ex);
    }
}


2.3 Waiting for client connection

Runnable runSelectLoop(String abiList) {
    
    
    ArrayList<FileDescriptor> socketFDs = new ArrayList<>();
    ArrayList<ZygoteConnection> peers = new ArrayList<>();
    // 将与SystemServer通信的Socket对应文件描述符存入到list中,后续通过Os.poll监听对应文件是否发生了可读事件唤醒Zygote进程
    socketFDs.add(mZygoteSocket.getFileDescriptor());
    // ......

    while (true) {
    
    
        // ...
        // 表示达到了超时时间或者出现了非阻塞轮询并且传递进去的文件描述符都没有就绪则返回0
        if (pollReturnValue == 0) {
    
    
          // ......

        } else {
    
    
            boolean usapPoolFDRead = false;
            while (--pollIndex >= 0) {
    
    
                // 判断对应文件是否有可读事件发生
                if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
    
    
                    continue;
                }
                
                if (pollIndex == 0) {
    
    
                    // ⭐️监听连接请求
                    ZygoteConnection newPeer = acceptCommandPeer(abiList);
                    peers.add(newPeer);
                    socketFDs.add(newPeer.getFileDescriptor());
                // 处理来自SystemServer端的请求
                } else if (pollIndex < usapPoolEventFDIndex) {
    
    
                    try {
    
    
                        ZygoteConnection connection = peers.get(pollIndex);
                        //判断Zygote进程中子线程是否全部停止
                        boolean multipleForksOK = !isUsapPoolEnabled()
                                && ZygoteHooks.isIndefiniteThreadSuspensionSafe();
                        //处理请求
                        final Runnable command =
                                connection.processCommand(this, multipleForksOK);
                        ......
                    } catch (Exception e) {
    
    
                        ......
                    } 
                ......
                } else {
    
    
                    ......
                }
            }
            ......
        }
    }
}


2.4 acceptMethod waiting for connection

mZygoteSocketIt is a created LocalServerSocketobject that monitors the connection request of the process by calling the acceptfunction of the object SystemServer. The current thread will also be blocked until the client sends a request.

private ZygoteConnection acceptCommandPeer(String abiList) {
    
    
    try {
    
    
        // ⭐️ accept等待客户端连接
        return createNewConnection(mZygoteSocket.accept(), abiList);
    } catch (IOException ex) {
    
    
        throw new RuntimeException(
                "IOException during accept()", ex);
    }
}


3.SystemServer implementation-client

3.1. Entry-startViaZygote

Call the function openZygoteSocketIfNeededto create clientthe client LocalSocketand try to connect to the server (Zygote process). Finally, the function zygoteSendArgsAndGetResultis called to convert various parameters and Stringsend them to Zygotethe process.

private Process.ProcessStartResult startViaZygote(......) throws ZygoteStartFailedEx {
    
    
    // ⭐️ openZygoteSocketIfNeeded(abi)
    synchronized(mLock) {
    
    
        return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),
                                          zygotePolicyFlags,
                                          argsForZygote);
    }
}


3.2 Create LocalSocket and connect

private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
    
    
     attemptConnectionToPrimaryZygote();
}

private void attemptConnectionToPrimaryZygote() throws IOException {
    
    
    if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
    
    
        primaryZygoteState =
                ZygoteState.connect(mZygoteSocketAddress,mUsapPoolSocketAddress);
        // ......
    }
}
static ZygoteState connect(@NonNull LocalSocketAddress zygoteSocketAddress,
        @Nullable LocalSocketAddress usapSocketAddress)
        throws IOException {
    
    

    DataInputStream zygoteInputStream;
    BufferedWriter zygoteOutputWriter;
    // ⭐️ 创建LocalSocket
    final LocalSocket zygoteSessionSocket = new LocalSocket();

    if (zygoteSocketAddress == null) {
    
    
        throw new IllegalArgumentException("zygoteSocketAddress can't be null");
    }

    try {
    
    
        // zygoteSocketAddress = new LocalSocketAddress(Zygote.PRIMARY_SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED);
        // ⭐️ 连接到Zygote进程中的LocalServerSocket
        zygoteSessionSocket.connect(zygoteSocketAddress);
        // 获取接收Zygote进程回调信息I/O流
        zygoteInputStream = new DataInputStream(zygoteSessionSocket.getInputStream());
        // 获取传递参数给Zygote进程I/O流
        zygoteOutputWriter = new BufferedWriter(
                    new OutputStreamWriter(zygoteSessionSocket.getOutputStream()),
                    Zygote.SOCKET_BUFFER_SIZE);
    } catch (IOException ex) {
    
    
        try {
    
    
            zygoteSessionSocket.close();
        } catch (IOException ignore) {
    
     }

        throw ex;
    }
    //创建对象并返回
    return new ZygoteState(zygoteSocketAddress, usapSocketAddress,
                           zygoteSessionSocket, zygoteInputStream, zygoteOutputWriter,
                           getAbiList(zygoteOutputWriter, zygoteInputStream));
}


3.3 Parameter sending and data receiving and reading

private Process.ProcessStartResult attemptZygoteSendArgsAndGetResult(
        ZygoteState zygoteState, String msgStr) throws ZygoteStartFailedEx {
    
    
    try {
    
    
        // 获取发送参数I/O流
        final BufferedWriter zygoteWriter = zygoteState.mZygoteOutputWriter;
        // 获取数据接收I/O流
        final DataInputStream zygoteInputStream = zygoteState.mZygoteInputStream;
        // 将参数发送给Zygote进程
        zygoteWriter.write(msgStr);
        zygoteWriter.flush();

        //接收Zygote进程发送回来的数据
        Process.ProcessStartResult result = new Process.ProcessStartResult();
        //新建进程pid
        result.pid = zygoteInputStream.readInt();
        result.usingWrapper = zygoteInputStream.readBoolean();

        if (result.pid < 0) {
    
    
            throw new ZygoteStartFailedEx("fork() failed");
        }

        return result;
    } catch (IOException ex) {
    
    
        zygoteState.close();
        throw new ZygoteStartFailedEx(ex);
    }
}


4. Question

Why is Zygotecommunication used Socketinstead Binder?

Reason 1: Timing issues

BinderinitThe driver is loaded earlier than the process. The process initis the first process started by the Android system.

The references generally used in Android Binderare saved in ServiceManagerthe process, and if you want ServiceManagerto get the corresponding Binderreferences, you need to register.

Although Initthe process is created first ServiceManager, Zygotethe process is created later. Although Zygoteit is created later, there is no guarantee that Zygotethe process will have been initialized Binderwhen it is registered. ServiceManagerThe registration time cannot be guaranteed, and the reference AMScannot be obtained , so the timing cannot be guaranteed.Zygotebinder

at last

If you want to become an architect or want to break through the 20-30K salary range, then don't be limited to coding and business, you must be able to select and expand, and improve your programming thinking. In addition, good career planning is also very important, and learning habits are important, but the most important thing is to be able to persevere. Any plan that cannot be implemented consistently is empty talk.

If you have no direction, here I would like to share with you a set of "Advanced Notes on the Eight Major Modules of Android" written by the senior architect of Ali, to help you organize the messy, scattered and fragmented knowledge systematically, so that you can systematically and efficiently Master various knowledge points of Android development.
img
Compared with the fragmented content we usually read, the knowledge points of this note are more systematic, easier to understand and remember, and are arranged strictly according to the knowledge system.

Welcome everyone to support with one click and three consecutive times. If you need the information in the article, just scan the CSDN official certification WeChat card at the end of the article to get it for free↓↓↓

PS: There is also a ChatGPT robot in the group, which can answer everyone’s work or technical questions.

picture

Guess you like

Origin blog.csdn.net/weixin_43440181/article/details/132814222