Komunikasi Proses Antar - Sinyal

SEBUAH signaladalah pemberitahuan ke proses yang menunjukkan terjadinya suatu peristiwa. Sinyal juga disebutsoftware interrupt dan tidak dapat diprediksi untuk diketahui kemunculannya, maka itu disebut juga asynchronous event.

Sinyal dapat ditentukan dengan nomor atau nama, biasanya nama sinyal dimulai dengan SIG. Sinyal yang tersedia dapat diperiksa dengan perintah kill –l (l untuk daftar nama sinyal), yaitu sebagai berikut -

Setiap kali sinyal dibesarkan (baik secara terprogram atau sinyal yang dihasilkan sistem), tindakan default dilakukan. Bagaimana jika Anda tidak ingin melakukan tindakan default tetapi ingin melakukan tindakan Anda sendiri saat menerima sinyal? Apakah ini mungkin untuk semua sinyal? Ya, itu mungkin untuk menangani sinyal tetapi tidak untuk semua sinyal. Bagaimana jika Anda ingin mengabaikan sinyal, apakah ini mungkin? Ya, dimungkinkan untuk mengabaikan sinyal. Mengabaikan sinyal berarti tidak melakukan tindakan default maupun menangani sinyal. Dimungkinkan untuk mengabaikan atau menangani hampir semua sinyal. Sinyal yang tidak dapat diabaikan atau ditangani / ditangkap adalah SIGSTOP dan SIGKILL.

Singkatnya, tindakan yang dilakukan untuk sinyal adalah sebagai berikut -

  • Tindakan Default
  • Tangani sinyalnya
  • Abaikan sinyalnya

Seperti yang telah dibahas, sinyal dapat ditangani dengan mengubah eksekusi tindakan default. Penanganan sinyal dapat dilakukan dengan salah satu dari dua cara yaitu, melalui panggilan sistem, sinyal () dan sigaction ().

#include <signal.h>

typedef void (*sighandler_t) (int);
sighandler_t signal(int signum, sighandler_t handler);

Sinyal panggilan sistem () akan memanggil penangan terdaftar pada generasi sinyal seperti yang disebutkan dalam signum. Penangan dapat berupa salah satu dari SIG_IGN (Mengabaikan Sinyal), SIG_DFL (Pengaturan sinyal kembali ke mekanisme default) atau penangan sinyal yang ditentukan pengguna atau alamat fungsi.

Panggilan sistem ini saat berhasil mengembalikan alamat dari suatu fungsi yang menggunakan argumen integer dan tidak memiliki nilai kembali. Panggilan ini mengembalikan SIG_ERR jika terjadi kesalahan.

Meskipun dengan signal () masing-masing penangan sinyal yang didaftarkan oleh pengguna dapat dipanggil, penyetelan halus seperti menutupi sinyal yang harus diblokir, memodifikasi perilaku sinyal, dan fungsi lainnya tidak dimungkinkan. Ini dimungkinkan menggunakan panggilan sistem sigaction ().

#include <signal.h>

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)

Panggilan sistem ini digunakan untuk memeriksa atau mengubah tindakan sinyal. Jika tindakan tidak null, tindakan baru untuk sinyal signum diinstal dari tindakan. Jika oldact tidak null, tindakan sebelumnya disimpan di oldact.

Struktur sigaction berisi bidang berikut -

Field 1 - Handler disebutkan baik dalam sa_handler atau sa_sigaction.

void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);

Handler untuk sa_handler menentukan tindakan yang akan dilakukan berdasarkan signum dan dengan SIG_DFL menunjukkan tindakan default atau SIG_IGN untuk mengabaikan sinyal atau penunjuk ke fungsi penanganan sinyal.

Handler untuk sa_sigaction menentukan nomor sinyal sebagai argumen pertama, penunjuk ke struktur siginfo_t sebagai argumen kedua dan penunjuk ke konteks pengguna (periksa getcontext () atau setcontext () untuk detail lebih lanjut) sebagai argumen ketiga.

Struktur siginfo_t berisi informasi sinyal seperti nomor sinyal yang akan dikirim, nilai sinyal, id proses, id pengguna nyata dari proses pengiriman, dll.

Field 2 - Set sinyal yang akan diblokir.

int sa_mask;

Variabel ini menentukan topeng sinyal yang harus diblokir selama eksekusi penangan sinyal.

Field 3 - Bendera khusus.

int sa_flags;

Bidang ini menetapkan satu set bendera yang mengubah perilaku sinyal.

Field 4 - Kembalikan pawang.

void (*sa_restorer) (void);

