精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Linux應用開發之進程和程序

嵌入式應用研究院 ? 來源:嵌入式應用研究院 ? 2023-06-04 16:35 ? 次閱讀

1.進程和程序

進程是一個可執行程序的實例,程序包含了一系列信息文件,這些信息描述了如何在運行期間創建一個進程:

二進制格式標識 :包含用于描述可執行文件格式的元信息,內核根據此信息來解釋文件中的其他信息:

a.out(匯編程序輸出)

COFF(通用文件格式)

ELF(可執行連接格式),目前大多數 linux 實現采用此種方式,具有更多優點

機器語言指令 :對程序算法進行編碼

程序入口地址 :表示程序開始執行時的起始指令位置

數據 :程序文件包含的變量初始值和程序使用的字面量值(比如字符串)

符號表及重定位表 :描述程序中函數和變量的位置及名稱,包括調試和運行時的符號解析(動態鏈接)

共享庫和動態鏈接信息 :程序文件所包含的一些字段,列出程序運行時需要使用的共享庫,以及加載共享庫的動態鏈接器的路徑名

其他信息 :程序文件還包含了很多其他信息,用以描述如何創建進程

可以用一個程序來創建很多進程。

從內核角度來看:

進程是內核定義的抽象的實體,并為該實體分配用以執行程序的各項系統資源。

進程由用戶內存空間和一系列內核數據結構組成,用戶內存空間包含了程序代碼及代碼所使用的變量,內核數據結構則用于維護進程狀態信息,其中包括:

進程相關的標識號

虛擬內存表

打開文件的描述表

信號傳遞和處理的相關信息

進程資源使用及限制

當前工作目錄

一些其他信息

2.進程號和父進程號

每個進程都有一個進程號 PID,是一個正數,唯一標識系統中的某個進程。

#include 
#include 

pid_t getpid(void);

getpid 返回值的數據類型是 pid_t ,專用于存儲進程號

Linux 內核限制進程號需要小于 32767(內核常量 PID_MAX 定義),新進程創建時,內核會按順序將一個可用的進程號分配給其使用,進程號到達 32767 的限制時,內核將重置進程號計數器為300(不是1)

#include 
#include 

pid_t getppid(void);

getppid 獲取父進程的進程號

看 /proc/PID/status 文件提供的 PPid 字段可以獲知每個進程的父進程

pstree 命令可以查看進程家族樹

3.進程內存布局

每個進程所分配的內存都由很多虛擬內存邏輯劃分的部分,稱之為 "段":

文本段 :包含了進程運行的程序機器語言指令,文本段是只讀的,也是可共享的

初始化數據段 :包含顯式初始化的全局變量和靜態變量

未初始化數據段 :包含了未顯示初始化的全局變量和靜態變量,程序啟動之前,系統將本段內所有內存初始化為0,這個段又稱為 BSS(block started by symbol) 段。未初始化的全局變量和靜態變量與初始化的全局變量和靜態變量分開放的原因:

程序在磁盤存儲時,沒有必要為未經初始化的變量分配存儲空間

可執行文件只需要記錄未初始化數據段的位置及大小,直到運行時再有程序加載器來分配這一空間

棧:動態增長和收縮的段,由棧幀組成,系統會為每個當前調用的函數分配一個棧幀,棧幀中存儲了函數的局部變量,實參和返回值

堆 :在運行時為變量動態進行分配的一塊區域,堆頂端稱為 program break

初始化數據段和未初始化數據段又常被稱為用戶初始化數據段和零初始化數據段。

size 命令可以顯示二進制文件的文本段,初始化數據段,未初始化數據段的大小。

10c07d58-025d-11ee-90ce-dac502259ad0.png

argv 和 environ 用來存儲程序的命令行實參和環境列表

十六進制的地址會因為內核配置和程序鏈接選項的差異而有所不同

灰色區域表示這些范圍在進程虛擬地址空間中不可用,也就是說沒有為這些區域創建頁表

4.虛擬內存管理

linux 內核采用虛擬內存管理技術,該技術利用了大多數程序的一個典型特征,即訪問局部性:要求高效使用 CPU 和 RAM(物理內存)資源:

空間局部性 :程序傾向于訪問在最近訪問過的內存地址附近的內存(指令是順序執行的)

時間局部性 :程序傾向于在不久將來再次訪問最近剛訪問的內存地址(由于循環)

正是因為局部性特征,使得程序即便在僅有部分地址空間存在于 RAM 中,依然可以獲得執行。

虛擬內存的規劃之一就是將每個程序使用的內存切割成小型的,固定大小的 "頁" 單元。相應的,將 RAM 劃分成一系列與虛擬頁尺寸相同的頁幀:

任意時刻,每個程序僅有部分頁駐留在物理內存頁幀中,這些頁構成了所謂的駐留集

