Preface
I believe that everyone Socket
is familiar with it, but LocalSocket
they 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 Android
that it is used in many places in the system, and this is a more in-depth article.
This blog mainly introduces the following content:
- What is LocalSocket?
- Implement communication process through LocalSocket
- Analyze
SystemServer
how processes communicateLocalSocket
with processes through implementation from a source code perspectiveZygote
What is LocalSocket?
Let’s review Socket
the concepts first:
Socket
Often 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 LocalSocket
what is it? I think it can be translated into "local socket"
Android
What is it LocalSocket
based UNIX-domain Socket
on ? UNIX-domain Socket
I'm completely confused. Before I understand one concept, another one comes up.
UNIX-domain Socket
It is Socket
an 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 LocalSocket
this Local
expression refers to the same host, and it solves the problem of communication between different processes on the same host.
So the question is, socket
why build one if it can’t also be used for inter-process communication on the same host LocalSocket
?
In fact, this is because socket
it 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 socket
is 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
Android
The above use LocalSocket
is mainly name
distinguished by , that is to say, the connection between the client and the server must use the same connection name
, and name
only one server can run at the same time. name
It 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 LocalSocket
implement 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 Android
the interface:
int socket_local_server(const char *name, int namespaceId, int type)
The function read and write id
is read id
to receive data from the server, write id
and to send data to the server – the parameter name is the key to connect the client to the server name
, namespaceId
generally used ANDROID_SOCKET_NAMESPACE_ABSTRACT
, type
useSOCK_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
Android
There are many places in the system source code that are used LocalSocket
to implement cross-process communication.
For example, the process sends a request to the process to exit the child process SystemServer
through LocalSocke
t instead of other cross-process (for example Binder
) communication methods .Zygote
fork
For another example, PackageManagerServie
the (PKMS for short) service is responsible for the installation and uninstallation of applications and other related work, but the real work is still the installd
process. After the daemon process installd
is started, the upper layer framework
can socket
communicate with the daemon process. What is used here socket
isLocalSocket
The following is an introduction to SystemServer
how processes LocalSocket
implement Zygote
process 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
createManagedSocketFromInitSocket
socketName
The method will obtain the corresponding file descriptor based on the passed in , then create LocalServerSocket
an 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 accept
Method waiting for connection
mZygoteSocket
It is a created LocalServerSocket
object that monitors the connection request of the process by calling the accept
function 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 openZygoteSocketIfNeeded
to create client
the client LocalSocket
and try to connect to the server (Zygote process). Finally, the function zygoteSendArgsAndGetResult
is called to convert various parameters and String
send them to Zygote
the 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 Zygote
communication used Socket
instead Binder
?
Reason 1: Timing issues
Binder
init
The driver is loaded earlier than the process. The process init
is the first process started by the Android system.
The references generally used in Android Binder
are saved in ServiceManager
the process, and if you want ServiceManager
to get the corresponding Binder
references, you need to register.
Although Init
the process is created first ServiceManager
, Zygote
the process is created later. Although Zygote
it is created later, there is no guarantee that Zygote
the process will have been initialized Binder
when it is registered. ServiceManager
The registration time cannot be guaranteed, and the reference AMS
cannot be obtained , so the timing cannot be guaranteed.Zygote
binder
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.
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.