作業系統概念-程式控制 實驗報告

回鍋肉炒肉發表於2020-11-17

前言

  • Collatz 猜想:任意寫出一個正整數 N,並且按照以下的規律進行變換: 如果是個奇數,則下一步變成 3N+1;
    如果是個偶數,則下一步變成 N/2。 無論 N 是怎樣的一個數字,最終都無法逃脫回到谷底 1。 例如:如果 N=35,則有序列 35,
    106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1。

實驗內容一

實驗要求

  • 採用系統呼叫 fork(),編寫一個 C 程式,以便在子程式中生成這個序列。 要求:
    • 從命令列提供啟動數字
    • 由子程式輸出數字序列
    • 父程式等子程式結束後再退出。

實驗步驟

  • 直接建立子程式處理序列,等待子程式執行結束,父程式列印輸出。
	// 建立子程式
	pid = fork();
    if (pid < 0) {
        printf("Create child process error!\n");
    } else if (pid == 0) {
    	// 子程式計算結果
        printf("(Child process %d)The sequence is: %d ", getpid(), N);
        while (N != 1) {
            if (N % 2 == 0) 
                N /= 2;
            else
                N = 3 * N + 1;
            printf("%d ", N);
        }
        printf("\n");
    } else {
    	// 等待子程式執行結束,父程式列印結果
        wait(NULL);
        printf("(Parent process %d)Parent process exit!\n", getpid());
    }

執行結果

在這裡插入圖片描述在這裡插入圖片描述

實驗內容二

實驗要求

  • 以共享記憶體技術程式設計實現 Collatz 猜想。 要求在父子程式之間建立一個共享記憶體物件,允許子程式將序列內容寫入共
    享記憶體物件,當子程式完成時,父程式輸出序列。 父程式包括如下步驟: 建立共享記憶體物件(shm_open(), ftruncate(), mmap()) 建立子程式並等待他終止 輸出共享記憶體的內容 刪除共享記憶體物件。

實驗步驟

  1. 共享記憶體概念
      共享記憶體是System V版本的最後一個程式間通訊方式。共享記憶體,顧名思義就是允許兩個不相關的程式訪問同一個邏輯記憶體,共享記憶體是兩個正在執行的程式之間共享和傳遞資料的一種非常有效的方式。不同程式之間共享的記憶體通常為同一段實體記憶體。程式可以將同一段實體記憶體連線到他們自己的地址空間中,所有的程式都可以訪問共享記憶體中的地址。如果某個程式向共享記憶體寫入資料,所做的改動將立即影響到可以訪問同一段共享記憶體的任何其他程式。
  2. 共享記憶體原理
      在Linux中,每個程式都有屬於自己的程式控制塊(PCB)和地址空間(Addr Space),並且都有一個與之對應的頁表,負責將程式的虛擬地址與實體地址進行對映,通過記憶體管理單元(MMU)進行管理。兩個不同的虛擬地址通過頁表對映到物理空間的同一區域,它們所指向的這塊區域即共享記憶體。

共享記憶體原理圖

在這裡插入圖片描述

  1. 共享記憶體呼叫的函式
      LINUX共享記憶體的POSIX介面是shm_open這個函式。其實這個介面就是在/dev/shm目錄建立一個檔案,然後使用這個檔案來實現共享記憶體的功能。
    int shm_open(const char *name, int oflag, mode_t mode);建立或開啟一個共享記憶體,成功返回一個整數的檔案描述符,錯誤返回-1。 三個引數的意義:
    1.name:共享記憶體區的名字;
    2.標誌位:open的標誌一樣
    3.許可權位 ftruncate(fd, SIZE);已知共享記憶體fd,調整這一塊空間大小為SIZE

建立子程式

// 建立子程式
pid = fork();
if (pid < 0) {
    printf("Create child process error!\n");
}

建立共享記憶體

// 建立共享記憶體temp
shm_fd1 = shm_open("temp", O_CREAT|O_RDWR, 0777);
if (shm_fd1 < 0) {
    printf("Open shm_fd1 failed!\n");
    return -1;
}

處理序列並寫入共享記憶體

char cur_num[10] = {0};
            
// 存入序列頭N
sprintf(cur_num, "%d", N);
message = (char*)malloc(sizeof(char));
message = realloc(message, strlen(message) + strlen(cur_num));
sprintf(message, "%s%s", message, cur_num);

while (N != 1) {
    if (N % 2 == 0) {
        N /= 2;
    } else {
        N = 3 * N + 1;
    }
    
    // 存入下一個數字 
    sprintf(cur_num, "%d", N);
    message = realloc(message, strlen(message) + strlen(cur_num));
    sprintf(message, "%s %s", message, cur_num);
}

printf("(Child process %d)Done!\n", getpid());

