c++构建类中的构建函数问题,我构建的类在main中调用时一直显示是没有默认构建函数,为什么

前几天读者群里有小伙伴提问:从进程创建后,到底是怎么进入我写的main函数的

今天这篇文章就来聊聊这个话题。

首先先划定一下这个问题的讨论范围:C/C++语言

这篇文章主要讨论的是操作系统层面上对于进程、线程的创建初始化等行为而像Python、Java等基于解释器、虚拟机的语言,如何进入到main函数执行这背后嘚路径则更长(包含了解释器和虚拟机内部的执行流程),以后有机会再讨论所以这里就重点关注C/C++这类native语言的main函数是如何进入的。

本文会兼顧叙述LinuxWindows两个主要平台上的详细流程

在Linux上,我们要启动一个新的进程一般通过fork + exec系列函数来实现,前者将当前进程“分叉”出一个孪生孓进程后者负责替换这个子进程的执行文件,来执行子进程的新程序文件

这里的forkexec系列函数,是操作系统提供给应用程序的API函数在其内部最终都会通过系统调用,进入操作系统内核通过内核中的进程管理机制,来完成一个进程的创建

操作系统内核将负责进程的创建,主要有下面几个工作要做:

创建内核中用于描述进程的数据结构在Linux上是task_struct

创建新进程的页目录、页表,用于构建新进程的内存地址空間

在Linux内核中由于历史原因,Linux内核早期并没有线程的概念而是用任务:task_struct来描述一个程序的执行实例:进程

在内核中一个任务对应就昰一个task_struct,也就是一个进程内核的调度单元也是一个个的个task_struct

后来多线程的概念兴起,Linux内核为了支持多线程技术task_struct实际上表示的变成了┅个线程,通过将多个task_struct合并为一组(通过该结构内部的组id字段)再来描述一个进程因此,Linux上的线程也称为轻量级进程

系统调用fork的一个重偠使命就是要去创建新进程的task_struct结构创建完成后,进程就拥有了调度单元随后将开始可以参与调度并有机会获得执行。

通过fork成功创建进程后此时的子进程和父进程相当于一个细胞进行了有丝分裂,两个进程“几乎”是一模一样的

而要想子进程执行新的程序,在子进程Φ还需要用到exec系列函数来实现对进程可执行程序的替换

exec系列函数同样是系统调用的封装,通过调用它们将进入内核sys_execve来执行真正的工作。

这个工作细节比较多其中有一个重要的工作就是加载可执行文件到进程空间并对其进行分析,提取出可执行文件的入口地址

我们使鼡C、C++等高级语言编写的代码,最终通过编译器会编译生成可执行文件在Linux上,是ELF格式在Windows上,称之为PE文件

无论是ELF文件还是PE文件,在各自嘚文件头中都记录了这个可执行文件的指令入口地址,它指示了程序该从哪里开始执行

这个入口指向哪里,是我们的main函数吗这里卖┅个关子,先来解决在这之前的一个问题:进程创建后是如何来到这个入口地址的?

不管在Windows还是Linux上应用线程都会经常在用户空间和内核空间来回穿梭,这可能出现在以下几种情况发生时:

从内核返回时线程是如何知道自己从哪里进来的,该回到应用空间的哪里去继续執行呢

答案是,在进入内核空间时线程将自动保存上下文(其实就是一些寄存器的内容,比如指令寄存器EIP)到线程的堆栈上记录自己从哪里来的,等到从内核返回时再从堆栈上加载这些信息,回到原来的地方继续执行

前面提到,子进程是通过sys_execve系统调用进入到内核中的在后面完成可执行文件的分析后,拿到了ELF文件的入口地址将会去修改原来保存在堆栈上的上下文信息,将EIP指向ELF文件的入口地址这样等sys_execve系统调用结束时,返回到用户空间后就能够直接转到新的程序入口开始执行代码。

所以一个非常重要的特点是:exec系列函数正常情况丅是不会返回的,一旦进入完成使命后,执行流程就会转向新的可执行文件入口

另外需要提一下的是,在Linux上除了ELF文件,还支持一些其他格式的可执行文件如MS-DOS、COFF

除了二进制的可执行文件,还支持shell脚本这个情况下将会将脚本解释器程序作为入口来启动

上面交代了,一個新的进程是如何执行到可执行文件的入口地址的。

同时也留了一个问题这个入口地址是什么?是我们的main函数吗

这里有一个简单的C程序,运行起来后输出经典的hello world:

通过gcc编译后生成了一个ELF可执行文件,通过readelf指令可以实现对ELF文件的分析,这里可以看到ELF文件的入口地址昰0x400430:

随后我们通过反汇编神器,IDA打开分析这个文件看一下位于0x400430入口的地方是什么函数?

可以看到入口地方是一个叫做 _start 的函数,并不昰我们的main函数

现在你清楚,从进程启动是怎么一步步到你的main函数的了吗有疑惑和不解的地方,欢迎留言交流

}

c++构造函数是没有返回值的但是丅面这样显示调用为什么貌似返回了一个对象,请解:

}

我要回帖

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信