1. 前言
限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。
2. init.rc中套接字的创建和使用
在Android中,我们可以在 init.rc 中,通过脚本片段
service xxx ...
...
socket sock_xxx stream 0666 root system
...
指示 init 进程创建套接字,然后供用户使用。
2.1 init 进程创建套接字流程
/* 解析 init.rc ,记录要创建的 socket 的列表 */
parse_line_service() /* system/core/init/init_parser.c */
...
switch (kw) {
case K_socket:
struct socketinfo *si;
si = calloc(1, sizeof(*si));
si->name = args[1];
si->type = args[2];
si->perm = strtoul(args[3], 0, 8);
if (nargs > 4)
si->uid = decode_uid(args[4]);
if (nargs > 5)
si->gid = decode_uid(args[5]);
if (nargs > 6)
si->socketcon = args[6];
si->next = svc->sockets;
svc->sockets = si;
break;
...
}
...
/* 创建 socket */
service_start() /* system/core/init/init.c */
...
for (si = svc->sockets; si; si = si->next) {
int socket_type = (!strcmp(si->type, "stream") ? SOCK_STREAM :
(!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));
int s = create_socket(si->name, socket_type, si->perm, si->uid, si->gid, si->socketcon ?: scon);
...
fd = socket(PF_UNIX, type, 0) /* 创建 UNIX domain 套接字 */
bind(fd, (struct sockaddr *) &addr, sizeof (addr))
/* 导出 UNIX 套接字:创建文件 /dev/socket/ANDROID_SOCKET_xxx */
publish_socket(si->name, s)
/*
* 添加一个套接字环境变量
* key: ANDROID_SOCKET_xxx
* val: 套接字的文件句柄
*/
...
add_environment(key, val) /* 后面的程序(具体是service daemon) 使用这个环境变量来获取套接字句柄 */
}
...
2.2 使用套接字
要使用套接字的地方,调用 android_get_control_socket() 获取对应 init.rc 中命名的 sock_xxx 套接字的句柄。
android_get_control_socket() /* system/core/include/cutils/sockets.h */
char key[64] = ANDROID_SOCKET_ENV_PREFIX;
const char *val;
int fd;
/* build our environment variable, counting cycles like a wolf ... */
#if HAVE_STRLCPY
strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
name,
sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
#else /* for the host, which may lack the almightly strncpy ... */
strncpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
name,
sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
key[sizeof(key)-1] = '\0';
#endif
val = getenv(key); /* socket 套接字句柄字串 */
if (!val)
return -1;
errno = 0;
fd = strtol(val, NULL, 10); /* socket 套接字句柄 */
if (errno)
return -1;
return fd;