前几天,读者群里有小伙伴提问:从进程创建后,到底是怎么进入我写的main函数的?
今天这篇文章就来聊聊这个话题。
首先先划定一下这个问题的讨论范围:C/C++语言
这篇文章主要讨论的是操作系统层面上对于进程、线程的创建初始化等行为,而像Python、Java等基于解释器、虚拟机的语言,如何进入到main函数执行,这背后的路径则更长(包含了解释器和虚拟机内部的执行流程),以后有机会再讨论。所以这里就重点关注C/C++这类native语言的main函数是如何进入的。
本文会兼顾叙述Linux和Windows两个主要平台上的详细流程。
创建进程
第一步,创建进程。
在Linux上,我们要启动一个新的进程,一般通过fork + exec系列函数来实现,前者将当前进程“分叉”出一个孪生子进程,后者负责替换这个子进程的执行文件,来执行子进程的新程序文件。
这里的fork、exec系列函数,是操作系统提供给应用程序的API函数,在其内部最终都会通过系统调用,进入操作系统内核,通过内核中的进程管理机制,来完成一个进程的创建。
操作系统内核将负责进程的创建,主要有下面几个工作要做:
创建内核中用于描述进程的数据结构,在Linux上是task_struct
创建新进程的页目录、页表,用于构建新进程的内存地址空间
在Linux内核中,由于历史原因,Linux内核早期并没有线程的概念,而是用任务:task_struct来描述一个程序的执行实例:进程。
在内核中