簡體   English   中英

Shell中的輸出重定向如何在Linux中由C中的fork()生成的子進程工作?

[英]How the Output Redirection in shell works for the child process produced by fork() in C in Linux?

我目前正在研究操作系統和並發性,有關進程調度程序的一種實踐是使用C語言來計算多個進程如何在Linux中以“毫秒”粒度“並行”工作。 這是我的代碼:

/* This file's name is Task05_3.c */
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <string.h>

int kill(pid_t pid, int sig);
unsigned usleep(unsigned seconds);

#define NUMBER_OF_PROCESSES 7
#define MAX_EXPERIMENT_DURATION 4

long int getDifferenceInMilliSeconds(struct timeval start, struct timeval end)
{
    int seconds = end.tv_sec - start.tv_sec;
    int useconds = end.tv_usec - start.tv_usec;
    int mtime = (seconds * 1000 + useconds / 1000);
    return mtime;
}

int main(int argc, char const *argv[])
{
    struct timeval startTime, currentTime;
    int diff;

    int log[MAX_EXPERIMENT_DURATION + 2] = {-1};
    /* initialization */
    for (int k = 0; k < MAX_EXPERIMENT_DURATION + 2; ++k)
        log[k] = -1;

    gettimeofday(&startTime, NULL);

    pid_t pid_for_diss = 0;

    for (int i = 0; i < NUMBER_OF_PROCESSES; ++i)
    {
        pid_for_diss = fork();
        if (pid_for_diss < 0) {
            printf("fork error, errno(%d): %s\n", errno, strerror(errno));
        } else if (pid_for_diss == 0) {
            /* This loop is for logging when the child process is running */
            while (1) {
                gettimeofday(&currentTime, NULL);
                diff = getDifferenceInMilliSeconds(startTime, currentTime);
                if (diff > MAX_EXPERIMENT_DURATION)
                {
                    break;
                }
                log[diff] = i;
            }
            // for (int k = 0; k < MAX_EXPERIMENT_DURATION + 2; ++k)
            // {
            //     if (log[k] != -1)
            //     {
            //         printf("%d, %d\n", log[k], k);
            //     }
            // }
            // exit(0);
            break;
        }
    }

    /* This loop is for print the logged results out */
    if (pid_for_diss == 0)
    {
        for (int k = 0; k < MAX_EXPERIMENT_DURATION + 2; ++k)
        {
            if (log[k] != -1)
            {
                printf("%d, %d\n", log[k], k);
            }
        }
        kill(getpid(), SIGKILL);
    }

    int status;
    while (wait(&status) != -1);// -1 means wait() failed
    printf("Bye from the parent!\n");
}

基本上,我的想法是為父進程設置一個for循環,以使用fork()產生7個子進程,並將它們設置為while循環,以迫使它們在一段時間內競爭CPU的使用。 並且每次安排子進程運行時,我大約將當前時間與父進程的開始時間之間的差記錄到屬於正在運行的子進程的數組中。 然后,在所有7個進程都打破了while循環之后,我為每個子進程設置了另一個for循環以打印出其記錄的結果。

但是,當我嘗試將輸出重定向到Linux機器中的.csv文件時,發生了一些奇怪的事情:首先,我將循環設置為在主for循環之外進行打印(如您在代碼中所見),然后運行./Task05_3直接放在bash中,結果如下:

psyhq@bann:osc$ gcc -std=c99 Task05_3.c -o Task05_3
psyhq@bann:osc$ ./Task05_3
5, 0
4, 0
6, 0
4, 1
1, 0
4, 2
4, 3
4, 4
0, 0
1, 1
6, 1
1, 2
1, 3
1, 4
5, 1
5, 2
5, 3
5, 4
6, 2
6, 3
2, 0
6, 4
2, 1
2, 2
2, 3
2, 4
0, 1
3, 0
0, 2
0, 3
0, 4
3, 1
3, 2
3, 3
3, 4
Bye from the parent!
psyhq@bann:osc$

您可以在此處看到所有結果(來自父過程和子過程)都已在終端中打印出來,並且子過程的結果是隨機順序的(我認為這可能是由於多個過程寫入了標准輸出)與此同時)。 但是,如果嘗試通過./Task05_3 > 5output_c.csv運行它,我會發現我的目標.csv文件僅包含來自父進程的結果,它看起來像: Result_in_csv01

所以我的第一個問題是.csv文件如何僅包含父進程的提示? 是因為我在bash中鍵入的指令僅重定向了父進程的輸出,而與子進程的輸出流無關?

而且,當我嘗試將for循環(用於打印)放入主for循環(請參閱上面我的代碼中注釋的for循環)並通過./Task05_3 > 5output_c.csv運行代碼時,發生了更令人困惑的事情, .csv文件現在看起來像: Result_in_csv02

現在它包含所有結果! 子進程結果的順序不再是隨機的! (顯然,其他子進程一直等待,直到正在運行的子進程將其所有結果打印出來)。 因此,我的第二個問題是,僅更改了for循環的位置后怎么辦?

PS。 我運行代碼的Linux機器位於:

psyhq@bann:osc$ cat /proc/version
Linux version 3.10.0-693.2.2.el7.x86_64 (builder@kbuilder.dev.centos.org) (gcc version 4.8.5 20150623 (Red Hat 4.8.5-16) (GCC) ) #1 SMP Tue Sep 12 22:26:13 UTC 2017

GCC版本是:

psyhq@bann:osc$ gcc --version
gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-16)
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

默認情況下,通過stdio函數的輸出會被緩沖。 這意味着它不會立即寫入,而是會累積在某些內部結構中(在FILE內部),直到...發生某些事情為止。 有三種可能性:

  • FILE是無緩沖的。 然后立即寫入輸出。
  • 行緩沖。 當緩沖區已滿或看到'\\n' (換行符)時,將寫入輸出。
  • 塊緩沖。 緩沖區已滿時寫入輸出。

您始終可以使用fflush手動強制執行寫操作。

默認情況下,使用fopen打開的文件是塊緩沖的。 stderr從無緩沖開始。 如果stdout指向終端,則為行緩沖,否則為塊緩沖。

您的子進程將打印全行( printf("%d, %d\\n", log[k], k); )。 這意味着只要stdout進入終端,一切都會立即出現(因為它是行緩沖的)。

但是,當您將輸出重定向到文件時, stdout變為塊緩沖。 緩沖區可能很大,因此所有輸出都累積在緩沖區中(永遠不會變滿)。 通常,在關閉FILE句柄(使用fclose )時,也會刷新(即寫入和清空)緩沖區,並且通常在程序結束時(通過從main return或通過調用exit )自動關閉所有打開的文件。

但是,在這種情況下,您可以通過發送(嚴重,無法捕獲)信號來終止該過程。 這意味着您的文件永遠不會關閉,緩沖區也永遠不會寫入,它們的內容會丟失。 這就是為什么您看不到任何輸出的原因。


在第二個版本中,您調用exit而不是向自己發送信號。 這將執行正常的清理工作,包括調用atexit處理程序,關閉所有打開的文件並刷新其緩沖區。


順便說一句,您可以編寫一下kill(getpid(), X)來代替kill(getpid(), X) raise(X) 它更短,更輕便( raise是標准C)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM