初始化
在我们写完程序之后,经过编译之后,我们有一个指令叫做系统调用指令,这个指令帮助我们将程序交给操作系统执行,然后操作系统联合相关硬件资源交给CPU执行。比如这个程序可能是发送网络数据包,那么操作系统就会将这个网络数据包给发送出去。
用简单的代码可以表示上面这个过程:
def StateMachine():b = sys_read()if b == 0:sys_write('I got a zero')else:sys_write('I got a one')def main():sys_spawn(StateMachine)
有了上面的认知我们知道操作系统会给我们去调度资源,执行操作,
那么操作系统启动的第一个进程是什么呢?
int main() {pid_t pid;pid = fork();if (pid == -1) {printf("failed");return 0;} else if (pid == 0) {printf("process son %d\n", getpid());sleep(5);exit(0);} else {printf("process facther %d\n", getpid());waitpid(pid, NULL, 0);}return 0;}
关于fork函数的返回值也是非常的有意思,主要有下面这三种:
1. 返回值负数:如果 fork() 函数返回一个负值,这表示创建子进程失败了。最常见的原因是系统资源限制,例如可用的内存不足以创建新的进程。在这种情况下,你应该检查错误码 errno 来获取更详细的错误信息。
2. 返回值为0:如果 fork() 函数返回0,这表示它在子进程中被调用。也就是说,执行 fork() 之后的代码将只在子进程中运行。父进程不会执行这个返回值之后的代码。
3. 返回值为正数:如果 fork() 函数返回一个正值,这表示它在父进程中被调用。返回的值是新创建的子进程的进程ID(PID)。父进程可以使用这个PID来对子进程进行进一步的操作,比如使用 `wait()` 或 `waitpid()` 函数等待子进程结束。
但是记这种API比较抽象,我们可以通过一个比较生动的例子来理解这个过程,我们可以将每一个段程序看作是一个状态机或者说是一个整体,每次执行fork的话都会将所有的状态机的信息都给复制一份。

此时我们需要考虑一个问题,那就是如果什么东西都需要复制一份的话,那么在性能上不会造成非常大的损耗么?在linux内核中并不会复制全部的内容而只会复制一份对应的虚拟页表, 这样就建立了关于虚拟地址和物理地址之间的联系,这样技术叫做cow,这个有机会再说😄。
exec函数
有了fork函数之后我门就可以一生二,二生三,三生万物了,接下来我们需要考虑的就是在相应的进程中执行对应的程序, 那么就需要使用一个叫做execve的系统调用。
exec将一个新程序加载到当前进程的内存中并执行, 旧程序的内存页将刷出,其内容将替换 为新的数据。Linux提供的execve系统调用可以用于这个目的。
int do_execve(const char* filename,char * const argv[],char* const envp[]);
从状态机器角度来理解,将当前运行进程的重置成为一个可执行文件描述状态机的初始化状态。

比如当我们编译一个文件的时候,比如gcc -c xxx,那么一定会有一个系统调用帮助我们将这个参数xxx给传入到对应的进程中去。
于是我们可以说exec允许对新的状态机设置参数argv和环境变量envp
进程的退出必须使用exit系统调用终止,这让内核有机会将该进程使用的资源给释放会系统, 在我们的操作系统中存在四种exit退出的方法。
但是这四种方法存在一些比较细微的一些区别, 比如说exit(0)这个是可以被我们的C编译器给识别的,其余两个是进行系统调用,可能无法被识别,这样就会导致不会出发atexit(func), 具体的情况可以通过下面这个代码去实验。
void func() {printf("Goodbye, Cruel OS World!\n");}int main(int argc, char *argv[]) {// This is a convenient mechanism foratexit(func);if (argc < 2) {// We can reference the return code $? in bash.// Our online judge also uses this return code.return EXIT_FAILURE;}if (strcmp(argv[1], "exit") == 0) {// This is a libc exit.exit(0);}if (strcmp(argv[1], "_exit") == 0) {// This is an immediate operating system exit.// This _exit() is provided by libc._exit(0);}if (strcmp(argv[1], "__exit") == 0) {// This is an even more "operating system" exit.syscall(SYS_exit, 0);}}

