程式的誕生到滅亡

北風之神發表於2021-11-21

本文我們將談論 程式與程式的密切關係,將給大佬們介紹一下何謂程式,何謂程式,它們之間的關係是什麼,程式如何誕生,誕生之後它的資料擺在哪裡,誕生之後它在做什麼事情,又如何滅亡,滅亡之後誰給他們收屍,如何收屍等內容進行講述它們之間撲朔迷離的神祕關係。我會以真實的資料陳述我的本文以達到各位頂級大佬能理解本文所表達的思想。如有錯誤在所難免。

本文講述的內容只是冰山(珠穆朗瑪峰那麼高的知識量,在這裡兒我寫不完)一角(就是說只講億點點兒,不能講多了)如欲系統完整的想把這座大山啃完可以找我(我會以實際資料告訴你這座大山的祕密在哪兒)

本文的內容跟:跟程式語言沒有多大的關係。
本文適合什麼樣的朋友閱讀: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目錄下的資訊也會被刪除

注:所有語言編寫的程式全部一樣,沒有任何區別(這裡拿它演示,只是目前它火而已)

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 協議》,轉載必須註明作者和本文連結
北風之神

相關文章