程序未使用的頁拷貝保存在交換區,交換區是磁盤空間中的保留區域,是作為計算機 RAM 補充,僅在需要時才會載入物理內存

若進程欲訪問的頁面并未駐留在物理內存中,將會發生頁面錯誤(page fault),內核即刻掛起進程的執行,同時從磁盤中將該頁面載入內存

頁面的大小通常為 4096 個字節,但也有的實現使用更大的頁面。

內核中維護了一張頁表,該頁表描述了每頁在進程虛擬地址空間中的位置:頁表中的每個條目要么指出這個虛擬頁面在 RAM 中的具體位置,要么表明其駐留在磁盤上。

進程虛擬地址空間中,并非所有的地址范圍都需要頁表條目:

由于可能存在大段的虛擬地址空間并未投入使用,因此也不需要為其維護頁表條目

進程試圖訪問的地址無頁表條目與之對應,那么進程將收到 SIGSEGV 信號

由于內核能夠為進程分配和釋放頁和頁表條目,所以進程的有效地址范圍在其聲明周期內可以發生變化:

棧向下增長超出了之前未達到的位置

堆中分配或者釋放內存時引起 program break 位置的變化

調用 shmat() 連接 System V 共享內存區或者調用 shmdt() 脫離共享內存區時

調用 mmap() 創建內存映射時,或者調用 munmap() 解除內存映射時

10e62206-025d-11ee-90ce-dac502259ad0.png

虛擬內存的實現需要硬件中分頁內存管理單元(PMMU)的支持,PMMU 把要訪問的每個虛擬地址轉換成相應的物理內存地址,當特定虛擬內存地址所對應的頁沒有駐留在 RAM 中時,將以頁面錯誤通知內核。

虛擬內存管理使虛擬地址空間與 RAM 物理地址隔離,帶來的優點:

進程與進程,進程與內核相互隔離,一個進程不能讀寫另一個進程或內核中的數據,因為每個進程的頁表條目指向 RAM 或交換分區中不同的物理頁面集合

適當情況下,兩個或者更多的進程能夠共享內存,因為內核可以使不同進程的頁表條目指向相同的 RAM 頁,經常發生在下面的情形:

多個程序執行相同的程序文件或者加載相同的共享庫時,會共享一份代碼副本

可以使用 shmget() 和 mmap() 等系統調用顯式的請求與其它進程共享內存區,這么做是出于通信的目的

便于實現內存保護機制,可以對頁表條目進行標記,以表示相關頁面內容是可讀、可寫、可執行,多個進程共享 RAM 頁面時,允許每個進程對內存采取不同的保護措施,例如一個進程可能以只讀方式訪問某頁面,而另一進程則以讀寫方式訪問同一頁面

程序員和編譯器、鏈接器之類的工具無需關注在 RAM 中的物理布局

因為需要駐留在內存中的僅是程序的一部分,所以程序在加載和運行時都很快,而且,一個進程所占用的虛擬內存大小能夠超出 RAM 容量

每個進程使用的 RAM 減少了,RAM 中同時可以容納的進程數量就增多了,從而任意時刻,CPU 都可執行至少一個進程,CPU 的利用率也會提高

5.棧和棧幀

11023fea-025d-11ee-90ce-dac502259ad0.png

棧駐留在內存的高端,并向下增長,專用寄存器——棧指針(stack pointer),用于跟蹤當前棧頂。

每次函數調用時,會在棧上新分配一幀,每當函數返回,再從棧上將此幀移去。

通常將這里的棧稱為用戶棧,以便與內核棧加以區分,內核棧是每個進程駐留在內核內存中的內存區域,在執行系統調用的過程中供內核內部函數調用使用。

每個用戶棧包括的信息:

函數實參和局部變量

函數調用鏈接信息

6.命令行參數

111a1d86-025d-11ee-90ce-dac502259ad0.png

argc 表示命令行參數的個數

argv 是指向命令行參數的指針數組,每一個參數都是以空字符 null 結尾的字符串,第一個參數,即 argv[0] 通常是程序名,argv[argc] 是 NULL

要想從程序內的任一位置訪問部分或者全部內容,還有兩種方法:

/proc/PID/cmdline 文件可以讀取任一進程的命令行參數,每個參數都以 null 字節終止,程序可以通過 /proc/self/cmdline 文件訪問自己的命令行參數

GNC C 語言庫提供兩個全局變量,可以在程序中內獲取調用該程序時的程序名稱,即命令行的第一個參數,定義 _GNU_SOURCE 后,從 中獲取:

program_invocation_name :提供了用于調用該程序的完整路徑名

program_invocation_short_name :提供了程序基本名稱

