GNU/Linux >> Belajar Linux >  >> Linux

Komunikasi antar-proses di Linux:Menggunakan pipa dan antrian pesan

Ini adalah artikel kedua dalam seri tentang komunikasi antarproses (IPC) di Linux. Artikel pertama berfokus pada IPC melalui penyimpanan bersama:file bersama dan segmen memori bersama. Artikel ini beralih ke pipa, yang merupakan saluran yang menghubungkan proses komunikasi. Sebuah saluran memiliki akhir tulis untuk menulis byte, dan akhir baca untuk membaca byte ini dalam urutan FIFO (masuk pertama, keluar pertama). Dalam penggunaan umum, satu proses menulis ke saluran, dan proses yang berbeda membaca dari saluran yang sama ini. Byte itu sendiri dapat mewakili apa saja:angka, catatan karyawan, film digital, dan sebagainya.

Pipa datang dalam dua rasa, bernama dan tidak bernama, dan dapat digunakan baik secara interaktif dari baris perintah atau dalam program; contoh yang akan datang. Artikel ini juga membahas antrean memori, yang sudah ketinggalan zaman—namun tidak semestinya demikian.

Contoh kode di artikel pertama mengakui ancaman kondisi balapan (baik berbasis file atau berbasis memori) di IPC yang menggunakan penyimpanan bersama. Pertanyaan secara alami muncul tentang konkurensi yang aman untuk IPC berbasis saluran, yang akan dibahas dalam artikel ini. Contoh kode untuk pipa dan antrian memori menggunakan API dengan cap persetujuan POSIX, dan tujuan inti dari standar POSIX adalah keamanan thread.

Pertimbangkan halaman manual untuk mq_open fungsi, yang termasuk dalam API antrian memori. Halaman ini menyertakan bagian tentang Atribut dengan tabel kecil ini:

Antarmuka Atribut Nilai
mq_open() Keamanan utas MT-Aman

Nilai MT-Safe (dengan MT untuk multi-utas) berarti mq_open fungsinya adalah thread-safe, yang pada gilirannya menyiratkan proses-aman:Sebuah proses dieksekusi dengan tepat dalam arti bahwa salah satu utasnya dieksekusi, dan jika kondisi balapan tidak dapat muncul di antara utas dalam sama proses, kondisi seperti itu tidak dapat muncul di antara utas dalam proses yang berbeda. MT-Aman atribut memastikan bahwa kondisi balapan tidak muncul dalam pemanggilan mq_open . Secara umum, IPC berbasis saluran aman secara bersamaan, meskipun catatan peringatan diberikan dalam contoh berikut.

Pipa tanpa nama

Mari kita mulai dengan contoh baris perintah yang menunjukkan cara kerja pipa tanpa nama. Pada semua sistem modern, bilah vertikal | mewakili pipa yang tidak disebutkan namanya di baris perintah. Asumsikan % adalah prompt baris perintah, dan pertimbangkan perintah ini:

% sleep 5 | echo "Hello, world!" ## writer to the left of |, reader to the right

tidur dan gema utilitas dijalankan sebagai proses yang terpisah, dan pipa yang tidak disebutkan namanya memungkinkan mereka untuk berkomunikasi. Namun, contohnya dibuat-buat karena tidak ada komunikasi yang terjadi. Salam Halo, dunia! muncul di layar; kemudian, setelah sekitar lima detik, prompt baris perintah kembali, menunjukkan bahwa keduanya sleep dan gema proses telah keluar. Apa yang terjadi?

Dalam sintaks bilah vertikal dari baris perintah, proses ke kiri (tidur ) adalah penulisnya, dan proses di sebelah kanan (echo ) adalah pembaca. Secara default, pembaca memblokir hingga ada byte untuk dibaca dari saluran, dan penulis—setelah menulis byte-nya—menyelesaikan dengan mengirimkan penanda akhir aliran. (Bahkan jika penulis berhenti sebelum waktunya, penanda akhir aliran dikirim ke pembaca.) Pipa tanpa nama tetap ada sampai penulis dan pembaca berhenti.

[Unduh panduan lengkap untuk komunikasi antar-proses di Linux]