Panggilan sistem ini mengembalikan 0 jika berhasil dan -1 jika gagal.

Mari kita pertimbangkan beberapa program contoh.

Pertama, mari kita mulai dengan program contoh, yang menghasilkan pengecualian. Dalam program ini, kami mencoba melakukan operasi divide by zero, yang membuat sistem menghasilkan pengecualian.

/* signal_fpe.c */
#include<stdio.h>

int main() {
   int result;
   int v1, v2;
   v1 = 121;
   v2 = 0;
   result = v1/v2;
   printf("Result of Divide by Zero is %d\n", result);
   return 0;
}

Langkah Kompilasi dan Eksekusi

Floating point exception (core dumped)

Jadi, ketika kami mencoba melakukan operasi aritmatika, sistem telah menghasilkan pengecualian floating point dengan core dump, yang merupakan aksi default dari sinyal.

Sekarang, mari kita ubah kode untuk menangani sinyal khusus ini menggunakan pemanggilan sistem signal ().

/* signal_fpe_handler.c */
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>

void handler_dividebyzero(int signum);

int main() {
   int result;
   int v1, v2;
   void (*sigHandlerReturn)(int);
   sigHandlerReturn = signal(SIGFPE, handler_dividebyzero);
   if (sigHandlerReturn == SIG_ERR) {
      perror("Signal Error: ");
      return 1;
   }
   v1 = 121;
   v2 = 0;
   result = v1/v2;
   printf("Result of Divide by Zero is %d\n", result);
   return 0;
}

void handler_dividebyzero(int signum) {
   if (signum == SIGFPE) {
      printf("Received SIGFPE, Divide by Zero Exception\n");
      exit (0);
   } 
   else
      printf("Received %d Signal\n", signum);
      return;
}

Langkah Kompilasi dan Eksekusi

Received SIGFPE, Divide by Zero Exception

Seperti yang telah dibahas, sinyal dihasilkan oleh sistem (setelah melakukan operasi tertentu seperti membagi dengan nol, dll.) Atau pengguna juga dapat menghasilkan sinyal secara terprogram. Jika Anda ingin menghasilkan sinyal secara terprogram, gunakan fungsi library raise ().

Sekarang, mari kita ambil program lain untuk mendemonstrasikan penanganan dan mengabaikan sinyal.

Asumsikan bahwa kita telah menaikkan sinyal menggunakan raise (), lalu apa yang terjadi? Setelah sinyal dinaikkan, eksekusi proses saat ini dihentikan. Lalu apa yang terjadi dengan proses yang dihentikan? Mungkin ada dua skenario - Pertama, lanjutkan eksekusi kapan pun diperlukan. Kedua, akhiri (dengan perintah kill) prosesnya.

Untuk melanjutkan eksekusi proses yang dihentikan, kirim SIGCONT ke proses tersebut. Anda juga dapat mengeluarkan perintah fg (foreground) atau bg (background) untuk melanjutkan eksekusi. Di sini, perintah hanya akan memulai kembali eksekusi dari proses terakhir. Jika lebih dari satu proses dihentikan, maka hanya proses terakhir yang dilanjutkan. Jika Anda ingin melanjutkan proses yang sebelumnya dihentikan, lanjutkan pekerjaan (menggunakan fg / bg) bersama dengan nomor pekerjaan.

Program berikut ini digunakan untuk menaikkan sinyal SIGSTOP menggunakan fungsi raise (). Signal SIGSTOP juga dapat dihasilkan dengan menekan tombol CTRL + Z (Control + Z) pengguna. Setelah mengeluarkan sinyal ini, program akan berhenti dijalankan. Kirim sinyal (SIGCONT) untuk melanjutkan eksekusi.

Dalam contoh berikut, kami melanjutkan proses yang dihentikan dengan perintah fg.

/* signal_raising.c */
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>

int main() {
   printf("Testing SIGSTOP\n");
   raise(SIGSTOP);
   return 0;
}

Langkah Kompilasi dan Eksekusi

Testing SIGSTOP
[1]+ Stopped ./a.out
./a.out

Sekarang, tingkatkan program sebelumnya untuk melanjutkan eksekusi dari proses yang dihentikan dengan menerbitkan SIGCONT dari terminal lain.

/* signal_stop_continue.c */
#include<stdio.h>
#include<signal.h>
#include <sys/types.h>
#include <unistd.h>

void handler_sigtstp(int signum);