argv 駐留于進程棧之上的一個內存區域,此區域可存儲的字節數有上限要求, 中的 ARG_MAX 常量規定了其大小,一般要求下限是 4096 個字節,調用 sysconf(_SC_ARB_MAX) 確定此值。

7.環境列表

每個進程都有一個與其相關的稱之為環境列表的字符串數組,或者簡稱為環境:

環境是 "名稱-值" 的成對集合

常將列表中的名稱稱為環境變量

新進程在創建時會繼承其父進程的環境副本,這種傳遞是單向的,一次性的,子進程創建后,父、子進程均可各自修改自己的環境變量,且這些變更對對方是不可見的

環境變量常用于 shell 中,通過在自身環境變量中存放變量值,shell 可以確保將這些值傳遞給其所創建的進程

可以通過設置環境變量來改變一些庫函數的行為

大多數 shell 使用 export 添加環境變量:

export SHELL=/bin/bash

printenv 命令可以顯示當前的環境列表,export 命令不加任何參數時也可以達到此目的。

通過 /proc/PID/environ 文件可以檢查任一進程的環境列表。

8.從程序中訪問環境

C 語言使用全局變量 char** environ 訪問列表。

1133efa4-025d-11ee-90ce-dac502259ad0.png

#include 

char *getenv(const char *name);

根據換了變量的名稱,返回相應的字符串指針,如果該環境變量不存在則返回 NULL

9.修改環境

#include 

int putenv(char *string);

向調用進程的環境添加一個新的變量或者修改一個已經存在的變量值

string 是指向 name=value 形式的字符串

調用函數后,該字符串就稱為環境變量的一部分,environ 變量的某一元素的指向與 string 參數指向相同,而非 string 參數指向字符串的副本,因此:

后續修改 string 將會影響進程的環境

string 參數不能是棧空間變量

調用失敗返回非 0 值,而不是 -1

glibc 中將 putenv 做了非標準擴展,如果 string 中不包含 =,環境列表將會移除以 string 命名的環境變量

#include 

int setenv(const char *name, const char *value, int overwrite);

會為 name=value 的字符串分配內存緩沖區,并將 name 和 value 所指的字符串復制到緩沖區,創建一個新的環境變量

name 結尾處 和value開始處不要加 =,因為 setenv() 會自動添加

name 的環境變量在環境中已經存在,且參數 overwrite 的值是0,則 setenv() 不改變環境,如果 overwrite 的值非0,則 setenv() 將總是改變環境

之后修改 name 和 value 以及使用棧上變量都不會有影響

#include 

int unsetenv(const char *name);

將 name 指定的環境變量從環境列表中移除

#include 

int clearenv(void);

清除環境變量,即設置 environ=NULL

需要預定義 _SVID_SOURCE 或者_BSD_SOURCE

使用 setenv 和 clearenv() 可能導致內存泄漏:

調用 setenv 分配一塊內存緩沖區,隨即稱為進程環境的一部分,調用 clearenv 并不會釋放上述的緩沖區

但是一般這不會稱為一個問題,因為程序一般在開始處調用 clearenv() 以清除繼承自父進程的環境

10.執行非局部跳轉

#include 

int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val);

setjmp() 和 longjmp() 可執行非局部跳轉,"非局部" 指的是跳轉目標為當前執行函數之外的某個位置

setjmp() 調用為后續 longjmp() 調用執行的跳轉確立了跳轉目標,該目標就是程序發起 setjmp() 調用的位置

通過查看 setjmp() 返回的整數值,可以區分 setjmp() 是初始返回還是第二次返回:

初始調用返回 0

后續 "偽返回" 的返回值為 longjmp() 調用中 val 參數指定的任意值,如果 val 指定為 0,則 longjmp() 實際返回時用 1 替換

setjmp() 將環境變量 env 參數中,調用 longjmp() 時必須指定相同的 env 變量,因為跳轉發生在不同的函數,所以:

env 定義為全局變量

env 作為函數的參數傳遞,此種方法比較少見

調用 setjmp() 時,env 除了保存當前進程的其他信息外,還保存了程序計數器(指向當前正在執行的機器語言指令) 和棧指針寄存器(標記棧頂)的副本,這些信息保證后續 longjmp() 調用完成后執行兩個關鍵的操作:

將發起 longjmp() 調用的函數與之前調用 setjmp() 的函數之間的函數棧從棧上剝離,稱為棧解開,這是通過將棧指針寄存器重置為 env 參數的保存值

重置程序計數寄存器,使得程序得以從初始的 setjmp() 調用位置繼續執行,這是通過 env 參數中的程序計數寄存器實現的

編譯器優化會重組程序的指令執行順序,并在 CPU 寄存器中而非 RAM 中存儲某些變量,這些優化不會將 setjmp() 和 longjmp() 考慮在內,因而當有優化時,可能出錯。