// 寫入共享記憶體
ftruncate(shm_fd1, SIZE);
ptr1 = mmap(0, SIZE, PROT_WRITE, MAP_SHARED, shm_fd1, 0);
sprintf(ptr1, "%s", message);
ptr1 += strlen(message);

訪問共享記憶體列印序列

int   shm_fd2;
char* ptr2;

wait(NULL);
printf("(Parent process %d)The sequence is:\n", getpid());

// 訪問共享記憶體temp
shm_fd2 = shm_open("temp", O_CREAT|O_RDWR, 0777);
if (shm_fd2 < 0) {
    printf("Open shm_fd2 failed!\n");
    return -1;
} else {
	// 列印序列
    ptr2 = mmap(0, SIZE, PROT_WRITE, MAP_SHARED, shm_fd2, 0);
    printf("%s\n", (char*)ptr2);
    // 與共享記憶體temp斷開連線
    shm_unlink("temp");
}

執行結果

在這裡插入圖片描述在這裡插入圖片描述

實驗內容三

實驗要求

  • 設計一個程式,通過普通管道進行通訊,讓一個程式傳送一個字串訊息給 第二個程式,第二個程式收到此訊息後,變更字母的大小寫,然後再傳送給第一
    個程式。比如,第一個程式發訊息:“I am Here”,第二個程式收到後,將它改變 為:“i AM hERE”之後,再發給第一個程式。
    提示:
    1. 需要建立子程式,父子程式之間通過普通管道進行通訊。
    2. 需要建立兩個普通管道。

實驗步驟

管道實現程式間通訊原理:

  1. 父程式建立管道,得到兩個⽂件描述符指向管道的兩端
  2. 父程式fork()建立子程式,⼦程式也有兩個⽂件描述符指向同⼀管道。
  3. 父程式關閉fd[0],子程式關閉fd[1],即⽗程式關閉管道讀端,⼦程式關閉管道寫端(因為管道只支援單向通訊)。⽗程式可以往管道⾥寫,⼦程式可以從管道⾥讀,管道是⽤環形佇列實現的,資料從寫端流⼊從讀端流出,這樣就實現了程式間通訊。

原理圖

在這裡插入圖片描述

建立管道

int pipe_1[2], pipe_2[2];

if(pipe(pipe_1) == -1 || pipe(pipe_2) == -1) {
    printf("Create pipe failed!\n");
    return -1;
}

建立子程式

pid_t pid;
pid = fork();

if (pid < 0) {
    printf("Create child process error!\n");
}

父程式傳送待處理資訊

char W_msg_1[BUFFER_SIZE] = "I am Here";
char R_msg_1[BUFFER_SIZE];

// printf("(Parent process %d)Please input the message: ", getpid());
// scanf("%s", W_msg_1);

// 關閉讀通道
close(pipe_1[READ]);
printf("(Parent process %d)Send message: %s\n", getpid(), W_msg_1);
// 傳送待處理資訊
write(pipe_1[WRITE], W_msg_1, strlen(W_msg_1) + 1);
// 關閉寫通道
close(pipe_1[WRITE]);

子程式讀取待處理資訊

char W_msg_2[BUFFER_SIZE];
char R_msg_2[BUFFER_SIZE];

// 關閉寫通道
close(pipe_1[WRITE]);
// 讀取待處理資訊
read(pipe_1[READ], R_msg_2, BUFFER_SIZE);
printf("(Child process %d)Get message: %s\n", getpid(), R_msg_2);
// 關閉讀通道
close(pipe_1[READ]);

子程式處理資訊

// 大小寫轉換
for (int i = 0; i < strlen(R_msg_2); i++) {
    if (R_msg_2[i] >= 'a' && R_msg_2[i] <= 'z') {
        R_msg_2[i] -= 32;
    } else if (R_msg_2[i] >= 'A' && R_msg_2[i] <= 'Z') {
        R_msg_2[i] += 32;
    }
}

子程式傳送處理後資訊

strcpy(W_msg_2, R_msg_2);
printf("(Child process %d)Send message: %s\n", getpid(), W_msg_2);
// 關閉讀通道
close(pipe_2[READ]);
// 傳送處理後資訊
write(pipe_2[WRITE], W_msg_2, BUFFER_SIZE);
// 關閉寫通道
close(pipe_2[WRITE]);

父程式讀取處理後資訊並列印

// 關閉寫通道
close(pipe_2[WRITE]);
// 讀取資訊
read(pipe_2[READ], R_msg_1, BUFFER_SIZE);
// 列印
printf("(Parent process %d)Get message: %s\n", getpid(), R_msg_1);
// 關閉讀通道
close(pipe_2[READ]);

執行結果

命令列輸入

在這裡插入圖片描述在這裡插入圖片描述

  • 由於C語言特性,命令列不便於輸入帶有空格分隔的字串,改為將字串直接寫在程式碼內部。

在這裡插入圖片描述

原始碼

實驗內容一

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <wait.h>

