1. 程式人生 > >Linux程序(二上)環境變數和程式地址空間

Linux程序(二上)環境變數和程式地址空間

環境變數

基本概念
*環境變數一般是指在作業系統中用來制定作業系統執行環境的一些引數,如:我們在編寫c/c++程式碼的時候,在連線的時候,我們從來不知道所連線的動態靜態庫在哪裡,但是照樣可以連線成功,生成可執行程式,原因就是有相關的環境變數幫助編譯器進行查詢
*環境變數通常具有某些特殊用途,在系統紅通常具有全域性特性

常見的環境變數
PATH* :指定命令的搜尋路徑
HOME* :指定使用者的主工作目錄
HISTSIZE* :指儲存歷史命令記錄的條數
SHELL* :當前shell,它通常是/bin/bash

檢視環境變數的方法
echo $NAME
和環境變數相關的命令
1 echo:顯示某個環境變數值
2 export:設定一個新的環境變數
3 env:顯示所有的環境變數
4 unset:清楚環境變數
5 set:顯示本地定義的shell變數和環境變數

通過程式碼如何獲得環境變數
1 通過命令列的第三個引數

int main(int argc,char *argv[],char *env[])
{
    int i=0;
    for(;env[i].i++)
    {
        printf("%s\n",env[i]);

    }
    return 0;
}

2 通過第三方變數environ獲取

int main(int argc,char *argv[])
{
    extern char **environ;
    int i=0;
    for(;environ[i];i++)
    {
        printf
("%s\n",environ[i]); } return 0; } //libc中定義的全域性變數environ指向環境變量表,environ沒有包含在任何標頭檔案裡面,所以在使用的時候,要加 extern宣告

另外,我們還可以通過系統呼叫來獲取或者設定環境變數

int main()
{
    printf("%s\n",get("PATH"));
    return 0;
}

環境變數有通常具有全域性屬性,可以被子程序繼承下去

int main()
{
    char *env=getenv("MYENV");
    if(env)
    {
        printf
("%s\n",env); } return 0; }

直接檢視發現沒有結果,說明了該環境變數根本就不存在,接下來我們進行下面的操作:
匯出環境變數:export MYENV=”hello world”
再次執行程式的時候,發現結果有了!說明:環境變數是可以被子程式繼承下去的

程式地址空間

直接上程式碼

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

int g_val = 0;
int main()
{
    pid_t id =fork();
    if(id<0)
    {
        perror("fork");
        return 0;
    }
    else if(id==0)
    {
        printf("chile[%d]:%d:%p\n",getpid(),g_val,&g_val);

    }
    else
    {
        printf("parent[%d]:%d:%p\n",getpid(),g_val,&g_val);
    }
    sleep(1);
    return 0;

}

這裡寫圖片描述

我們發現,輸出出來的變數值和地址是一模一樣的,接下來我們對變數進行修改

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

int g_val = 0;
int main()
{
    pid_t id =fork();
    if(id<0)
    {
        perror("fork");
        return 0;
    }
    else if(id==0)
    {
    g_val = 100;
        printf("chile[%d]:%d:%p\n",getpid(),g_val,&g_val);

    }
    else
    {
        printf("parent[%d]:%d:%p\n",getpid(),g_val,&g_val);
    }
    sleep(1);
    return 0;

}

這裡寫圖片描述
在這裡我們可以看到,父子程序輸出的地址還是一致的,但是變數內容不一樣了

由此我們可以得出以下結論:
1,變數內容不一樣,所以父子進城輸出的變數絕對不是同一個變數;
2,但是他們的地址是一樣的,那就說明了,該地址絕對不是實體地址
3,在Linux地址下,這種地址叫做虛擬地址
我們在用c/c++語言所看到的所有地址,都是虛擬地址!!!
實體地址,用於一概看不到,由os統一管理

os必須負責將虛擬地址轉換成實體地址

早期記憶體管理機制
要執行一個程式,會把這些程式全都裝入記憶體。
當計算機同時執行多個程式的時,必須保證這些程式用到的記憶體總量要小於計算機實際實體記憶體的大小。
程序地址空間不隔離。由於程序都是直接訪問實體記憶體,所以惡意程式就可以隨意的修改別的程序的記憶體資料,以達到破壞的目的。
記憶體使用效率低。
程式執行的地址不確定。

現在採取分段的方式
在編寫程式碼的時候,只要指明瞭所屬段,程式碼段和資料段中出現的所有的地址,都是從零開始,對映關係完全由作業系統維護。
CPU將記憶體分成了不同的段,於是指令和資料的有效地址並不是真正的實體地址,而是相對於段首地址的偏移地址。
這樣的效果是
因為段暫存器的存在,是的程序的地址空間得以隔離,越界問題很容易被判定出來
實際程式碼和資料中的地址,都是偏移量,所以第一條指令可以從0地址開始,系統會自動進行轉化對映,也就解決了程式執行的地址不確定問題。
但是,分段並沒有解決效能問題,在記憶體空間不足的情況下,依舊要換入換出整個程式或者整個段,無疑要造成記憶體和硬碟之間大量拷貝資料的情況,進而導致效能問題

上面的程式碼就說明問題同一個變數地址相同,其實就是虛擬地址相同,內容不同其實就是被頁表對映到了不同的實體地址