Dalam contoh yang dibuat-buat, tidur proses tidak menulis byte apa pun ke saluran, tetapi berhenti setelah sekitar lima detik, yang mengirimkan penanda akhir aliran ke saluran. Sementara itu, gema proses segera menulis salam ke output standar (layar) karena proses ini tidak membaca byte apa pun dari saluran, sehingga tidak menunggu. Setelah tidur dan gema proses berakhir, pipa tanpa nama—tidak digunakan sama sekali untuk komunikasi—hilang dan prompt baris perintah kembali.

Berikut adalah contoh yang lebih berguna menggunakan dua pipa yang tidak disebutkan namanya. Misalkan file test.dat terlihat seperti ini:

this
is
the
way
the
world
ends

Perintahnya:

% cat test.dat | sort | uniq

menyalurkan output dari cat (menggabungkan) proses ke dalam sort proses untuk menghasilkan keluaran yang diurutkan, dan kemudian menyalurkan keluaran yang diurutkan ke dalam uniq proses untuk menghilangkan rekaman duplikat (dalam hal ini, dua kemunculan the kurangi menjadi satu):

ends
is
the
this
way
world

Adegan sekarang diatur untuk program dengan dua proses yang berkomunikasi melalui pipa yang tidak disebutkan namanya.

Contoh 1. Dua proses berkomunikasi melalui pipa tanpa nama.

#include <sys/wait.h> /* wait */
#include <stdio.h>
#include <stdlib.h>   /* exit functions */
#include <unistd.h>   /* read, write, pipe, _exit */
#include <string.h>

#define ReadEnd  0
#define WriteEnd 1

void report_and_exit(const char* msg) {
  perror(msg);
  exit(-1);    /** failure **/
}

int main() {
  int pipeFDs[2]; /* two file descriptors */
  char buf;       /* 1-byte buffer */
  const char* msg = "Nature's first green is gold\n"; /* bytes to write */

  if (pipe(pipeFDs) < 0) report_and_exit("pipeFD");
  pid_t cpid = fork();                                /* fork a child process */
  if (cpid < 0) report_and_exit("fork");              /* check for failure */

  if (0 == cpid) {    /*** child ***/                 /* child process */
    close(pipeFDs[WriteEnd]);                         /* child reads, doesn't write */

    while (read(pipeFDs[ReadEnd], &buf, 1) > 0)       /* read until end of byte stream */
      write(STDOUT_FILENO, &buf, sizeof(buf));        /* echo to the standard output */

    close(pipeFDs[ReadEnd]);                          /* close the ReadEnd: all done */
    _exit(0);                                         /* exit and notify parent at once  */
  }
  else {              /*** parent ***/
    close(pipeFDs[ReadEnd]);                          /* parent writes, doesn't read */

    write(pipeFDs[WriteEnd], msg, strlen(msg));       /* write the bytes to the pipe */
    close(pipeFDs[WriteEnd]);                         /* done writing: generate eof */

    wait(NULL);                                       /* wait for child to exit */
    exit(0);                                          /* exit normally */
  }
  return 0;
}

pipaUN program di atas menggunakan fungsi sistem fork untuk membuat sebuah proses. Meskipun program hanya memiliki satu file sumber, multi-pemrosesan terjadi selama eksekusi (berhasil). Berikut adalah rincian dalam tinjauan singkat tentang bagaimana fungsi perpustakaan garpu bekerja:

  • The garpu fungsi, dipanggil di induk proses, mengembalikan -1 kepada orang tua jika terjadi kegagalan. Di pipeUN contoh, pemanggilannya adalah:
    pid_t cpid = fork(); /* called in parent */

    Nilai yang dikembalikan disimpan, dalam contoh ini, dalam variabel cpid tipe integer pid_t . (Setiap proses memiliki ID proses sendiri-sendiri , bilangan bulat non-negatif yang mengidentifikasi proses.) Forking proses baru bisa gagal karena beberapa alasan, termasuk tabel proses lengkap , sebuah struktur yang dipertahankan sistem untuk melacak proses. Proses zombie, yang akan segera dijelaskan, dapat menyebabkan tabel proses terisi jika ini tidak dipanen.

  • Jika garpu panggilan berhasil, dengan demikian memunculkan (menciptakan) proses anak baru, mengembalikan satu nilai ke induk tetapi nilai yang berbeda untuk anak. Proses induk dan anak menjalankan sama kode yang mengikuti panggilan ke fork . (Anak mewarisi salinan semua variabel yang dideklarasikan sejauh ini di induknya.) Khususnya, panggilan yang berhasil ke fork kembali:
    • Nol untuk proses anak
    • ID proses anak ke orang tua
  • Sebuah jika/lain atau konstruksi yang setara biasanya digunakan setelah fork successful yang berhasil panggilan untuk memisahkan kode yang dimaksudkan untuk orang tua dari kode yang dimaksudkan untuk anak. Dalam contoh ini, konstruknya adalah:
    if (0 == cpid) {    /*** child ***/
    ...
    }
    else {              /*** parent ***/
    ...
    }