int main() {
    int N;
    __pid_t pid;

    printf("Please input N: ");
    scanf("%d", &N);

    pid = fork();
    if (pid < 0) {
        printf("Create child process error!\n");
    } else if (pid == 0) {
        printf("(Child process %d)The sequence is: %d ", getpid(), N);
        while (N != 1) {
            if (N % 2 == 0) 
                N /= 2;
            else
                N = 3 * N + 1;
            printf("%d ", N);
        }
        printf("\n");
    } else {
        wait(NULL);
        printf("(Parent process %d)Parent process exit!\n", getpid());
    }

    return 0;
}

實驗內容二

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <wait.h>

#define SIZE 4096

int main() {
    int   N = 0;
    pid_t pid;

    printf("Please input N: ");
    scanf("%d", &N);

    pid = fork();
    if (pid < 0) {
        printf("Create child process error!\n");
    } else if (pid == 0) {
        int   shm_fd1;
        char* message;
        char* ptr1;

        printf("(Child process %d)Dealing!\n", getpid());
        shm_fd1 = shm_open("temp", O_CREAT|O_RDWR, 0777);
        if (shm_fd1 < 0) {
            printf("Open shm_fd1 failed!\n");
            return -1;
        } else {
            char cur_num[10] = {0};
            
            sprintf(cur_num, "%d", N);
            message = (char*)malloc(sizeof(char));
            message = realloc(message, strlen(message) + strlen(cur_num));
            sprintf(message, "%s%s", message, cur_num);

            while (N != 1) {
                if (N % 2 == 0) {
                    N /= 2;
                } else {
                    N = 3 * N + 1;
                }
                sprintf(cur_num, "%d", N);
                message = realloc(message, strlen(message) + strlen(cur_num));
                sprintf(message, "%s %s", message, cur_num);
            }
            
            printf("(Child process %d)Done!\n", getpid());
            
            ftruncate(shm_fd1, SIZE);
            ptr1 = mmap(0, SIZE, PROT_WRITE, MAP_SHARED, shm_fd1, 0);
            sprintf(ptr1, "%s", message);
            ptr1 += strlen(message);
        } 
    } else {
            int   shm_fd2;
            char* ptr2;

            wait(NULL);
            printf("(Parent process %d)The sequence is:\n", getpid());

            shm_fd2 = shm_open("temp", O_CREAT|O_RDWR, 0777);
            if (shm_fd2 < 0) {
                printf("Open shm_fd2 failed!\n");
                return -1;
            } else {
                ptr2 = mmap(0, SIZE, PROT_WRITE, MAP_SHARED, shm_fd2, 0);
                printf("%s\n", (char*)ptr2);
                shm_unlink("temp");
            }
        }
    return 0;
}

實驗內容三

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>

#define BUFFER_SIZE 20
#define READ  0
#define WRITE 1

int main() {
    int pipe_1[2], pipe_2[2];

    if(pipe(pipe_1) == -1 || pipe(pipe_2) == -1) {
        printf("Create pipe failed!\n");
        return -1;
    }

    pid_t pid;
    pid = fork();

    if (pid < 0) {
        printf("Create child process error!\n");
    } else if (pid == 0) {
        char W_msg_2[BUFFER_SIZE];
        char R_msg_2[BUFFER_SIZE];

        close(pipe_1[WRITE]);
        read(pipe_1[READ], R_msg_2, BUFFER_SIZE);
        printf("(Child process %d)Get message: %s\n", getpid(), R_msg_2);
        close(pipe_1[READ]);

        for (int i = 0; i < strlen(R_msg_2); i++) {
            if (R_msg_2[i] >= 'a' && R_msg_2[i] <= 'z') {
                R_msg_2[i] -= 32;
            } else if (R_msg_2[i] >= 'A' && R_msg_2[i] <= 'Z') {
                R_msg_2[i] += 32;
            }
        }

        strcpy(W_msg_2, R_msg_2);
        printf("(Child process %d)Send message: %s\n", getpid(), W_msg_2);
        close(pipe_2[READ]);
        write(pipe_2[WRITE], W_msg_2, BUFFER_SIZE);
        close(pipe_2[WRITE]);
    } else {
        char W_msg_1[BUFFER_SIZE];
        char R_msg_1[BUFFER_SIZE];

        printf("(Parent process %d)Please input the message: ", getpid());
        scanf("%s", W_msg_1);

        close(pipe_1[READ]);
        printf("(Parent process %d)Send message: %s\n", getpid(), W_msg_1);
        write(pipe_1[WRITE], W_msg_1, strlen(W_msg_1) + 1);
        close(pipe_1[WRITE]);

        close(pipe_2[WRITE]);
        read(pipe_2[READ], R_msg_1, BUFFER_SIZE);
        printf("(Parent process %d)Get message: %s\n", getpid(), R_msg_1);
        close(pipe_2[READ]);
    }
}

相關文章