当前位置: 首页 > Linux

Mapplauncherd项目分析[鲮鲮JingOS]

时间:2023-04-06 11:39:25 Linux

mapplauncherd是sailfishos使用的一个应用启动加速模块,类似于Android的zygote。maplauncherd最初由MeeGo开发,后来被各种基于Linux的系统用于应用程序启动。本文主要分析mapplauncherd的基本运行原理源码参考https://github.com/sailfishos/mapplauncherd.git编译安装依赖sudoapt-getinstalllibcap-devlibsystemd-devlibdbus-1-devmkdirbuild&&cdbuildcmake..如何使用/make#安装cdbuildmkdirtestbinDESTDIR=./testbinmakeinstall#运行daemonLD_LIBRARY_PATH=./usr/local/lib./usr/local/libexec/maplauncherd/booster-generic#打开另一个终端运行invoker./usr/local/bin/invoker-tgeneric/path/to/exec源码分析文件布局,关键文件解释invoker目录,用于传递应用程序信息到launcher守护工具launcherlib目录,定义了核心功能类appdata应用信息booster启动加速类connection连接管理daemondaemonframework简介maplauncherd整体分为两个部分daemonservice,主控服务,其作用是控制应用整体的启动和结束Invoker应用程序启动工具,用于通知守护服务启动应用程序。基础类描述了Daemon类,封装了守护进程的基本功能。它是maplauncherd的主要控制模块。daemon服务进程负责fork出booster加速进程。SocketManager用于管理Booster监听的socket文件,socket用于调用者发送应用启动请求。Booster类是所有助推器类型的抽象。顾名思义,这个booster是作为应用程序启动加速的基类,启动的应用程序一般分为几种类型,比如Qt/QML应用程序,普通原生应用程序应用程序,或者用户自定义类型的应用程序。其加速的原因在于Booster预加载了一些公共资源,如QML控件、公共库等,从根本上提高了应用程序的启动速度。Booster进程还用于接收调用者发送的启动请求。我们可以创建一个新的JBooster类继承自Booster基类来加载JingOS自定义公共组件。示例如下:classJBooster:publicBooster{public:JBooster(){}protected:boolpreload(){//加载公共库文件//加载QML公共控件}};类关系如下:关键进程分析初始化进程user需要先确定一个启动boosterdaemon服务的方法,比如使用systemd机制自动启动。首先创建一个自定义的Booster,即JBooster,然后创建一个Daemon类对象,将JBooster对象传递给Daemon。在Daemon构造器中创建一个socketpair来与forkedbooster加速进程进行通信。具体交流内容稍后介绍。Daemon构建完成后,调用run进入主循环,为子进程(booster进程)创建接收invoker请求的socket。其实也可以在booster进程fork之后创建这个socket。maplauncherd在Daemon进程中创建socket嗯,应该是为了加速的目的。资源准备好后,开始forkbooster子进程。子进程初始化Booster类对象,主要设置两个socket,newBoosterLauncherSocket用于与父进程通信,socketFd用于接收invoker请求。连接。signal信号处理流程如果用户杀掉daemon进程,maplauncherd需要做哪些处理?信号处理函数定义在Daemon的构造函数中。下面的代码只展示了与信号处理相关的内容。Daemon::Daemon(int&argc,char*argv[]){//安装信号处理程序。//原始处理程序保存在守护程序实例中,以便它们可以在助推器中恢复。setUnixSignalHandler(SIGCHLD,write_to_signal_pipe);//收割僵尸setUnixSignalHandler(SIGINT,write_to_signal_pipe);//退出启动器setUnixSignalHandler(SIGTERM,write_to_signal_pipe);//退出启动器setUnixSignalHandler(SIGUSR1,write_to_signal_pipe);//从启动模式进入正常模式setUnixSignalHandler(SIGUSR2,write_to_signal_pipe);//进入启动模式(与--boot-mode相同)setUnixSignalHandler(SIGPIPE,write_to_signal_pipe);//brokeninvoker'spipesetUnixSignalHandler_write(SIGUSR2)re-exec}信号由write_to_signal_pipe函数统一处理staticvoidwrite_to_signal_pipe(intsig){charv=(char)sig;如果(写(大mon::instance()->sigPipeFd(),&v,1)!=1){/*如果我们不能写入内部信号转发*管道,我们不妨退出*/constcharm[]="***信号管道写入失败-正在终止\n";if(write(STDERR_FILENO,m,sizeofm-1)==-1){//不关心}_exit(EXIT_FAILURE);write_to_signal_pipe函数很简单,就是写到sigPipeFd()里面写什么信号?管道是在Daemon构造函数中创建的。读端已经加入poll集合,写时触发poll。之所以要处理相应的信号,是因为它在信号处理器中是最重要的。最好不要做过多的逻辑处理,更不要操作堆内存,比如像malloc这样的调用,会导致死锁,详见《Unix 环境?级编程》中的解释。守护进程是系统的关键服务。如果退出,则需要退出所有由booster启动的应用程序。caseSIGINT:caseSIGTERM:{for(;;){//遍历所有booster进程pidPidVect::iteratoriter(m_children.begin());if(iter==m_children.end())//遍历后跳出循环break;pid_tbooster_pid=*iter;/*终止booster*/kill_process("booster",booster_pid);}Logger::logDebug("boosterexit");//守护进程退出exit(EXIT_SUCCESS);break;}当收到应用进程的退出信号后回收子进程,即调用waitpidcaseSIGCHLD:reapZombies();休息;invoker请求进程桌面启动应用本质上是调用invoker命令,invoker的参数包括可执行程序,在文章前面介绍的使用方法中有提到。调用者连接booster的socket文件,将要启动的应用程序的可执行文件路径传递给booster。booster需要为应用准备一个沙箱环??境,比如uid等配置。出于安全考虑,你需要指定应用程序可以有能力在一切就绪后开始加载主函数。最后,守护进程发送启动成功消息,守护进程再次为应用程序启动请求启动助推器。应用程序启动流程为了支持maplauncherd的启动机制,应用程序的可执行程序需要是共享对象而不是可执行文件。这就需要在编译的时候加上-pie(positionindependentexecutable)选项(gcc),并设置main函数导出。加载应用程序主要功能的过程如下:void*Booster::loadMain(){//为dlopen设置标志intdlopenFlags=RTLD_LAZY;如果(m_appData->dlopenGlobal())dlopenFlags|=RTLD_GLOBAL;elsedlopenFlags|=RTLD_LOCAL;#if(PLATFORM_ID==Linux)&&defined(GLIBC)if(m_appData->dlopenDeep())dlopenFlags|=RTLD_DEEPBIND;#endif//打开调用者发送的可执行程序void*module=dlopen(m_appData->fileName().c_str(),dlopenFlags);错误();//导出主函数m_appData->setEntry(reinterpret_cast(dlsym(module,"main")));constchar*error_s=dlerror();if(error_s!=NULL)throwstd::runtime_error(std::string("Booster:Loadingsymbol'main'failed:'")+error_s+"'\n");返回模块;}~~执行过程intBooster::launchProcess(){setEnvironmentBeforeLaunch();//加载主函数loadMain();//调用主函数constintretVal=m_appData->entry()(m_appData->argc(),const_cast(m_appData->argv()));returnretVal;}该方案的优点开发者可以自定义Booster类,自定义需要预加载的资源和应用启动前的准备流程可配置的沙箱和能力控制,保证系统的安全使用fork的COW机制系统调用节省系统内存改进思路个人觉得zygote的结构比较合理。maplauncherd的守护进程服务可以作为应用程序孵化器来监控所有调用者的请求。在requestconnect的时候fork子进程,而不是提前fork一个booster,让daemon在loadmain函数之后再fork一个booster,这个过程感觉多余,zygote的流程更简洁。