Linux Ubuntu虛擬機 實驗分析

實踐項目名稱:     Linux內核分析                 

一、實踐目的

1. 在Ubuntu系統條件下,學會使用虛擬機安裝Ubuntu操作系統

2. 掌握組創建、用戶創建,並授權。

3. 學會配置C語言編程環境,完成C程序運行。

4. 學會使用fork()系統調用進行進程創建,查看創建的進程。

5. 學習創建如下的進程關係,並解釋創建過程。

二、實踐內容

1. 在計算機上用虛擬機形式安裝linux,虛擬機可採用VmWare或VirtualBox。Linux用Ubuntu或CentOS。

2. 完成組創建、用戶創建,並授權。分別命名爲testgroup和testuser.

3. 配置C語言編程環境,完成hello world程序運行。

4. 使用fork()系統調用進行進程創建,查看創建的進程。

5. 創建如下的進程關係,並解釋創建過程。

三、實踐的具體實現過程

  1. 虛擬機的構建:

首先,下載Vmware虛擬機安裝Ubuntu操作系統。

上圖爲Ubuntu官網下載。下載地址爲:(建議使用迅雷下載)

https://ubuntu.com/download/server/thank-you?version=19.04&architecture=amd64

 

打開Vmware軟件,選擇新建虛擬機選項。

 

在創建Ubuntu虛擬機時,要在選擇系統界面提示框中選擇“稍後安裝操作系統”;

若選擇“安裝程序光盤映像文件”會導致虛擬機在安裝系統時會出現死機的狀態。

選擇Linux

選擇Ubuntu64

在創建完成一個虛擬機後(未安裝系統),要對其硬件進行配置。

對於Ubuntu虛擬機,要求最小內存爲2GB

並且我們要對虛擬機安裝Ubuntu系統,在“虛擬機設置中”,選擇“CD/DVD”,找到“使用ISO映像文件”選項,找到剛剛下載的Ubuntu系統並確定。虛擬機再次打開是便會自動安裝Ubuntu系統了。

現在這就是一臺完整的Ubuntu虛擬機了。

2完成組創建、用戶創建,並授權。分別命名爲testgroup和testuser.

  1. 登錄Ubuntu系統,查看所有用戶,需要查看/etc/passwd文件

cat/etc/passwd

  1. 創建一個用戶,使用命令

useradd newuser -m

useradd後是用戶的名字

-m代表創建用戶的同時創建該用戶的home目錄

  1. 提示沒有權限,使用sudo授權創建

sudo useradd newuser -m

查看/home目錄已經有了新用戶newuser的文件夾

用第一步的查看/etc/passwd文件內容,也有了newuser用戶

創建用戶時候,會自動創建該用戶所在組,默認組名和用戶名一樣。通過

cat /etc/group 

查看

  1. 切換到newuser用戶,命令如下

su - newuser

  1. 不能正常切換,需要先設置密碼,命令如下:

sudo passwd newuser

再次進行切換,可以正常登錄

su - newuser

‘-’的意義是切換過來直接到用戶的主目錄

可以使用pwd查看當前目錄

  1. 默認新建的用戶沒有sudo權限,要賦給sudo權限要在有sudo權限的用戶使用以下命令

sudo usermod -a -G adm newuser

sudo usermod -a -G sudo newuser

這兩句是將newuser用戶添加到adm組和sudo組

  1. 再切回newuser用戶使用sudo創建文件夾就可以了

 

  1. 配置C語言編程環境,完成hello world程序運行。

先在虛擬機上安裝Vim軟件,用於編寫代碼。

編寫一個test1.cpp的程序,

Ctrl + Alt + T打開Ubuntu下的cmd

使用cd命令打開test1.cpp所在的文件夾的位置。

再對其進行編譯。使用g++ test.cpp -o test1(如果是C則爲gcc)

所使用的語言 + 文件名.文件類型 + -o 文件名

