进程的诞生到灭亡

本文概绍

本文我们将谈论 程序与进程的密切关系,将给大佬们介绍一下何谓程序,何谓进程,它们之间的关系是什么,进程如何诞生,诞生之后它的数据摆在哪里,诞生之后它在做什么事情,又如何灭亡,灭亡之后谁给他们收尸,如何收尸等内容进行讲述它们之间扑朔迷离的神秘关系。我会以真实的数据陈述我的本文以达到各位顶级大佬能理解本文所表达的思想。如有错误在所难免。

本文讲述的内容只是冰山(珠穆朗玛峰那么高的知识量,在这里儿我写不完)一角(就是说只讲亿点点儿,不能讲多了)如欲系统完整的想把这座大山啃完可以找我(我会以实际数据告诉你这座大山的秘密在哪儿)

本文的内容跟:跟编程语言没有多大的关系。
本文适合什么样的朋友阅读:crud 3(或更高或新手)年以上的大佬
本文有没有瞧不起某人:没有,一点儿也没有
本文的完整知识点有没有讲完:没有,只讲冰山一角
本文阅读后有啥用:这个你自己填

本文程序的运行及测试环境

进程的诞生到灭亡
OS Name: Centos Linux
Kernel: Linux 4.18.0-147.5.1.el8_1.x86_64
Architecture: x86-64

x86:是指intel公司早期的cpu芯片以86结尾的一系列产品。
x86-64:是指以x86为基础架构扩展的64位程序,并兼容32位的程序。32位的CPU它的理论存储单元可寻址4G空间。

程序的运行流程说明

进程的诞生到灭亡

程序

什么是程序?程序就是已经编译好的含有程序指令+程序数据的集合文件
程序指令:叫法有指令码,机器码,机器指令码
指令:0和1的不同组合形成的指令,它的组合由intel芯片在研制时决定要输入何种组合的指令来驱动电路工作。intel芯片研制生产以后会编制相应的芯片手册告诉你编译器厂商,你开发的编译器编译的程序所生成的可执行文件里所包含的程序指令必须是intel芯片所识别的指令(芯片内部会预置指令),所以相关厂商在研发编译器时会参考intel芯片指令手册。

机器指令:由0和1组成,它的不同组合方式【就是不同的指令】输入到CPU电路里,从而驱动电路进行各种运算【如算术,逻辑,比较,移位等运算】等。由于它难以编写记忆,后来出现了指令助记符==》汇编诞生。

程序指令+程序数据:本质是二进制【在寄存器或是电路里就是高低电平表示的逻辑0和逻辑1】cpu在运行时会根据指令所在段和数据所在段区别它们是指令还是数据【由ELF文件构成的segment段决定】

你可能有以下误解

进程的诞生到灭亡

<?php
        echo 123;
        $pid = pcntl_fork();
        if($pid==0){
                pcntl_exec("/usr/bin/ls");
                echo "child ";
        }
        $pid = pcntl_wait($status);
        echo "exit pid=".$pid;

你可能认为上面文件所包含的内容是程序,而它只是ascii text 文本文件。

进程的诞生到灭亡

package main
import (
        "fmt"
)
func main(){
        fmt.Println(123)
}

.php .go .js .c .cpp .hpp .java .py 这种文件===》叫ascii text 文本文件,它们不叫我们严格意义所讲的程序。

程序文件它里面的内容到底是什么

在Linux系统中,程序文件它以ELF格式封装存储,内部含有大量的各种信息,其中就含有程序指令+程序数据,程序指令会放在代码段里.code,程序数据会放在数据段里.data,代码段里的数据权限为可读可执行,数据段的权限为可读可写。下面我们以php解释器及golang及c语言编译出来的文件给大家简单的介绍说明。
下面分别是golang/c程序的ascii text文本程序

进程的诞生到灭亡

#include <stdio.h>
#include <stdlib.h>

int x=10;
int y=300;

int main()
{

        return 0;
}
package main

var a int=300
var b byte=10

func main(){

}

编译后的文件

进程的诞生到灭亡

[root@iZ2vc2vrdlg6pu7memplcgZ bili]# file testxgo
testxgo: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
[root@iZ2vc2vrdlg6pu7memplcgZ bili]# file testxc
testxc: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=9a1de1124019b5f8acc727bef75bc302e8048ce5, not stripped

