前言
程序加载框架
源文件通过预编译,将代码词法和语法进行分析,然后交给编译器;编译之后生成一些汇编文件,链接装载进应用内,最终变成可执行文件;
动态库/静态库
静态库: 链接时,会被完整的复制到可执行文件内,会被系统多次使用,拷贝多份;
动态库: 链接时不复制,程序运行时由系统动态加载进内存,系统只加载一次,多个程序共用,节省内存空间;
静态库与动态库的区别,主要在链接时的区别,一个是静态链接,一个是动态链接;
静态库/动态库生成可执行文件后,接下来验证可执行文件
(提示:
:尽量使用MacO工程验证,如果使用iOS工程,在终端运行可执行文件的时候,会将模拟器或者是真机运行起来,这时候会需要一些访问权限,处理起来比较麻烦;此处我使用源码工程验证)
使用静态库或者动态库的优势:
了解了库的部分原理后,那么在应用中,库是如何加载到内存中呢???
dyld
-
dyld 链接器
库
是通过链接器
:也就是dyld动态链接器
,加载到内存中的;
App启动,会加载程序需要的库(如:libSystem库),进入runtime
运行时,注册回调函数(_dyld_objc_notify_register函数),然后加载新image(image是库,是镜像文件,库加载的过程,就是一种映射的过程,将库映射一份到内存中) ,image加载完毕,执行map_images、Load_images函数,这两个函数执行完毕后,才是main()函数执行; 这就是库的加载流程;
接下来,我们着重查看,调用main()函数之前,dyld的链接过程;
start断点没有执行,但是我们发现,在调用main()函数之前,日志区打印load方法log,这就表示,load方法在main()函数之前就已经调用。
既然load函数在main()之前,那么在load函数内断点查看底层执行流程,断点在load函数内,在log区通过bt
打印栈队列:
通过栈流程,可以看到,最先执行的是_dyld_start
,并且是在dyld
源码内;
在dyld
源码中,搜索_dyld_start
函数,我们发现_dyld_start
函数,是采用汇编的形式,并且,根据不同的架构,采用不同的流程;
汇编的源码,阅读起来不是很方便,但从注释中,我们阅读到_dyld_start
函数,将会执行dyldbootstrap::start(app_mh, argc, argv, dyld_mh, &startGlue)
函数,所以我们可以直接定位到这个函数;
由于这是C函数命名规范,可以解读为:在dyldbootstrap
文件内,执行start
函数;
如此,我们先查询dyldbootstrap
文件,再查询start
函数;
start
函数内,最终返回的是dyld::_main()函数
,而dyld::_main函数
源码复杂,难已阅读,需要开启上帝视角,我们直接查看它的返回值,依据返回值,由下往上逆推;
dyld::_main函数
返回的result
,result
的赋值方式,更多的是来自于sMainExecutable函数
;
// instantiate ImageLoader for main executable
sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
//instantiateFromLoadedImage:镜像文件加载器
// load any inserted libraries
**if** ( sEnv.DYLD_INSERT_LIBRARIES != **NULL** ) {
**for** (**const** **char*** **const*** lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != **NULL**; ++lib)
loadInsertedDylib(*lib);
}
// record count of inserted libraries so that a flat search will look at
// inserted libraries, then main, then others.
//获取动态库镜像文件数量
sInsertedDylibCount = sAllImages.size()-1;
//开始链接镜像文件
link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, **true**, ImageLoader::RPathChain(**NULL**, **NULL**), -1);
sMainExecutable->setNeverUnloadRecursive();
if ( sMainExecutable->forceFlat() ) {
gLinkContext.bindFlat = **true**;
gLinkContext.prebindUsage = ImageLoader::kUseNoPrebinding;
}
//判断动态库镜像文件是否存在
if ( sInsertedDylibCount > 0 ) {
for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
ImageLoader* image = sAllImages[i+1];
//link 动态库镜像文件
link(image, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
image->setNeverUnloadRecursive();
}
......
sMainExecutable->weakBind(gLinkContext);
gLinkContext.linkingMainExecutable = false;
......
当link
,weakBind
结束,主程序开始运行;
initializeMainExecutable();
notifyMonitoringDyldMain();
这就是dyld的大体流程,内部有很多细节,大家可以自行查看,在此就不一一展开讲述;
initializeMainExecutable 程序运行
通过流程可以看到,初始化主程序系统会做一些准备,那么是什么准备呢,接下来我们看processInitializers()
这个函数;
processInitializers()
函数:
void ImageLoader::processInitializers(const LinkContext& context, mach_port_t thisThread,
InitializerTimingList& timingInfo, ImageLoader::UninitedUpwards& images)
{
uint32_t maxImageCount = context.imageCount()+2;
ImageLoader::UninitedUpwards upsBuffer[maxImageCount];
ImageLoader::UninitedUpwards& ups = upsBuffer[0];
ups.count = 0;
// Calling recursive init on all images in images list, building a new list of
// uninitialized upward dependencies.
//在当前线程,对images list 中的所有 image 调用递归init,建立一个新的images list
for (uintptr_t i=0; i < images.count; ++i) {
images.imagesAndPaths[i].first->recursiveInitialization(context, thisThread, images.imagesAndPaths[i].second, timingInfo, ups);
}
// If any upward dependencies remain, init them.
//递归流程
if ( ups.count > 0 )
processInitializers(context, thisThread, timingInfo, ups);
}
processInitializers()
函数是一个递归的过程,并且init所有的image,那么调用recursiveInitialization
函数,又是什么情况呢?这个流程真的是,越看越迷糊
继
续
看
吧
先加载依赖文件,再加载当前文件;这是因为依赖文件没有加载的话,当前文件是无法加载的;举例:
ViewA内有一个子ViewB,如果子ViewB没有加载完成的话,那么ViewA就没有办法引用ViewB,就导致ViewA无法完成;
在recursiveInitialization
函数中,加载文件后,系统都执行了notifySingle
函数,接下来我们分析这个函数;
notifySingle
函数
sNotifyObjCInit
原文地址:https://blog.csdn.net/weixin_37767095/article/details/127388673
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_9305.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!