Jika forking anak berhasil, pipeUN program berlangsung sebagai berikut. Ada larik bilangan bulat:

int pipeFDs[2]; /* two file descriptors */

untuk menampung dua deskriptor file, satu untuk menulis ke pipa dan satu lagi untuk membaca dari pipa. (Elemen larik pipeFDs[0] adalah deskriptor file untuk akhir baca, dan elemen larik pipeFDs[1] adalah deskriptor file untuk akhir penulisan.) Panggilan berhasil ke pipa sistem fungsi, dibuat segera sebelum panggilan ke fork , mengisi larik dengan dua deskriptor file:

if (pipe(pipeFDs) < 0) report_and_exit("pipeFD");

Lebih banyak sumber daya Linux

  • Lembar contekan perintah Linux
  • Lembar contekan perintah Linux tingkat lanjut
  • Kursus online gratis:Ikhtisar Teknis RHEL
  • Lembar contekan jaringan Linux
  • Lembar contekan SELinux
  • Lembar contekan perintah umum Linux
  • Apa itu container Linux?
  • Artikel Linux terbaru kami

Orang tua dan anak sekarang memiliki salinan dari kedua deskriptor file, tetapi pemisahan masalah pattern berarti bahwa setiap proses membutuhkan tepat satu deskriptor. Dalam contoh ini, orang tua menulis dan anak membaca, meskipun perannya bisa dibalik. Pernyataan pertama pada anak jika -clause code, oleh karena itu, menutup ujung penulisan pipa:

close(pipeFDs[WriteEnd]); /* called in child code */

dan pernyataan pertama di induk else -kode klausa menutup akhir baca pipa:

close(pipeFDs[ReadEnd]);  /* called in parent code */

Orang tua kemudian menulis beberapa byte (kode ASCII) ke pipa yang tidak disebutkan namanya, dan anak membaca ini dan menggemakannya ke output standar.

Satu aspek lagi dari program ini membutuhkan klarifikasi:panggilan untuk menunggu fungsi dalam kode induk. Setelah muncul, proses anak sebagian besar tidak tergantung pada induknya, bahkan sebagai pipeUN . pendek program menggambarkan. Anak dapat mengeksekusi kode arbitrer yang mungkin tidak ada hubungannya dengan induknya. Namun, sistem tidak memberi tahu orang tua melalui sinyal—jika dan ketika anak berhenti.

Bagaimana jika orang tua berakhir sebelum anak? Dalam hal ini, kecuali tindakan pencegahan diambil, anak menjadi dan tetap menjadi zombie proses dengan entri dalam tabel proses. Tindakan pencegahan terdiri dari dua jenis yang luas. Salah satu tindakan pencegahan adalah meminta orang tua memberi tahu sistem bahwa orang tua tidak tertarik pada penghentian anak:

signal(SIGCHLD, SIG_IGN); /* in parent: ignore notification */

Pendekatan kedua adalah meminta induk menjalankan menunggu pada penghentian anak, sehingga memastikan bahwa orang tua hidup lebih lama dari anak. Pendekatan kedua ini digunakan dalam pipeUN program, di mana kode induk memiliki panggilan ini:

wait(NULL); /* called in parent */

Panggilan ini untuk menunggu artinya tunggu sampai terjadi pemutusan hubungan kerja anak , dan di pipeUN program, hanya ada satu proses anak. (NULL argumen dapat diganti dengan alamat variabel integer untuk menahan status keluar anak.) Ada waitpid yang lebih fleksibel fungsi untuk kontrol berbutir halus, misalnya, untuk menentukan proses anak tertentu di antara beberapa.

