本文所用lxc-4.0.0版本,使用ndk26进行交叉编译,过程在此忽略。
以下启动Android系统大体流程
中间经过lxccontainer.c 构建容器
struct lxc_container *lxc_container_new(const char *name, const char *configpath)
{
struct lxc_container *c;
size_t len;
int rc;
/*
* 开辟空间,设置挂载指针,最后给容器取个名字
*/
c = malloc(sizeof(*c));
memset(c, 0, sizeof(*c));
(void)strlcpy(c->name, name, len + 1);
//创建容器的RUN的文件夹,这里要结合交叉编译调试
c->slock = lxc_newlock(c->config_path, name);
if (!c->slock) {
fprintf(stderr, "Failed to create lock for %s\n", name);
goto err;
}
//检查容器的创建状态
rc = ongoing_create(c);
/* 设置实现函数 */
c->is_defined = lxcapi_is_defined;
c->want_daemonize = lxcapi_want_daemonize;
c->start = lxcapi_start;
return c;
err:
lxc_container_free(c);
return NULL;
}
进入lxc_start.c 的入口函数,最终调用容器的start函数。需注意容器启动的要么没有要么是默认参数/sbin/init
int main(int argc, char *argv[])
{
const char *lxcpath;
char *const *args;
struct lxc_container *c;
struct lxc_log log;
int err = EXIT_FAILURE;
char *rcfile = NULL;
char *const default_args[] = {
"/sbin/init",
NULL,
};
lxc_list_init(&defines);
//容器内系统功能
if (lxc_caps_init())
exit(err);
//解析参数
if (lxc_arguments_parse(&my_args, argc, argv))
exit(err);
//容器目录的挂载指针位置
lxcpath = my_args.lxcpath[0];
if (access(lxcpath, O_RDONLY) < 0) {
ERROR("You lack access to %s", lxcpath);
exit(err);
}
// 解析自定义配置文件,如果有
if (my_args.rcfile) {
rcfile = (char *)my_args.rcfile;
// 创建容器
c = lxc_container_new(my_args.name, lxcpath);
if (!c) {
ERROR("Failed to create lxc_container");
exit(err);
}
}
//开启容器
if (args == default_args)
err = c->start(c, 0, NULL) ? EXIT_SUCCESS : EXIT_FAILURE;
else
err = c->start(c, 0, args) ? EXIT_SUCCESS : EXIT_FAILURE;
out:
lxc_container_put(c);
exit(err);
}
进入do_lxcapi_start 函数,这一步只是生了个孩子,在笔者实验下进入lxc_start 函数
static bool do_lxcapi_start(struct lxc_container *c, int useinit, char * const argv[])
{
/* initialize handler */
handler = lxc_init_handler(c->name, conf, c->config_path, c->daemonize);
//生孩子
if (c->daemonize) {
pid_first = fork();
pid_second = fork();
/* second parent */
if (pid_second != 0) {
free_init_cmd(init_cmd);
lxc_free_handler(handler);
_exit(EXIT_SUCCESS);
}
/* second child */
}
reboot:
//进一步在容器里执行
if (useinit)
ret = lxc_execute(c->name, argv, 1, handler, c->config_path,
c->daemonize, &c->error_num);
else
ret = lxc_start(argv, handler, c->config_path, c->daemonize,
&c->error_num);
return true;
}
进入start.c 的 __lxc_start 函数,只要用户的init进程创建出来了,那么它的父进程会被回收
int __lxc_start(struct lxc_handler *handler, struct lxc_operations *ops,
void *data, const char *lxcpath, bool daemonize, int *error_num)
{
int ret, status;
const char *name = handler->name;
struct lxc_conf *conf = handler->conf;
struct cgroup_ops *cgroup_ops;
// 设置环境变量,文件描述符,terminal终端,cgroup初始化,运行钩子函数
ret = lxc_init(name, handler);
if (ret < 0) {
ERROR("Failed to initialize container \"%s\"", name);
goto out_abort;
}
handler->ops = ops;
handler->data = data;
handler->daemonize = daemonize;
cgroup_ops = handler->cgroup_ops;
//克隆逼格附加块设备
if (!attach_block_device(handler->conf)) {
ERROR("Failed to attach block device");
ret = -1;
goto out_abort;
}
//监视器的创建和进入
if (!cgroup_ops->monitor_create(cgroup_ops, handler)) {
ERROR("Failed to create monitor cgroup");
ret = -1;
goto out_abort;
}
if (!cgroup_ops->monitor_enter(cgroup_ops, handler)) {
ERROR("Failed to enter monitor cgroup");
ret = -1;
goto out_abort;
}
//生成容器
ret = lxc_spawn(handler);
if (ret < 0) {
ERROR("Failed to spawn container \"%s\"", name);
goto out_detach_blockdev;
}
handler->conf->reboot = REBOOT_NONE;
//利用文件描述符设置事件回调
ret = lxc_poll(name, handler);
if (ret) {
ERROR("LXC mainloop exited with error: %d", ret);
goto out_delete_network;
}
//回收僵尸进程
status = lxc_wait_for_pid_status(handler->pid);
if (status < 0)
SYSERROR("Failed to retrieve status for %d", handler->pid);
}
最后一步,克隆进程并在里执行用户的操作系统
static int lxc_spawn(struct lxc_handler *handler)
{
__do_close int data_sock0 = -EBADF, data_sock1 = -EBADF;
int i, ret;
char pidstr[20];
bool wants_to_map_ids;
struct lxc_list *id_map;
const char *name = handler->name;
const char *lxcpath = handler->lxcpath;
bool share_ns = false;
struct lxc_conf *conf = handler->conf;
struct cgroup_ops *cgroup_ops = handler->cgroup_ops;
id_map = &conf->id_map;
// 克隆一个子进程,等待回收
if (share_ns) {
pid_t attacher_pid;
attacher_pid = lxc_clone(do_share_ns, handler,
CLONE_VFORK | CLONE_VM | CLONE_FILES, NULL);
if (attacher_pid < 0) {
SYSERROR(LXC_CLONE_ERROR);
goto out_delete_net;
}
ret = wait_for_pid(attacher_pid);
if (ret < 0) {
SYSERROR("Intermediate process failed");
goto out_delete_net;
}
} else {
handler->pid = lxc_raw_clone_cb(do_start, handler,
CLONE_PIDFD | handler->ns_on_clone_flags,
&handler->pidfd);
}
if (handler->pid < 0) {
SYSERROR(LXC_CLONE_ERROR);
goto out_delete_net;
}
TRACE("Cloned child process %d", handler->pid);
/* 往进程发一个信号验证是否能用 */
if (!lxc_can_use_pidfd(handler->pidfd))
close_prot_errno_disarm(handler->pidfd);
// 克隆标记逻辑运算
for (i = 0; i < LXC_NS_MAX; i++)
if (handler->ns_on_clone_flags & ns_info[i].clone_flag)
INFO("Cloned %s", ns_info[i].flag_name);
ret = lxc_sync_wake_child(handler, LXC_SYNC_STARTUP);
if (ret < 0)
goto out_delete_net;
ret = lxc_sync_wait_child(handler, LXC_SYNC_CONFIGURE);
if (ret < 0)
goto out_delete_net;
//设置,和添加cgroup层级
if (!cgroup_ops->setup_limits_legacy(cgroup_ops, handler->conf, false)) {
ERROR("Failed to setup cgroup limits for container \"%s\"", name);
goto out_delete_net;
}
//赋予配置权限
if (!cgroup_ops->chown(cgroup_ops, handler->conf))
goto out_delete_net;
ret = lxc_netns_set_nsid(handler->nsfd[LXC_NS_NET]);
if (ret < 0)
SYSWARN("Failed to allocate new network namespace id");
else
TRACE("Allocated new network namespace id");
/* 设置网络配置 */
if (handler->ns_clone_flags & CLONE_NEWNET) {
ret = lxc_create_network(handler);
if (ret < 0) {
ERROR("Failed to create the network");
goto out_delete_net;
}
ret = lxc_network_send_to_child(handler);
if (ret < 0) {
ERROR("Failed to send veth names to child");
goto out_delete_net;
}
}
// 设置/proc 文件系统
if (!lxc_list_empty(&conf->procs)) {
ret = setup_proc_filesystem(&conf->procs, handler->pid);
if (ret < 0)
goto out_delete_net;
}
/* Tell the child to continue its initialization. We'll get
* LXC_SYNC_CGROUP when it is ready for us to setup cgroups.
*/
ret = lxc_sync_barrier_child(handler, LXC_SYNC_POST_CONFIGURE);
if (ret < 0)
goto out_delete_net;
// 设置cgroup2 控制器
if (!cgroup_ops->devices_activate(cgroup_ops, handler)) {
ERROR("Failed to setup cgroup2 device controller limits");
goto out_delete_net;
}
TRACE("Set up cgroup2 device controller limits");
// 记录网络日志
lxc_log_configured_netdevs(conf);
/* 读取tty设备的信息 */
ret = lxc_recv_ttys_from_child(handler);
if (ret < 0) {
ERROR("Failed to receive tty info from child process");
goto out_delete_net;
}
// 在容器里启动用户系统
ret = handler->ops->post_start(handler, handler->data);
if (ret < 0)
goto out_abort;
// 更改容器状态
ret = lxc_set_state(name, handler, RUNNING);
if (ret < 0) {
ERROR("Failed to set state to \"%s\"", lxc_state2str(RUNNING));
goto out_abort;
}
lxc_sync_fini(handler);
return -1;
}