ELF: 是指 Executable and Linking Format (ELF) files 可执行可链接的文件s
可链接:由链接器链接【链接器链接时干什么了,做什么事情了?在此不提,因为这里讲不完】
上面的信息我们看到含有:ELF,X86-64,statically linked,dynamically linked,interpreter信息,它们表示的是这个文件是ELF格式封装存储的,ELF文件可分为静态链接,动态链接,运行的时候如果是静态链接则会找到ELF程序的入口地址,如果是动态链接会先运行linux ld解释器。
【这方面的内容可以讲几个章节,不过显然本文说不完】

  • testxgo ELF文件的段信息【程序指令段+程序数据段 .text .data .bss .rodata .rdata】

进程的诞生到灭亡

进程的诞生到灭亡

进程的诞生到灭亡

Section Headers是指ELF 里的段表,它描述了ELF文件各个段的信息,有段名,类型,大小,段地址,偏移,权限等信息。其中我们关注的是程序指令所在的段为.text【该段存储的是testx.go里的函数代码,已经被编译为二进制指令,至于怎么看到它的二进制指令以及怎么看到它的汇编指令,以及各个指令的字节大小,指令的内容,指令所在的虚拟地址(大家肯定用过指针,如函数指针,变量指针,在这里它跟ELF文件有何关系暂时不提),指令对应的汇编内容,指令对应的intel芯片指令,这里暂时不提,因为讲不完嘛】

  • testxc ELF文件的段信息【程序指令段+程序数据段 .text .data .bss .rodata .rdata】

进程的诞生到灭亡

进程的诞生到灭亡

上面是c和go编译器编译出来的ELF文件:它里面含有.text程序指令+.data[.bss .rdata等]程序数据段。启动运行的时候会装载到存储芯片里【如何装载,怎么装载,怎么证明装载了,装载什么了,在此不提】

在这里大家记住:它们编译好的程序指令放在.text里,程序数据放在.data里即可【详细后续有机会会说】

进程

进程:当操作系统把ELF文件装载(装载是什么?装载什么?如何装载?装载后干什么这里不提,因为要讲几个小时)到存储芯片里【这里涉及到程序装载的知识】启动运行时,它就是一个进程。

进程的诞生到灭亡

我们编译好的程序(如php解释器,python解释器,golang程序,c/c++程序,rust程序)它们已经是ELF文件(链接器已经给它们分配好地址与空间,如何分配的?分配的地址与空间是什么?能看吗?在哪里看啊?有啥用啊?在此不提),在linux终端下输入如php testx.php 即可启动或是输入./testxgo .testxc 即可运行。

那么它的诞生条件就是:1必须有一个ELF文件(程序指令+数据的集合) 2 必须由一个进程启动

注意

特别是写脚本语言的大佬要注意
进程的诞生到灭亡

你输入php test.php运行时,它在底层是这样执行的:/usr/bin/php test.php

进程的诞生到灭亡

这个python,nodejs 同理。

在linux你是这样启动程序的

这里拿golang编译好的文件举例(其它语言一样)

进程的诞生到灭亡

  • ./testxgo
    这本身是一个含程序指令+程序数据的ELF文件
  • [root@iZ2vc2vrdlg6pu7memplcgZ bili]# ./testxgo
    这一堆,你能输入并显示(回显)出来,是因为当前的终端它本身是个进程(叫控制进程,为什么叫控制进程,叫其它不可以吗?因为它自己连接了伪终端设备文件,直白点该进程连接了可以用于输入输出的键盘和显示器)
  • 控制进程(linux终端)的PID号查看

进程的诞生到灭亡

这里显示的31301就是指当前控制终端,如果你不相信,可以在开另一个终端,用kill(它的功能是用来发送中断信号,并不是用来杀死什么,如果你不相信,可以跟我聊几天,我证明给你看)

进程的诞生到灭亡

诞生过程

1 控制进程(终端)接收输入的.testxgo 文本
进程的诞生到灭亡
2 控制进程,创建一个新子进程(clone创建),然后装载testxgo ELF文件

进程的诞生到灭亡