會出現test1這個文件,在cmd上運行這個文件即可。

 

  1. 使用fork()系統調用進行進程創建,查看創建的進程。

實驗代碼: 

#include<stdio.h>

#include<unistd.h>

#include<sys/types.h>

int main()

{

   printf("1\n");

   printf("PID = %d  ",getpid());

   fork();

   printf("PID = %d, PPID = %d\n",getpid(),getppid());

   printf("2\n");

   return 0;

}

對其進行編譯:

我們執行完fork.c文件後發現多一個2

這是我們獲取進程的代碼,PID爲當前進程ID,PPID爲父進程ID

生成兩個進程,其中一個進程是原先的進程,而且父進程不一樣

 

 

分析內核:fork系統之前只有一個進程,fork之後又兩個進程,其實在fork之前有一個PCB(內核state_struct)指向代碼段,數據段,堆棧段,fork之後生成新的PCB,複製一份代碼段,數據段,堆棧段,PCB函數指針指向代碼段,數據段,堆棧段。此時有兩個PCB,則有兩個進程。複製前PCB函數指針指向fork系統調用,複製完也是指向系統調用。故代碼都是從fork系統調用開始跑,所以fork之後的代碼實現兩次(兩個進程各自實現一次)。

 

 

然後我們對fork()函數進行分析。

實驗代碼:

#include<unistd.h>

#include<sys/types.h>

#include<stdio.h>

int main()

{

    int cnt = 0;

    int flag = fork();;

    if(flag > 0)

    {

        printf("cnt = %d\n",cnt);

        printf("cnt address = 0x%x\n",&cnt);

    }

    else if(falg == 0)

    {

        cnt =10;

        printf("cnt = %d\n",cnt);

        printf("cnt address = 0x%x\n",&cnt);

    }

    else

    {

        printf("ERROR!\n");

    }

    return 0;

}

實驗截圖:

實驗分析:

我們必須知道fork系統調用有返回值,返回值爲0則是子進程,大於0爲父進程

我們可以看到結果是值不一樣,但是地址卻是一樣的,值不一樣是因爲我們賦值了,所以拷貝的時候也變化了,但是地址還是一樣

 

內核分析:其實內核裏面有一個邏輯內存又成虛擬內存,父進程指向虛擬內存訪問到變量count,當拷貝的時候,子進程拷貝了父進程的虛擬內存,所以他們的地址是一樣。當我們變量改變的時候,其實物理內存是改變了,但是是虛擬內存指向的變量,而我們在終端只能看到虛擬內存,並不能看到物理內存。(這種方式稱爲copy-on-write 寫入時候才拷貝,訪問的時候還是原來變量)爲什麼要有這種方式呢?有時候父進程太大,子進程又用得內存不多,原樣拷貝父進程浪費太多內存,所以在寫的時候纔來分配內存。

 

 

  1. 創建如下的進程關係,並解釋創建過程。

我們知道,Linux下父進程可以使用fork 函數創建子進程,但是當父進程先退出後,子進程會不會也退出呢?

實驗代碼:

#include<unistd.h>

#include<sys/types.h>

#include<stdlib.h>

#include<stdio.h>

int main()

{

    int pronum;

    int pid = fork();

    int cnt = 0;

    while(pid == 0)

    {

        printf("PPID = %d\n",++pronum);

        cnt++;

        if(cnt>=3)

        {

            exit(0);

        }

    }

    printf("PID = %d\n",pronum);

    return 0;

}

實驗截圖:

 

代碼分析:

首先按代碼解釋應該先順序執行三個子進程,即pronum100110021003.而後創建完三個子進程後,系統被強制退出exit(0);父進程1000不執行。

但是由執行結果可知:

可以看出父進程1000首先退出,退出前子程序的pronum1003 退出後子進程的PPID變爲了 1.說明在執行子進程前,系統會先執行父進程,父進程退出後的子進程由 init 超級進程1領養。而該進程是不絕不會退出的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章