int main() {
   pid_t pid;
   printf("Testing SIGSTOP\n");
   pid = getpid();
   printf("Open Another Terminal and issue following command\n");
   printf("kill -SIGCONT %d or kill -CONT %d or kill -18 %d\n", pid, pid, pid);
   raise(SIGSTOP);
   printf("Received signal SIGCONT\n");
   return 0;
}

Langkah Kompilasi dan Eksekusi

Testing SIGSTOP
Open Another Terminal and issue following command
kill -SIGCONT 30379 or kill -CONT 30379 or kill -18 30379
[1]+ Stopped ./a.out

Received signal SIGCONT
[1]+ Done ./a.out

Di terminal lain

kill -SIGCONT 30379

Sejauh ini, kita telah melihat program yang menangani sinyal yang dibangkitkan oleh sistem. Sekarang, mari kita lihat sinyal yang dihasilkan melalui program (menggunakan fungsi raise () atau melalui perintah kill). Program ini menghasilkan sinyal SIGTSTP (terminal stop), yang tindakan defaultnya adalah menghentikan eksekusi. Namun, karena kita menangani sinyal sekarang daripada tindakan default, itu akan datang ke penangan yang ditentukan. Dalam hal ini, kami hanya mencetak pesan dan keluar.

/* signal_raising_handling.c */
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>

void handler_sigtstp(int signum);

int main() {
   void (*sigHandlerReturn)(int);
   sigHandlerReturn = signal(SIGTSTP, handler_sigtstp);
   if (sigHandlerReturn == SIG_ERR) {
      perror("Signal Error: ");
      return 1;
   }
   printf("Testing SIGTSTP\n");
   raise(SIGTSTP);
   return 0;
}

void handler_sigtstp(int signum) {
   if (signum == SIGTSTP) {
      printf("Received SIGTSTP\n");
      exit(0);
   }
   else
      printf("Received %d Signal\n", signum);
      return;
}

Langkah Kompilasi dan Eksekusi

Testing SIGTSTP
Received SIGTSTP

Kami telah melihat contoh melakukan tindakan default atau menangani sinyal. Sekarang, saatnya mengabaikan sinyalnya. Di sini, dalam program contoh ini, kami mendaftarkan sinyal SIGTSTP untuk diabaikan melalui SIG_IGN dan kemudian kami menaikkan sinyal SIGTSTP (terminal stop). Ketika sinyal SIGTSTP sedang dibuat, itu akan diabaikan.

/* signal_raising_ignoring.c */
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>

void handler_sigtstp(int signum);

int main() {
   void (*sigHandlerReturn)(int);
   sigHandlerReturn = signal(SIGTSTP, SIG_IGN);
   if (sigHandlerReturn == SIG_ERR) {
      perror("Signal Error: ");
      return 1;
   }
   printf("Testing SIGTSTP\n");
   raise(SIGTSTP);
   printf("Signal SIGTSTP is ignored\n");
   return 0;
}

Langkah Kompilasi dan Eksekusi

Testing SIGTSTP
Signal SIGTSTP is ignored

Sejauh ini, kami telah mengamati bahwa kami memiliki satu penangan sinyal untuk menangani satu sinyal. Bisakah kita memiliki satu penangan untuk menangani banyak sinyal? Jawabannya iya. Mari kita pertimbangkan ini dengan sebuah program.

Program berikut melakukan hal berikut -

Step 1 - Mendaftarkan penangan (handleSignals) untuk menangkap atau menangani sinyal SIGINT (CTRL + C) atau SIGQUIT (CTRL + \)

Step 2 - Jika pengguna menghasilkan sinyal SIGQUIT (baik melalui perintah kill atau kontrol keyboard dengan CTRL + \), handler hanya mencetak pesan sebagai return.

Step 3 - Jika pengguna menghasilkan sinyal SIGINT (baik melalui perintah kill atau kontrol keyboard dengan CTRL + C) pertama kali, kemudian memodifikasi sinyal untuk melakukan tindakan default (dengan SIG_DFL) dari waktu berikutnya.

Step 4 - Jika pengguna menghasilkan sinyal SIGINT untuk kedua kalinya, ia melakukan tindakan default, yang merupakan penghentian program.

/* Filename: sigHandler.c */
#include<stdio.h>
#include<unistd.h>
#include<signal.h>

void handleSignals(int signum);

