主页(http://www.175shouji.com):iOS App从点击到启动
▲点击上方“”关注即可免费学习iOS开发
原文链接:
程序启动之前
从exec()开始
main()函数是整个程序的入口,在程序启动之前,系统会调用exec()函数。在Unix中exec和system的不同在于,system是用shell来调用程序,相当于fork+exec+waitpid,fork 函数创建子进程后通常都会调用 exec 函数来执行一个新程序;而exec是直接让你的程序代替原来的程序运行。
system 是在单独的进程中执行命令,完了还会回到你的程序中。而exec函数是直接在你的进程中执行新的程序,新的程序会把你的程序覆盖,除非调用出错,否则你再也回不到exec后面的代码,也就是当前的程序变成了exec调用的那个程序了。
UNIX 提供了 6 种不同的 exec 函数供我们使用。
#include
int execl(const char *pathname, const char *arg0, ... /* (char *)0 */);
int execv(const char *pathname, char *const argv[]);
int execle(const char *pathname, const char *arg0, ... /* (char *)0, char *const envp[] */);
int execve(const char *pathname, char *const argv[], char *const envp[]);
int execlp(const char *filename, const char *arg0, ... /* (char *)0 */);
int execvp(cosnt char *filename, char *const argv[]);
通过分析我们发现,含有 l 和 v 的 exec 函数的参数表传递方式是不同的。含有 e 结尾的 exec 函数会传递一个环境变量列表。含有 p 结尾的 exec 函数取的是新程序的文件名作为参数,而其他exec 函数取的是新程序的路径。
如果函数出错则返回-1,若成功则没有返回值。其中只有execve是真正意义上的系统调用,其它都是在此基础上经过包装的库函数。
exec函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件。这里的可执行文件既可以是二进制文件,也可以是任何Unix下可执行的脚本文件。
iOS 系统架构
Mac系统是基于Unix内核的图形化操作系统,Mac OS 和 iOS 系统架构的对比分析发现,Mac OS和iOS的系统架构层次只有最上面一层不同,Mac是Cocoa框架,而iOS是Cocoa Touch框架,其余的架构层次都是一样的。
核心OS层的驱动提供了硬件和系统框架之间的接口。然而,由于安全的考虑,只有有限的系统框架类能访问内核和驱动。iPhone OS提供了许多访问操作系统低层功能的接口集,iPhone 应用通过LibSystem库来访问这些功能,这些接口集有线程(POSIX线程)、网络(BSD sockets)、文件系统访问、标准I/O、Bonjour和DNS服务、现场信息(Locale Information)、内存分配和数学计算等。
Core Services在Core OS基础上提供了更为丰富的功能, 它包含了Foundation.Framework和Core Foundation.Framework, 之所以叫Foundation,就是因为它提供了一系列处理字符串,排列,组合,日历,时间等等的基本功能。
Foundation是属于Objective-C的API,Core Fundation是属于C的API。另外Core servieces还提供了如Security(用来处理认证,密码管理,安全性管理等), Core Location, SQLite和Address Book等功能。
核心基础框架(CoreFoundation.framework)是基于C语言的接口集,提供iPhone应用的基本数据管理和服务功能。该框架支持Collection数据类型(Arrays、 Sets等)、Bundles、字符串管理、日期和时间管理、原始数据块管理、首选项管理、URL和Stream操作、线程和运行循环(Run Loops)、端口和Socket通信。
核心基础框架与基础框架是紧密相关的,它们为相同的基本功能提供了Objective-C接口。如果开发者混合使用Foundation Objects 和Core Foundation类型,就能充分利用存在两个框架中的"toll-free bridging"技术(桥接)。toll-free bridging使开发者能使用这两个框架中的任何一个的核心基础和基础类型。
静态链接库与动态链接库
iOS中的相关文件有如下几种:Dylib,动态链接库(又称 DSO 或 DLL);Bundle,不能被链接的 Dylib,只能在运行时使用 dlopen() 加载,可当做 macOS 的插件。Framework,包含 Dylib 以及资源文件和头文件的文件夹。
动态链接库是一组源代码的模块,每个模块包含一些可供应用程序或者其他动态链接库调用的函数,在应用程序调用一个动态链接库里面的函数的时候,操作系统会将动态链接库的文件映像映射到进程的地址空间中,这样进程中所有的线程就可以调用动态链接库中的函数了。动态链接库加载完成后,这个时候动态链接库对于进程中的线程来说只是一些被放在地址进程空间附加的代码和数据,操作系统为了节省内存空间,同一个动态链接库在内存中只有一个,操作系统也只会加载一次到内存中。
因为代码段在内存中的权限都是为只读的,所以当多个应用程序加载同一个动态链接库的时候,不用担心应用程序会修改动态链接库的代码段。当线程调用动态链接库的一个函数,函数会在线程栈中取得传递给他的参数,并使用线程栈来存放他需要的变量,动态链接库函数创建的任何对象都为调用线程或者调用进程拥有,动态链接库不会拥有任何对象。如果动态链接库中的一个函数调用了VirtualAlloc,系统会从调用进程的地址空间预定地址,即使撤销了对动态链接库的映射,调用进程的预定地址依然会存在,直到用户取消预定或者进程结束。
静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib 中的指令都全部被直接包含在最终生成的包文件中了。但是若使用 动态链接库,该 动态链接库 不必被包含在最终包里,包文件执行时可以“动态”地引用和卸载这个与 安装包 独立的 动态链接库文件。静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。
Linux中静态函数库的名字一般是libxxx.a;利用静态函数库编译成的文件比较大,因为整个函数库的所有数据都会被整合进目标代码中。编译后的执行程序不需要外部的函数库支持,因为所有使用的函数都已经被编译进去了。当然这也会成为他的缺点,因为如果静态函数库改变了,那么你的程序必须重新编译。
动态函数库的名字一般是libxxx.so,相对于静态函数库,动态函数库在编译的时候并没有被编译进目标代码中,你的程序执行到相关函数时才调用该函数库里的相应函数,因此动态函数库所产生的可执行文件比较小。由于函数库没有被整合进你的程序,而是程序运行时动态的申请并调用,所以程序的运行环境中必须提供相应的库。动态函数库的改变并不影响你的程序,所以动态函数库的升级比较方便。