程序中應該盡可能避免使用非局部跳轉。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • Linux
    +關注

    關注

    87

    文章

    11227

    瀏覽量

    208920
  • 計數器
    +關注

    關注

    32

    文章

    2253

    瀏覽量

    94351
  • PID控制
    +關注

    關注

    10

    文章

    460

    瀏覽量

    40039
  • LINUX內核
    +關注

    關注

    1

    文章

    316

    瀏覽量

    21617
  • 機器語言
    +關注

    關注

    0

    文章

    35

    瀏覽量

    10744

原文標題:Linux應用開發之進程和程序

文章出處:【微信號:嵌入式應用研究院,微信公眾號:嵌入式應用研究院】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    Linux開發_Linux進程編程

    介紹Linux進程概念、進程信號捕獲、進程管理相關的命令的使用等知識點。
    的頭像 發表于 09-17 15:38 ?1326次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>開發</b>_<b class='flag-5'>Linux</b>下<b class='flag-5'>進程</b>編程

    Linux進程間如何實現共享內存通信

    這次我們來講一下Linux進程通信中重要的通信方式:共享內存作為Linux軟件開發攻城獅,進程間通信是必須熟練掌握的重要技能,而共享內存是在
    發表于 04-26 17:14 ?675次閱讀

    Linux進程相關知識

    進程是在你的系統上運行的程序。它們由內核管理,每個進程都有一個與關聯的ID,稱為進程ID(PID)。這個PID是按照
    發表于 08-09 10:02 ?286次閱讀
    <b class='flag-5'>Linux</b>下<b class='flag-5'>進程</b>相關知識

    Linux下的進程結構

    `#嵌入式培訓#華清遠見嵌入式linux學習資料《Linux下的進程結構》,進程不但包括程序的指令和數據,而且包括
    發表于 08-05 11:05

    Linux學習雜談】進程的誕生和消失

    死機。 來看下幾個進程的概念:僵尸進程:(1)子進程先于父進程結束Linux系統設計的時候,當每一個進程
    發表于 09-01 20:38

    Linux進程管理

    Linux進程管理
    發表于 05-20 10:53

    Linux進程管理

    Linux進程管理 本章主要介紹進程的概念、狀態、構成以及Linux進程的相關知識。 掌握進程
    發表于 04-28 14:57 ?0次下載

    嵌入式Linux應用程序開發詳解

    。接著系統地講解了嵌入式linux的環境搭建,以及嵌入式linux的i/o與文件系統的開發進程控制開發
    發表于 06-17 17:24 ?61次下載

    進程間通信Linux進程間通信概述

    在上一章中,讀者已經知道了進程是一個程序的一次執行。這里所說的進程一般是指運行在用戶態的進程,而由于處于用戶態的不同進程之間是彼此隔離的,就
    發表于 10-18 16:21 ?0次下載

    進程控制開發編寫多進程程序實驗解析

    7.4.1 編寫多進程程序 1.實驗目的 通過編寫多進程程序,使讀者熟練掌握fork()、exec()、wait()和waitpid()等函數的使用,進一步理解在
    發表于 10-18 16:33 ?0次下載
    <b class='flag-5'>進程</b>控制<b class='flag-5'>開發</b><b class='flag-5'>之</b>編寫多<b class='flag-5'>進程</b><b class='flag-5'>程序</b>實驗解析

    基于Linux進程管理的詳細剖析

    上一篇,我們講到了Linux內核開發和應用程序開發,今天我們來講講Linux重點部分Linux
    的頭像 發表于 01-26 11:24 ?3709次閱讀
    基于<b class='flag-5'>Linux</b><b class='flag-5'>進程</b>管理的詳細剖析

    linux進程怎么查看

    Linux系統中,每個程序啟動后可以創建一個或多個進程。例如,提供Web服務的httpd程序,當有大量用戶同時訪問Web頁面時,httpd程序
    發表于 05-22 08:56 ?856次閱讀
    <b class='flag-5'>linux</b>的<b class='flag-5'>進程</b>怎么查看

    你們知道Linux進程是怎樣創建的嗎

    一顆樹的結構。就像下面這樣: ? ? 在Linux中,為了創建一個子進程,父進程用系統調用fork來創建子進程。fork()其實就是把父進程
    的頭像 發表于 11-09 10:46 ?3276次閱讀
    你們知道<b class='flag-5'>Linux</b>的<b class='flag-5'>進程</b>是怎樣創建的嗎

    嵌入式Linux應用程序開發圖形編程

    嵌入式Linux應用程序開發圖形編程資料分享。
    發表于 04-12 14:50 ?2次下載

    如何在Linux使用ps/pstree/top命令查看進程

    Linux 程序的運行實例稱為進程。有時在Linux工作時,您可能需要找出當前正在運行的進程
    的頭像 發表于 12-05 17:07 ?1790次閱讀