int main(void) {
   void (*sigHandlerInterrupt)(int);
   void (*sigHandlerQuit)(int);
   void (*sigHandlerReturn)(int);
   sigHandlerInterrupt = sigHandlerQuit = handleSignals;
   sigHandlerReturn = signal(SIGINT, sigHandlerInterrupt);
   if (sigHandlerReturn == SIG_ERR) {
      perror("signal error: ");
      return 1;
   }
   sigHandlerReturn = signal(SIGQUIT, sigHandlerQuit);
   
   if (sigHandlerReturn == SIG_ERR) {
      perror("signal error: ");
      return 1;
   }
   while (1) {
      printf("\nTo terminate this program, perform the following: \n");
      printf("1. Open another terminal\n");
      printf("2. Issue command: kill %d or issue CTRL+C 2 times (second time it terminates)\n", getpid());
      sleep(10);
   }
   return 0;
}

void handleSignals(int signum) {
   switch(signum) {
      case SIGINT:
      printf("\nYou pressed CTRL+C \n");
      printf("Now reverting SIGINT signal to default action\n");
      signal(SIGINT, SIG_DFL);
      break;
      case SIGQUIT:
      printf("\nYou pressed CTRL+\\ \n");
      break;
      default:
      printf("\nReceived signal number %d\n", signum);
      break;
   }
   return;
}

Langkah Kompilasi dan Eksekusi

To terminate this program, perform the following:
1. Open another terminal
2. Issue command: kill 74 or issue CTRL+C 2 times (second time it terminates)
^C
You pressed CTRL+C
Now reverting SIGINT signal to default action
          
To terminate this program, perform the following:
1. Open another terminal
2. Issue command: kill 74 or issue CTRL+C 2 times (second time it terminates)
^\You pressed CTRL+\
To terminate this program, perform the following:
1. Open another terminal
2. Issue command: kill 120
Terminated

Terminal lain

kill 71

Metode Kedua

To terminate this program, perform the following:
1. Open another terminal
2. Issue command: kill 71 or issue CTRL+C 2 times (second time it terminates)
^C
You pressed CTRL+C
Now reverting SIGINT signal to default action

To terminate this program, perform the following:
1. Open another terminal
2. Issue command: kill 71 or issue CTRL+C 2 times (second time it terminates)
^C

Kita tahu bahwa untuk menangani sinyal, kita memiliki dua panggilan sistem yaitu, signal () atau sigaction (). Sampai sekarang kita sudah melihat dengan system call signal (), sekarang saatnya untuk system call sigaction (). Mari kita memodifikasi program di atas untuk dilakukan menggunakan sigaction () sebagai berikut -

/* Filename: sigHandlerSigAction.c */
#include<stdio.h>
#include<unistd.h>
#include<signal.h>

void handleSignals(int signum);

int main(void) {
   void (*sigHandlerReturn)(int);
   struct sigaction mysigaction;
   mysigaction.sa_handler = handleSignals;
   sigemptyset(&mysigaction.sa_mask);
   mysigaction.sa_flags = 0;
   sigaction(SIGINT, &mysigaction, NULL);
   
   if (mysigaction.sa_handler == SIG_ERR) {
      perror("signal error: ");
      return 1;
   }
   mysigaction.sa_handler = handleSignals;
   sigemptyset(&mysigaction.sa_mask);
   mysigaction.sa_flags = 0;
   sigaction(SIGQUIT, &mysigaction, NULL);
   
   if (mysigaction.sa_handler == SIG_ERR) {
      perror("signal error: ");
      return 1;
   }
   while (-1) {
      printf("\nTo terminate this program, perform either of the following: \n");
      printf("1. Open another terminal and issue command: kill %d\n", getpid());
      printf("2. Issue CTRL+C 2 times (second time it terminates)\n");
      sleep(10);
   }
   return 0;
}

void handleSignals(int signum) {
   switch(signum) {
      case SIGINT:
      printf("\nYou have entered CTRL+C \n");
      printf("Now reverting SIGINT signal to perform default action\n");
      signal(SIGINT, SIG_DFL);
      break;
      case SIGQUIT:
      printf("\nYou have entered CTRL+\\ \n");
      break;
      default:
      printf("\nReceived signal number %d\n", signum);
      break;
   }
   return;
}

Mari kita lihat proses kompilasi dan eksekusi. Dalam proses eksekusi, mari kita lihat masalah CTRL + C dua kali, sisa pemeriksaan / cara (seperti di atas) Anda dapat mencoba program ini juga.

Langkah Kompilasi dan Eksekusi

To terminate this program, perform either of the following:
1. Open another terminal and issue command: kill 3199
2. Issue CTRL+C 2 times (second time it terminates)
^C
You have entered CTRL+C
Now reverting SIGINT signal to perform default action
To terminate this program, perform either of the following:
1. Open another terminal and issue command: kill 3199
2. Issue CTRL+C 2 times (second time it terminates)
^C