GNU/Linux >> Belajar Linux >  >> Linux

Implementasi beberapa pipa di C

Saya percaya masalahnya di sini adalah Anda menunggu dan menutup di dalam lingkaran yang sama yang menciptakan anak-anak. Pada iterasi pertama, anak akan dieksekusi (yang akan menghancurkan program anak, menimpanya dengan perintah pertama Anda) dan kemudian orang tua menutup semua deskriptor file dan menunggu anak selesai sebelum melanjutkan untuk membuat anak berikutnya . Pada saat itu, karena orang tua telah menutup semua pipanya, anak selanjutnya tidak akan memiliki apa pun untuk ditulis atau dibaca. Karena Anda tidak memeriksa keberhasilan panggilan dup2 Anda, hal ini tidak diperhatikan.

Jika Anda ingin mempertahankan struktur loop yang sama, Anda harus memastikan bahwa induk hanya menutup deskriptor file yang telah digunakan, tetapi meninggalkan deskriptor yang belum digunakan. Kemudian, setelah semua anak dibuat, orang tua Anda dapat menunggu.

EDIT :Saya mencampuradukkan orang tua/anak dalam jawaban saya, tetapi alasannya masih berlaku:proses yang berlanjut ke fork lagi menutup semua salinan pipanya, jadi proses apa pun setelah fork pertama tidak akan memiliki deskriptor file yang valid untuk dibaca ke/tulis dari.

kode semu, menggunakan larik pipa yang dibuat di muka:

/* parent creates all needed pipes at the start */
for( i = 0; i < num-pipes; i++ ){
    if( pipe(pipefds + i*2) < 0 ){
        perror and exit
    }
}

commandc = 0
while( command ){
    pid = fork()
    if( pid == 0 ){
        /* child gets input from the previous command,
            if it's not the first command */
        if( not first command ){
            if( dup2(pipefds[(commandc-1)*2], 0) < ){
                perror and exit
            }
        }
        /* child outputs to next command, if it's not
            the last command */
        if( not last command ){
            if( dup2(pipefds[commandc*2+1], 1) < 0 ){
                perror and exit
            }
        }
        close all pipe-fds
        execvp
        perror and exit
    } else if( pid < 0 ){
        perror and exit
    }
    cmd = cmd->next
    commandc++
}

/* parent closes all of its copies at the end */
for( i = 0; i < 2 * num-pipes; i++ ){
    close( pipefds[i] );
}

Dalam kode ini, proses induk asli membuat anak untuk setiap perintah dan karenanya bertahan dari seluruh cobaan. Anak-anak memeriksa untuk melihat apakah mereka harus mendapatkan masukan dari perintah sebelumnya dan apakah mereka harus mengirimkan keluaran mereka ke perintah berikutnya. Kemudian mereka menutup semua salinan deskriptor file pipa dan kemudian mengeksekusi. Orang tua tidak melakukan apa pun kecuali fork sampai dibuat anak untuk setiap perintah. Itu kemudian menutup semua salinan deskriptornya dan dapat terus menunggu.

Membuat semua pipa yang Anda butuhkan terlebih dahulu, dan kemudian mengelolanya dalam lingkaran, itu rumit dan memerlukan beberapa aritmatika larik. Namun, tujuannya terlihat seperti ini:

cmd0    cmd1   cmd2   cmd3   cmd4
   pipe0   pipe1  pipe2  pipe3
   [0,1]   [2,3]  [4,5]  [6,7]

Menyadari bahwa, pada waktu tertentu, Anda hanya memerlukan dua set pipa (pipa ke perintah sebelumnya dan pipa ke perintah berikutnya) akan menyederhanakan kode Anda dan membuatnya sedikit lebih kuat. Ephemient memberikan pseudo-code untuk ini di sini. Kodenya lebih bersih, karena induk dan anak tidak perlu melakukan perulangan yang tidak perlu untuk menutup deskriptor file yang tidak diperlukan dan karena induk dapat dengan mudah menutup salinan deskriptor file segera setelah fork.

Sebagai catatan tambahan:Anda harus selalu memeriksa nilai kembalian dari pipa, dup2, fork, dan exec.

EDIT 2 :salah ketik dalam kode semu. OP:num-pipes adalah jumlah pipa. Misalnya, "ls | grep foo | sort -r" akan memiliki 2 pipa.


Berikut kode fungsi yang benar

void runPipedCommands(cmdLine* command, char* userInput) {
    int numPipes = countPipes(userInput);


    int status;
    int i = 0;
    pid_t pid;

    int pipefds[2*numPipes];

    for(i = 0; i < (numPipes); i++){
        if(pipe(pipefds + i*2) < 0) {
            perror("couldn't pipe");
            exit(EXIT_FAILURE);
        }
    }


    int j = 0;
    while(command) {
        pid = fork();
        if(pid == 0) {

            //if not last command
            if(command->next){
                if(dup2(pipefds[j + 1], 1) < 0){
                    perror("dup2");
                    exit(EXIT_FAILURE);
                }
            }

            //if not first command&& j!= 2*numPipes
            if(j != 0 ){
                if(dup2(pipefds[j-2], 0) < 0){
                    perror(" dup2");///j-2 0 j+1 1
                    exit(EXIT_FAILURE);

                }
            }


            for(i = 0; i < 2*numPipes; i++){
                    close(pipefds[i]);
            }

            if( execvp(*command->arguments, command->arguments) < 0 ){
                    perror(*command->arguments);
                    exit(EXIT_FAILURE);
            }
        } else if(pid < 0){
            perror("error");
            exit(EXIT_FAILURE);
        }

        command = command->next;
        j+=2;
    }
    /**Parent closes the pipes and wait for children*/

    for(i = 0; i < 2 * numPipes; i++){
        close(pipefds[i]);
    }

    for(i = 0; i < numPipes + 1; i++)
        wait(&status);
}

Kode yang relevan (disingkat) adalah:

    if(fork() == 0){
            // do child stuff here
            ....
    }
    else{
            // do parent stuff here
            if(command != NULL)
                command = command->next;

            j += 2;
            for(i = 0; i < (numPipes ); i++){
               close(pipefds[i]);
            }
           while(waitpid(0,0,0) < 0);
    }

Yang berarti proses induk (pengontrol) melakukan ini:

  • garpu
  • tutup semua pipa
  • tunggu proses anak
  • putaran / anak berikutnya

Tapi seharusnya seperti ini:

  • garpu
  • garpu
  • garpu
  • tutup semua pipa (semuanya seharusnya sudah ditipu sekarang)
  • tunggu anak-anak

Linux
  1. Cara Menambahkan Banyak Host di phpMyAdmin

  2. Dd:Beberapa File Input?

  3. Jalankan beberapa skrip python secara bersamaan

  1. Pipa, dup2 dan exec()

  2. Potong halaman PDF menjadi beberapa halaman

  3. systemd - Memberikan banyak argumen pada layanan saya

  1. Cara Membuat Zip Banyak File di Linux

  2. Bagaimana Cara Mengirim Stdout Ke Beberapa Perintah?

  3. Memecah gambar dd menjadi beberapa file