装载此ELF文件后(内部会建立映射关系,如何映射?如何证明,哪里可以看到?在此不提)
运行程序(运行后会跟存储芯片即物理内存建立映射关系,如何证明,哪里可以看到,在此不提)
自此进程诞生
进程诞生后它的数据(你大佬诞生,你的身体就摆在地球上某个位置,同时给你分配身份证号来标示你的身份信息,同时与你相关的就是你住的房子,以及你和你父母兄弟间的关系),这个进程诞生也是如此,操作系统会分配进程标识PID,以及proc目录信息(这里的信息会告诉你一切有关的信息,什么信息呢,我在这里不讲,因为讲不完)。你可以在proc/PID下可以看到进程的一切信息(相当于你在这个地球上的生存时所产生的一切信息都在地球上)

3 进程执行结束后,灭亡

进程的诞生到灭亡

exit_group(0) 0是进程退出状态码。
4 控制进程收尸(进程回收)

进程的诞生到灭亡

当进程结束以后,它会被控制进程回收,自此跟进程有关的proc目录下的信息也会被删除

golang 进程诞生到灭亡

注:所有语言编写的程序全部一样,没有任何区别(这里拿它演示,只是目前它火而已)

filename:tcp1.go  go build -o tcp1 tcp1.go
package main
import (
        "fmt"
        "net"
)
func recv4socket(conn net.Conn){
        var buffer string=""
        for{
                temp:=make([]byte,1024)
                readBytes,err:=conn.Read(temp)
                if err!=nil{
                        fmt.Println("read err",err)
                }
                if readBytes==0{
                        fmt.Println("对端关闭了")
                        break
                }
                buffer+=string(temp[:readBytes])
                fmt.Println("接收到的数据为:",buffer)
        }
}
func main(){
        listner,err:=net.Listen("tcp4","0.0.0.0:9501")
        if err!=nil{
                fmt.Println("listen err",err)
                return
        }
        for{
                conn,err:=listner.Accept()
                if err!=nil{
                        fmt.Println("accept err",err)
                        continue
                }
                go recv4socket(conn)
        }
        listner.Close()
}

1 控制进程接收输入.tcp1

进程的诞生到灭亡

2 控制进程创建新的子进程(会分配虚拟地址空间,怎么分配的,怎么证明啊,在此不提)

进程的诞生到灭亡
控制进程创建新子进程clone,然后装载elf文件tcp1,然后调用wait4阻塞等待回收子进程
3 子进程执行代码

进程的诞生到灭亡
大家如果学过c/c++或是之前看过我讲过的网络编程(核心技术)必然非常熟悉这几个socket,bind,listen,accept
4 进程的数据(它诞生后放在哪里)

进程的诞生到灭亡
tcp1 是程序名,32458是主线程,以下是它的子线程(总为5个线程)

进程的诞生到灭亡

此目录下的信息是什么(它是进程的一切信息,相当于进程的秘密全在这儿,要不要全部解释一下呢?在此不提,因为讲不完)

5 进程结束时

进程的诞生到灭亡

总结

进程的诞生到灭亡

作业

看你心情,你高兴就做,不高兴就不做
1 控制进程是怎么连接到终端的?
2 进程控制是怎么获取输入的信息的如./tcp1
3 elf文件里的代码段和数据段长啥样?
4 golang程序如var a int=300 数据编译后放在哪里?怎么存放的?
5 进程诞生时这里的execve干什么事情了?
6 进程灭亡有几种情况?
7 进程还有哪些秘密?
8 上面的tcp1.go程序底层为什么调用的是socket,bind,listen,accept函数?为什么能调用?它跟用php,python,nodejs是一样的吗?
9 上面说到tcp1启动后有5个线程,真的假的?
10 上面一些内容老是说讲不完,或是后续再讲,作者这家伙想表达什么意思?
11 上面所讲的内容是不是不过瘾???
12 上面所讲的知识到底是php还是golang还是c还是python?
13 通过本文是不是了解的不够,还想得寸进丈了解更多?
14 想系统全面的了解???

本作品采用《CC 协议》,转载必须注明作者和本文链接
北风之神
本帖由系统于 2周前 自动加精
讨论数量: 5

是B站风神本人吗

2周前 评论
twggo 2周前

为啥感觉这个文章不是我这种智商可以看的 :joy:

2周前 评论

@Fx 如果有难度可看我讲的核心基础有完整系统的

2周前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
文章
1
粉丝
14
喜欢
10
收藏
0
排名:2191
访问:191
私信
所有博文