pipaUN program mengambil tindakan pencegahan lain. Ketika orang tua selesai menunggu, orang tua mengakhiri dengan panggilan ke keluar regular biasa fungsi. Sebaliknya, anak diakhiri dengan panggilan ke _exit varian, yang mempercepat pemberitahuan penghentian. Akibatnya, anak memberi tahu sistem untuk memberi tahu orang tua secepatnya bahwa anak telah dihentikan.

Jika dua proses menulis ke pipa tanpa nama yang sama, dapatkah byte disisipkan? Misalnya, jika proses P1 menulis:

foo bar

ke pipa dan proses P2 secara bersamaan menulis:

baz baz

ke pipa yang sama, tampaknya isi pipa mungkin sesuatu yang sewenang-wenang, seperti:

baz foo baz bar

Standar POSIX memastikan bahwa penulisan tidak disisipkan selama tidak ada penulisan yang melebihi PIPE_BUF byte. Pada sistem Linux, PIPE_BUF berukuran 4.096 byte. Preferensi saya dengan pipa adalah memiliki satu penulis dan satu pembaca, sehingga menghindari masalah.

Pipa bernama

Pipa yang tidak disebutkan namanya tidak memiliki file pendukung:sistem mempertahankan buffer dalam memori untuk mentransfer byte dari penulis ke pembaca. Setelah penulis dan pembaca berhenti, buffer direklamasi, sehingga pipa yang tidak disebutkan namanya hilang. Sebaliknya, pipa bernama memiliki file pendukung dan API yang berbeda.

