首页 文章 精选 留言 我的

精选列表

搜索[桌面应用程序],共10000篇文章
优秀的个人博客,低调大师

Android应用程序进程启动过程的源代码分析(2)

Step 5.ZygoteInit.runSelectLoopMode 这个函数定义在frameworks/base/core/java/com/android/internal/os/ZygoteInit.java文件中: publicclassZygoteInit{ ...... /** *Runsthezygoteprocess'sselectloop.Acceptsnewconnectionsas *theyhappen,andreadscommandsfromconnectionsonespawn-request's *worthatatime. * *@throwsMethodAndArgsCallerinachildprocesswhenamain()should *beexecuted. */ privatestaticvoidrunSelectLoopMode()throwsMethodAndArgsCaller{ ArrayList<FileDescriptor>fds=newArrayList(); ArrayList<ZygoteConnection>peers=newArrayList(); FileDescriptor[]fdArray=newFileDescriptor[4]; fds.add(sServerSocket.getFileDescriptor()); peers.add(null); intloopCount=GC_LOOP_COUNT; while(true){ intindex; /* *Callgc()beforeweblockinselect(). *It'sworkthathastobedoneanyway,andit'sbetter *toavoidmakingeverychilddoit.Itwillalso *madvise()anyfreememoryasaside-effect. * *Don'tcalliteverytime,becausewalkingtheentire *heapisalotofoverheadtofreeafewhundredbytes. */ if(loopCount<=0){ gc(); loopCount=GC_LOOP_COUNT; }else{ loopCount--; } try{ fdArray=fds.toArray(fdArray); index=selectReadable(fdArray); }catch(IOExceptionex){ thrownewRuntimeException("Errorinselect()",ex); } if(index<0){ thrownewRuntimeException("Errorinselect()"); }elseif(index==0){ ZygoteConnectionnewPeer=acceptCommandPeer(); peers.add(newPeer); fds.add(newPeer.getFileDesciptor()); }else{ booleandone; done=peers.get(index).runOnce(); if(done){ peers.remove(index); fds.remove(index); } } } } ...... } 当Step 4将数据通过Socket接口发送出去后,就会下面这个语句: done=peers.get(index).runOnce(); 这里从peers.get(index)得到的是一个ZygoteConnection对象,表示一个Socket连接,因此,接下来就是调用ZygoteConnection.runOnce函数进一步处理了。 Step 6.ZygoteConnection.runOnce 这个函数定义在frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java文件中: classZygoteConnection{ ...... booleanrunOnce()throwsZygoteInit.MethodAndArgsCaller{ Stringargs[]; ArgumentsparsedArgs=null; FileDescriptor[]descriptors; try{ args=readArgumentList(); descriptors=mSocket.getAncillaryFileDescriptors(); }catch(IOExceptionex){ ...... returntrue; } ...... /**thestderrofthemostrecentrequest,ifavail*/ PrintStreamnewStderr=null; if(descriptors!=null&&descriptors.length>=3){ newStderr=newPrintStream( newFileOutputStream(descriptors[2])); } intpid; try{ parsedArgs=newArguments(args); applyUidSecurityPolicy(parsedArgs,peer); applyDebuggerSecurityPolicy(parsedArgs); applyRlimitSecurityPolicy(parsedArgs,peer); applyCapabilitiesSecurityPolicy(parsedArgs,peer); int[][]rlimits=null; if(parsedArgs.rlimits!=null){ rlimits=parsedArgs.rlimits.toArray(intArray2d); } pid=Zygote.forkAndSpecialize(parsedArgs.uid,parsedArgs.gid, parsedArgs.gids,parsedArgs.debugFlags,rlimits); }catch(IllegalArgumentExceptionex){ ...... }catch(ZygoteSecurityExceptionex){ ...... } if(pid==0){ //inchild handleChildProc(parsedArgs,descriptors,newStderr); //shouldneverhappen returntrue; }else{/*pid!=0*/ //inparent...pidof<0meansfailure returnhandleParentProc(pid,descriptors,parsedArgs); } } ...... } 真正创建进程的地方就是在这里了: pid=Zygote.forkAndSpecialize(parsedArgs.uid,parsedArgs.gid, parsedArgs.gids,parsedArgs.debugFlags,rlimits); 有Linux开发经验的读者很容易看懂这个函数调用,这个函数会创建一个进程,而且有两个返回值,一个是在当前进程中返回的,一个是在新创建的进程中返回,即在当前进程的子进程中返回,在当前进程中的返回值就是新创建的子进程的pid值,而在子进程中的返回值是0。因为我们只关心创建的新进程的情况,因此,我们沿着子进程的执行路径继续看下去: if(pid==0){ //inchild handleChildProc(parsedArgs,descriptors,newStderr); //shouldneverhappen returntrue; }else{/*pid!=0*/ ...... } 这里就是调用handleChildProc函数了。 Step 7.ZygoteConnection.handleChildProc 这个函数定义在frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java文件中: classZygoteConnection{ ...... privatevoidhandleChildProc(ArgumentsparsedArgs, FileDescriptor[]descriptors,PrintStreamnewStderr) throwsZygoteInit.MethodAndArgsCaller{ ...... if(parsedArgs.runtimeInit){ RuntimeInit.zygoteInit(parsedArgs.remainingArgs); }else{ ...... } } ...... } 由于在前面的Step 3中,指定了"--runtime-init"参数,表示要为新创建的进程初始化运行时库,因此,这里的parseArgs.runtimeInit值为true,于是就继续执行RuntimeInit.zygoteInit进一步处理了。 Step 8.RuntimeInit.zygoteInit 这个函数定义在frameworks/base/core/java/com/android/internal/os/RuntimeInit.java文件中: publicclassRuntimeInit{ ...... publicstaticfinalvoidzygoteInit(String[]argv) throwsZygoteInit.MethodAndArgsCaller{ //TODO:Doingthishereworks,butitseemskindofarbitrary.Find //abetterplace.Thegoalistosetitupforapplications,butnot //toolslikeam. System.setOut(newAndroidPrintStream(Log.INFO,"System.out")); System.setErr(newAndroidPrintStream(Log.WARN,"System.err")); commonInit(); zygoteInitNative(); intcurArg=0; for(/*curArg*/;curArg<argv.length;curArg++){ Stringarg=argv[curArg]; if(arg.equals("--")){ curArg++; break; }elseif(!arg.startsWith("--")){ break; }elseif(arg.startsWith("--nice-name=")){ StringniceName=arg.substring(arg.indexOf('=')+1); Process.setArgV0(niceName); } } if(curArg==argv.length){ Slog.e(TAG,"MissingclassnameargumenttoRuntimeInit!"); //lettheprocessexit return; } //Remainingargumentsarepassedtothestartclass'sstaticmain StringstartClass=argv[curArg++]; String[]startArgs=newString[argv.length-curArg]; System.arraycopy(argv,curArg,startArgs,0,startArgs.length); invokeStaticMain(startClass,startArgs); } ...... } 这里有两个关键的函数调用,一个是zygoteInitNative函数调用,一个是invokeStaticMain函数调用,前者就是执行Binder驱动程序初始化的相关工作了,正是由于执行了这个工作,才使得进程中的Binder对象能够顺利地进行Binder进程间通信,而后一个函数调用,就是执行进程的入口函数,这里就是执行startClass类的main函数了,而这个startClass即是我们在Step 1中传进来的"android.app.ActivityThread"值,表示要执行android.app.ActivityThread类的main函数。 我们先来看一下zygoteInitNative函数的调用过程,然后再回到RuntimeInit.zygoteInit函数中来,看看它是如何调用android.app.ActivityThread类的main函数的。 step 9.RuntimeInit.zygoteInitNative 这个函数定义在frameworks/base/core/java/com/android/internal/os/RuntimeInit.java文件中: [java] view plain copy publicclassRuntimeInit{ ...... publicstaticfinalnativevoidzygoteInitNative(); ...... } 这里可以看出,函数zygoteInitNative是一个Native函数,实现在frameworks/base/core/jni/AndroidRuntime.cpp文件中: [cpp] view plain copy staticvoidcom_android_internal_os_RuntimeInit_zygoteInit(JNIEnv*env,jobjectclazz) { gCurRuntime->onZygoteInit(); } 这里它调用了全局变量gCurRuntime的onZygoteInit函数,这个全局变量的定义在frameworks/base/core/jni/AndroidRuntime.cpp文件开头的地方: [cpp] view plain copy staticAndroidRuntime*gCurRuntime=NULL; 这里可以看出,它的类型为AndroidRuntime,它是在AndroidRuntime类的构造函数中初始化的,AndroidRuntime类的构造函数也是定义在frameworks/base/core/jni/AndroidRuntime.cpp文件中: [cpp] view plain copy AndroidRuntime::AndroidRuntime() { ...... assert(gCurRuntime==NULL);//oneperprocess gCurRuntime=this; } 那么这个AndroidRuntime类的构造函数又是什么时候被调用的呢?AndroidRuntime类的声明在frameworks/base/include/android_runtime/AndroidRuntime.h文件中,如果我们打开这个文件会看到,它是一个虚拟类,也就是我们不能直接创建一个AndroidRuntime对象,只能用一个AndroidRuntime类的指针来指向它的某一个子类,这个子类就是AppRuntime了,它定义在frameworks/base/cmds/app_process/app_main.cpp文件中: [cpp] view plain copy intmain(intargc,constchar*constargv[]) { ...... AppRuntimeruntime; ...... } 而AppRuntime类继续了AndroidRuntime类,它也是定义在frameworks/base/cmds/app_process/app_main.cpp文件中: [cpp] view plain copy classAppRuntime:publicAndroidRuntime { ...... }; 因此,在前面的com_android_internal_os_RuntimeInit_zygoteInit函数,实际是执行了AppRuntime类的onZygoteInit函数。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/,如需转载请自行联系原作者

优秀的个人博客,低调大师

ChatGPT 桌面应用里藏着 430MB 的 LibreOffice

Simon Willison 在清理磁盘时发现了一个 1.7GB 的目录。他打开一看,里面装着一整套 LibreOffice。 路径在 ~/.cache/codex-runtimes/codex-primary-runtime/plugins/openai-primary-runtime/plugins/documents。目录里不仅有完整 Python(440MB)和 Node.js(446MB),还有 Poppler(188MB)、git(148MB),以及一个 430MB 的 LibreOffice headless 版。目录下的 skills 文件告诉 Codex 如何找到和使用这些二进制文件。 Willison 把这个发现写成了博客,标题叫《Codex bundles LibreOffice》。 430MB 的 LibreOffice 被塞进一个 AI 应用的缓存目录里,不是用户安装的,也不是系统自带的。它是 OpenAI 决定「我们需要一个完整的文档处理引擎」之后,直接打包进去的。 为什么是 LibreOffice?因为 headless 模式下,LibreOffice 可以读取、转换几乎任何 Office 格式——docx、xlsx、pptx——不需要图形界面,不需要授权费,不需要 API 调用。AI Agent 要操作文档,最简单的方式就是自己带一个完整的办公套件。OpenAI 不是从零实现了一个文档解析器,而是选了一个开源项目,塞进自己的运行时,让它变成 AI 工具链的一部分。 它暗示了一种趋势:AI Agent 正在从「调用 API」走向「自带完整工具链」。Codex 不只打包了 LibreOffice,还有 Poppler 处理 PDF、git 操作版本控制、完整 Python 和 Node.js 运行代码。这不是一个聊天应用,这是一个在本地跑完整工作流的 agent 运行时。每一个被塞进去的开源工具,都在说同一件事:AI Agent 需要的不只是推理能力,还有操作真实文件的全部能力。 目前这只是一个发现。OpenAI 没有宣布这个功能,也没有文档说明它怎么用。但 430MB 的 LibreOffice 已经在那儿了,藏在每个安装 Codex 的用户的缓存目录里。 参考来源: Codex bundles LibreOffice — Simon Willison HN discussion

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册