Mari kita lihat contoh baris perintah lain untuk mendapatkan inti dari pipa bernama. Berikut langkah-langkahnya:

  • Buka dua terminal. Direktori kerja harus sama untuk keduanya.
  • Di salah satu terminal, masukkan dua perintah ini (promptnya lagi adalah % , dan komentar saya dimulai dengan ## ):
    % mkfifo tester  ## creates a backing file named tester
    % cat tester     ## type the pipe's contents to stdout

    Pada awalnya, tidak ada yang akan muncul di terminal karena belum ada yang ditulis ke pipa bernama.

  • Di terminal kedua, masukkan perintah:
    % cat > tester  ## redirect keyboard input to the pipe
    hello, world!   ## then hit Return key
    bye, bye        ## ditto
    <Control-C>     ## terminate session with a Control-C

    Apa pun yang diketik di terminal ini akan digaungkan di terminal lain. Sekali Ctrl+C dimasukkan, prompt baris perintah biasa kembali di kedua terminal:pipa telah ditutup.

  • Bersihkan dengan menghapus file yang mengimplementasikan pipa bernama:
    % unlink tester

Sebagai nama utilitas mkfifo menyiratkan, pipa bernama juga disebut FIFO karena byte pertama yang masuk adalah byte pertama yang keluar, dan seterusnya. Ada fungsi perpustakaan bernama mkfifo yang membuat pipa bernama dalam program dan digunakan dalam contoh berikutnya, yang terdiri dari dua proses:satu menulis ke pipa bernama dan yang lainnya membaca dari pipa ini.

Contoh 2. fifoWriter program

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>

#define MaxLoops         12000   /* outer loop */
#define ChunkSize           16   /* how many written at a time */
#define IntsPerChunk         4   /* four 4-byte ints per chunk */
#define MaxZs              250   /* max microseconds to sleep */

int main() {
  const char* pipeName = "./fifoChannel";
  mkfifo(pipeName, 0666);                      /* read/write for user/group/others */
  int fd = open(pipeName, O_CREAT | O_WRONLY); /* open as write-only */
  if (fd < 0) return -1;                       /* can't go on */

  int i;
  for (i = 0; i < MaxLoops; i++) {          /* write MaxWrites times */
    int j;
    for (j = 0; j < ChunkSize; j++) {       /* each time, write ChunkSize bytes */
      int k;
      int chunk[IntsPerChunk];
      for (k = 0; k < IntsPerChunk; k++)
        chunk[k] = rand();
      write(fd, chunk, sizeof(chunk));
    }
    usleep((rand() % MaxZs) + 1);           /* pause a bit for realism */
  }

  close(fd);           /* close pipe: generates an end-of-stream marker */
  unlink(pipeName);    /* unlink from the implementing file */
  printf("%i ints sent to the pipe.\n", MaxLoops * ChunkSize * IntsPerChunk);

  return 0;
}

fifoWriter program di atas dapat diringkas sebagai berikut:

  • Program membuat pipa bernama untuk menulis:
    mkfifo(pipeName, 0666); /* read/write perms for user/group/others */
    int fd = open(pipeName, O_CREAT | O_WRONLY);

    di mana pipeName adalah nama file pendukung yang diteruskan ke mkfifo sebagai argumen pertama. Pipa bernama kemudian dibuka dengan panggilan yang sekarang sudah dikenal ke buka fungsi, yang mengembalikan deskriptor file.

  • Untuk sentuhan realisme, fifoWriter tidak menulis semua data sekaligus, tetapi menulis sepotong, tidur sejumlah mikrodetik acak, dan seterusnya. Secara total, 768.000 nilai integer 4-byte ditulis ke pipa bernama.
  • Setelah menutup pipa bernama, fifoWriter juga memutuskan tautan file:
    close(fd);        /* close pipe: generates end-of-stream marker */
    unlink(pipeName); /* unlink from the implementing file */

    Sistem mengambil kembali file pendukung setelah setiap proses yang terhubung ke pipa telah melakukan operasi pemutusan tautan. Dalam contoh ini, hanya ada dua proses seperti itu:fifoWriter dan fifoReader , keduanya melakukan membatalkan tautan operasi.

Kedua program harus dijalankan di terminal yang berbeda dengan direktori kerja yang sama. Namun, fifoWriter harus dimulai sebelum fifoReader , karena yang pertama membuat pipa. fifoReader kemudian mengakses pipa bernama yang sudah dibuat.

Contoh 3. fifoReader program

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

unsigned is_prime(unsigned n) { /* not pretty, but efficient */
  if (n <= 3) return n > 1;
  if (0 == (n % 2) || 0 == (n % 3)) return 0;

  unsigned i;
  for (i = 5; (i * i) <= n; i += 6)
    if (0 == (n % i) || 0 == (n % (i + 2))) return 0;

  return 1; /* found a prime! */
}

int main() {
  const char* file = "./fifoChannel";
  int fd = open(file, O_RDONLY);
  if (fd < 0) return -1; /* no point in continuing */
  unsigned count = 0, total = 0, primes_count = 0;

  while (1) {
    int next;
    int i;

    ssize_t count = read(fd, &next, sizeof(int));
    if (0 == count) break;                  /* end of stream */
    else if (count == sizeof(int)) {        /* read a 4-byte int value */
      total++;
      if (is_prime(next)) primes_count++;
    }
  }

  close(fd);       /* close pipe from read end */
  unlink(file);    /* unlink from the underlying file */
  printf("Received ints: %u, primes: %u\n", total, primes_count);

  return 0;
}

fifoReader program di atas dapat diringkas sebagai berikut:

  • Karena fifoWriter membuat pipa bernama, fifoReader hanya membutuhkan panggilan standar buka untuk mengakses pipa melalui file pendukung:
    const char* file = "./fifoChannel";
    int fd = open(file, O_RDONLY);

    File terbuka sebagai hanya-baca.

  • Program kemudian masuk ke loop yang berpotensi tak terbatas, mencoba membaca potongan 4-byte pada setiap iterasi. baca panggilan:
    ssize_t count = read(fd, &next, sizeof(int));

    mengembalikan 0 untuk menunjukkan akhir aliran, dalam hal ini fifoReader keluar dari loop, menutup pipa bernama, dan memutuskan tautan file pendukung sebelum diakhiri.

  • Setelah membaca bilangan bulat 4-byte, fifoReader memeriksa apakah bilangan tersebut prima. Ini mewakili logika bisnis yang mungkin dilakukan oleh pembaca tingkat produksi pada byte yang diterima. Pada uji sampel, ada 37.682 bilangan prima di antara 768.000 bilangan bulat yang diterima.

Pada sampel yang dijalankan berulang kali, fifoReader berhasil membaca semua byte yang fifoWriter menulis. Ini tidak mengejutkan. Kedua proses dijalankan pada host yang sama, menghilangkan masalah jaringan dari persamaan. Pipa bernama adalah mekanisme IPC yang sangat andal dan efisien dan, oleh karena itu, digunakan secara luas.

Berikut adalah output dari kedua program, masing-masing diluncurkan dari terminal terpisah tetapi dengan direktori kerja yang sama:

% ./fifoWriter
768000 ints sent to the pipe.
###
% ./fifoReader
Received ints: 768000, primes: 37682

Antrian pesan

Pipa memiliki perilaku FIFO yang ketat:byte pertama yang ditulis adalah byte pertama yang dibaca, byte kedua yang ditulis adalah byte kedua yang dibaca, dan seterusnya. Antrian pesan dapat berperilaku dengan cara yang sama tetapi cukup fleksibel sehingga potongan byte dapat diambil dari urutan FIFO.

Seperti namanya, antrian pesan adalah urutan pesan, yang masing-masing memiliki dua bagian:

  • Payload, yang merupakan larik byte (char dalam C)
  • Jenis, diberikan sebagai nilai bilangan bulat positif; jenis mengkategorikan pesan untuk pengambilan yang fleksibel

Pertimbangkan penggambaran antrian pesan berikut, dengan setiap pesan diberi label dengan tipe integer:

          +-+    +-+    +-+    +-+
sender--->|3|--->|2|--->|2|--->|1|--->receiver
          +-+    +-+    +-+    +-+

Dari empat pesan yang ditampilkan, yang berlabel 1 ada di depan, yaitu yang paling dekat dengan penerima. Berikutnya datang dua pesan dengan label 2, dan terakhir, pesan berlabel 3 di belakang. Jika perilaku FIFO yang ketat diterapkan, maka pesan akan diterima dalam urutan 1-2-2-3. Namun, antrian pesan memungkinkan pesanan pengambilan lainnya. Misalnya, pesan dapat diambil oleh penerima dalam urutan 3-2-1-2.

mqueue contoh terdiri dari dua program, pengirim yang menulis ke antrean pesan dan penerima yang membaca dari antrian ini. Kedua program menyertakan file header queue.h ditunjukkan di bawah ini:

Contoh 4. File header queue.h

#define ProjectId 123
#define PathName  "queue.h" /* any existing, accessible file would do */
#define MsgLen    4
#define MsgCount  6

typedef struct {
  long type;                 /* must be of type long */
  char payload[MsgLen + 1];  /* bytes in the message */
} queuedMessage;

File header mendefinisikan tipe struktur bernama queuedMessage , dengan payload (byte array) dan ketik bidang (bilangan bulat). File ini juga mendefinisikan konstanta simbolis (#define pernyataan), dua yang pertama digunakan untuk menghasilkan kunci yang, pada gilirannya, digunakan untuk mendapatkan ID antrian pesan. ProjectId dapat berupa nilai bilangan bulat positif apa pun, dan PathName harus berupa file yang ada dan dapat diakses—dalam hal ini, file queue.h . Pernyataan penyiapan di kedua pengirim dan penerima programnya adalah:

key_t key = ftok(PathName, ProjectId);   /* generate key */
int qid = msgget(key, 0666 | IPC_CREAT); /* use key to get queue id */

ID qid pada dasarnya adalah kebalikan dari deskriptor file untuk antrian pesan.

Contoh 5. Pesan pengirim program

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <string.h>
#include "queue.h"

void report_and_exit(const char* msg) {
  perror(msg);
  exit(-1); /* EXIT_FAILURE */
}

int main() {
  key_t key = ftok(PathName, ProjectId);
  if (key < 0) report_and_exit("couldn't get key...");

  int qid = msgget(key, 0666 | IPC_CREAT);
  if (qid < 0) report_and_exit("couldn't get queue id...");

  char* payloads[] = {"msg1", "msg2", "msg3", "msg4", "msg5", "msg6"};
  int types[] = {1, 1, 2, 2, 3, 3}; /* each must be > 0 */
  int i;
  for (i = 0; i < MsgCount; i++) {
    /* build the message */
    queuedMessage msg;
    msg.type = types[i];
    strcpy(msg.payload, payloads[i]);

    /* send the message */
    msgsnd(qid, &msg, sizeof(msg), IPC_NOWAIT); /* don't block */
    printf("%s sent as type %i\n", msg.payload, (int) msg.type);
  }
  return 0;
}

pengirim program di atas mengirimkan enam pesan, masing-masing dua dari tipe tertentu:pesan pertama adalah tipe 1, dua berikutnya dari tipe 2, dan dua terakhir dari tipe 3. Pernyataan pengiriman:

msgsnd(qid, &msg, sizeof(msg), IPC_NOWAIT);

dikonfigurasi untuk tidak memblokir (bendera IPC_NOWAIT ) karena pesannya sangat kecil. Satu-satunya bahaya adalah bahwa antrian penuh, tidak mungkin dalam contoh ini, akan mengakibatkan kegagalan pengiriman. Penerima program di bawah ini juga menerima pesan menggunakan IPC_NOWAIT bendera.

Contoh 6. Pesan penerima program

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include "queue.h"

void report_and_exit(const char* msg) {
  perror(msg);
  exit(-1); /* EXIT_FAILURE */
}

int main() {
  key_t key= ftok(PathName, ProjectId); /* key to identify the queue */
  if (key < 0) report_and_exit("key not gotten...");

  int qid = msgget(key, 0666 | IPC_CREAT); /* access if created already */
  if (qid < 0) report_and_exit("no access to queue...");

  int types[] = {3, 1, 2, 1, 3, 2}; /* different than in sender */
  int i;
  for (i = 0; i < MsgCount; i++) {
    queuedMessage msg; /* defined in queue.h */
    if (msgrcv(qid, &msg, sizeof(msg), types[i], MSG_NOERROR | IPC_NOWAIT) < 0)
      puts("msgrcv trouble...");
    printf("%s received as type %i\n", msg.payload, (int) msg.type);
  }

  /** remove the queue **/
  if (msgctl(qid, IPC_RMID, NULL) < 0)  /* NULL = 'no flags' */
    report_and_exit("trouble removing queue...");

  return 0;
}

Penerima program tidak membuat antrian pesan, meskipun API menyarankan seperti itu. Di penerima , panggilan:

int qid = msgget(key, 0666 | IPC_CREAT);

menyesatkan karena IPC_CREAT bendera, tetapi bendera ini benar-benar berarti buat jika diperlukan, jika tidak akses . pengirim panggilan program msgsnd untuk mengirim pesan, sedangkan penerima panggilan msgrcv untuk mengambil mereka. Dalam contoh ini, pengirim mengirim pesan dalam urutan 1-1-2-2-3-3, tetapi penerima kemudian mengambilnya dalam urutan 3-1-2-1-3-2, menunjukkan bahwa antrian pesan tidak terikat dengan perilaku FIFO yang ketat:

% ./sender
msg1 sent as type 1
msg2 sent as type 1
msg3 sent as type 2
msg4 sent as type 2
msg5 sent as type 3
msg6 sent as type 3

% ./receiver
msg5 received as type 3
msg1 received as type 1
msg3 received as type 2
msg2 received as type 1
msg6 received as type 3
msg4 received as type 2

Output di atas menunjukkan bahwa pengirim dan penerima dapat diluncurkan dari terminal yang sama. Keluaran juga menunjukkan bahwa antrian pesan tetap ada bahkan setelah pengirim proses membuat antrian, menulis padanya, dan keluar. Antrian hilang hanya setelah penerima proses secara eksplisit menghapusnya dengan panggilan ke msgctl :

if (msgctl(qid, IPC_RMID, NULL) < 0) /* remove queue */

Menutup

Pipa dan API antrian pesan pada dasarnya searah :satu proses menulis dan yang lain membaca. Ada implementasi pipa bernama dua arah, tetapi dua sen saya adalah bahwa mekanisme IPC ini adalah yang terbaik saat paling sederhana. Seperti disebutkan sebelumnya, antrian pesan telah jatuh popularitasnya—tetapi tanpa alasan yang baik; antrian ini adalah alat lain di kotak peralatan IPC. Bagian 3 melengkapi tur singkat kotak peralatan IPC ini dengan contoh kode IPC melalui soket dan sinyal.


Linux
  1. Komunikasi antar-proses di Linux:Soket dan sinyal

  2. Cara Memulai Perintah Linux di Latar Belakang dan Melepaskan Proses di Terminal

  3. Cara mengecek OS dan versi menggunakan perintah Linux

  1. Memperkenalkan panduan untuk komunikasi antar-proses di Linux

  2. Bagaimana cara mematikan proses di Linux menggunakan perintah?

  3. Pipes dan Redirection di Linux - Dijelaskan!

  1. Pengantar pipa dan pipa bernama di Linux

  2. Cara Menambah dan Menghapus Rute Statis di Linux menggunakan Perintah IP

  3. Pelajari Perintah Pgrep dan Pkill dengan Contoh di Linux