Signal Programming
การส่งสัญญาณ (signal) ถือว่าเป็นการส่งข้อมูลขั้นพื้นฐานระหว่างโปรเซสภายใต้ระบบปฏิบัติการลีนุกซ์ โดยโปรเซสหนึ่งจะส่งสัญญาณไปยังอีกโปรเซสหนึ่ง เมื่ออีกโปรเซสที่ได้รับสัญญาณที่ถูกแทรกเข้ามา (interrupt) มันก็จะดำเนินการคำสั่งที่ถูกตั้งเอาไว้ทันที ซึ่งเมื่อเริ่มแรกของการนำสัญญาณมาใช้นั้น จะเป็นเพียงรูปแบบสัญญาณที่ไม่ได้แยกแยะชนิดหรือประเภทของสัญญาณอะไรมากมายแต่ในปัจจุบันการพัฒนาของระบบปฏิบัติการลีนุกซ์มีความซับซ้อนมากขึ้น รวมทั้งการสื่อสารระหว่างโปรเซสก็ต้องการเงื่อนไขที่หลากหลายมากขึ้นเช่นกัน จึงทำให้มีการกำหนดประเภทของสัญญาณ (signal type) ขึ้นมาโดยมีชนิดเป็นเลขจำนวนเต็ม (integer value) ตัวอย่างเช่น เลข 15 (SIGTERM
) แทนสัญญาณสิ้นสุดการทำงาน (Terminate) ซึ่งหมายเลขสัญญาณต่างๆจะถูกกำหนดอยู่ในฟังก์ชัน signal.h
1) SIGHUP
2) SIGINT
3) SIGQUIT
4) SIGILL
5) SIGTRAP
6) SIGABRT
7) SIGBUS
8) SIGFPE
9) SIGKILL
10) SIGUSR1
11) SIGSEGV
12) SIGUSR2
13) SIGPIPE
14) SIGALRM
15) SIGTERM
16) SIGSTKFLT
17) SIGCHLD
18) SIGCONT
19) SIGSTOP
20) SIGTSTP
21) SIGTTIN
22) SIGTTOU
23) SIGCONT
24) SIGXCPU
25) SIGXFSZ
26) SIGVTALRM
27) SIGPROF
28) SIGWINCH
29) SIGIO
30) SIGPWR
31) SIGSYS
34) SIGRTMIN
35) SIGRTMIN+1
36) SIGRTMIN+2
37) SIGRTMIN+3
38) SIGRTMIN+4
39) SIGRTMIN+5
40) SIGRTMIN+6
41) SIGRTMIN+7
42) SIGRTMIN+8
43) SIGRTMIN+9
44) SIGRTMIN+10
45) SIGRTMIN+11
46) SIGRTMIN+12
47) SIGRTMIN+13
48) SIGRTMIN+14
49) SIGRTMIN+15
50) SIGRTMAX-14
51) SIGRTMAX-13
52) SIGRTMAX-12
53) SIGRTMAX-11
54) SIGRTMAX-10
55) SIGRTMAX-9
56) SIGRTMAX-8
57) SIGRTMAX-7
58) SIGRTMAX-6
59) SIGRTMAX-5
60) SIGRTMAX-4
61) SIGRTMAX-3
62) SIGRTMAX-2
63) SIGRTMAX-1
64) SIGRTMAX
ตัวอย่างการส่งสัญญาณผ่านคีย์บอร์ด
Ctrl-C
ส่งสัญญาณ INT (SIGINT) ไปยังโปรเซสที่ทำงานอยู่ยุติการทำงานทันที
Ctrl-Z
ส่งสัญญาณ TSTP (SIGTSTP) ไปยังโปรเซสที่ทำงานอยู่ให้หยุดชั่วคราว
Ctrl-\
ส่งสัญญาณ ABRT (SIGABRT) ไปยังโปรเซสที่ทำงานอยู่ให้ยุติการทำงานของโปรเซสนั้นทันที เหมือน Ctrl-C แต่เป็นคำสั่งที่ดีกว่าคือสามารถแก้ไขได้
นอกจากนั้นจะมีการเรียกใช้ฟังก์ชัน kill() เพื่อทำการส่งสัญญาณออกไปยังโปรเซสปลายทาง โดยที่โปรเซสรับปลายทางจะมีการกำหนดไว้ว่าถ้าได้รับสัญญาณหมายเลขนี้ ก็ให้ดำเนินการเรียกฟังก์ชันที่กำหนดไว้ต่อไป ด้วยการเรียกใช้ฟังก์ชัน signal() อย่างไรก็ตามโปรเซสต่างๆที่อยู่ภายใต้ผู้ใช้คนเดียวกัน จะสามารถส่งสัญญาณถึงกันได้ แต่ไม่สามารถส่งสัญญาณไปยังโปรเซสอื่นๆของผู้ใช้คนอื่นได้ ยกเว้นโปรเซสของ superuser เท่านั้น
แสดงตัวอย่างการกำหนดฟังก์ชันที่จะให้ดำเนินการเมื่อตรวจจับสัญญาณที่กำหนดไว้ ซึ่งในตัวอย่างนี้จะตรวจสอบสัญญาณหมายเลข 2 (SIGINT
)

// signal1.c
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
void signalHandler(int signo) {
if (signo == SIGINT)
printf(" received SIGINT\n");
}
int main(void) {
printf("My pid is %d.\n", getpid());
if (signal(SIGINT, signalHandler) == SIG_ERR)
printf("\n Can't catch SIGINT\n");
// A long long wait so that we can easily issue a signal to this process
while (1)
sleep(1);
return 0;
}
$ gcc -o signal1 signal1.c -Wall
$ ./signal1
My pid is 21231
(ให้กด Ctrl+C เพื่อส่งสัญญาณให้กับโปรแกรม)
^C received SIGINT
^C received SIGINT
^C received SIGINT
^C received SIGINT
^C received SIGINT
^C received SIGINT
(ให้กด Ctrl+Z เพื่อส่งสัญญาณให้กับโปรแกรม)
^Z
[1]+ Stopped ./signal1
แสดงตัวอย่างโปรเซสลูกจะวนรอรับ signal ที่จะถูกส่งมาจากผู้ใช้เช่น Ctrl+C
เป็นต้น โดยโปรเซสแม่จะรอโปรเซสลูกจนกว่าจะทำงานสิ้นสุด
// signal2.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
void HandlerChild() {
printf("Sigint received by child \n");
}
void HandlerParent() {
printf("Sigint received by Parent \n");
signal(SIGINT, HandlerParent);
}
int n, statusp;
int main(void) {
// signal(SIGINT,HandlerParent);
n = fork();
if (n == 0) {
printf("Child ID %d\n", getpid());
signal(SIGINT, HandlerChild);
for (;;) {
};
} else {
printf("Parent ID %d \n", getpid());
// signal(SIGINT,HandlerParent);
wait(&statusp);
// kill(n,SIGKILL);
exit(0);
}
return 0;
}
$ gcc -o signal2 signal2.c -Wall
$ ./signal2
Parent ID 4564
Child ID 4565
^CSigint received by child
แสดงตัวอย่างการใช้ฟังก์ชัน alarm(5)
เพื่อสร้างสัญญาณขึ้นทุกๆ 5 วินาที
// signal3.c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
int r, count = 0, n;
void HL_AlarmSig(int al) {
printf(" I am active every 5 sec ");
signal(SIGALRM, HL_AlarmSig);
r = alarm(5);
count++;
printf(" .... Now the time is %d sec \n", count * 5);
}
void HL_AllSig(int s) {
printf(" Oh ! I just received signal number %d \n", s);
signal(s, HL_AllSig);
}
int main(void) {
for (n = 1; n <= 31; n++) {
signal(n, HL_AllSig);
}
signal(SIGALRM, HL_AlarmSig);
r = alarm(5);
while (count < 12) {
};
printf(" Program Terminate !! ");
return 0;
}
$ gcc -o signal3 signal3.c -Wall
$ ./signal3
^C Oh ! I just received signal number 2
^C Oh ! I just received signal number 2
^C Oh ! I just received signal number 2
I am active every 5 sec .... Now the time is 5 sec
^Z Oh ! I just received signal number 20
^\ Oh ! I just received signal number 3
I am active every 5 sec .... Now the time is 10 sec
I am active every 5 sec .... Now the time is 15 sec
^C Oh ! I just received signal number 2
^C Oh ! I just received signal number 2
^C Oh ! I just received signal number 2
^Z Oh ! I just received signal number 20
^Z Oh ! I just received signal number 20
I am active every 5 sec .... Now the time is 20 sec
I am active every 5 sec .... Now the time is 25 sec
I am active every 5 sec .... Now the time is 30 sec
I am active every 5 sec .... Now the time is 35 sec
I am active every 5 sec .... Now the time is 40 sec
I am active every 5 sec .... Now the time is 45 sec
I am active every 5 sec .... Now the time is 50 sec
I am active every 5 sec .... Now the time is 55 sec
I am active every 5 sec .... Now the time is 60 sec
จากผลการทำงานของโปรแกรมข้างต้นเมื่อมีการทำงานของคำสั่ง alarm(5)
ที่มีการสร้างสัญญาณขึ้นทุกๆ 5 วินาที แล้วทำให้มีการเรียกฟังก์ชัน HL_AlarmSig()
เพื่อแสดงข้อความนับเวลา แล้วเพิ่มค่าตัวแปร count
ขึ้นไปเรื่อยๆ จนกระทั่งเมื่อค่าในตัวแปร count
เพิ่มขึ้นจนเท่ากับหรือมากกว่า 12 โปรเซสแม่ก็จะสิ้นสุดการทำงานของตัวเอง ดังนั้นเมื่อรวมเวลาที่นับแล้วเท่ากับ 60 วินาที (12 x 5) นั่นเอง
แสดงตัวอย่างโปรแกรมตรวจสอบสัญญาณตามที่กำหนดเพื่อเรียกไปยังฟังก์ชันที่กำหนดไว้
// signal4.c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
int r, n;
void HL_AlarmSig(int al) {
printf(" Every 5 sec I have to tell you this PID \n");
printf(" ....... THIS PROCESS ID = %d ....... \n", getpid());
signal(SIGALRM, HL_AlarmSig);
r = alarm(5);
}
void Nothing(int s) {
signal(s, Nothing);
}
void InfiniteLoop(int s) {
printf(" No ! I don't care signal number %d \n", s);
printf("If you want to stop this process send signal number 9 ONLY!!!\n");
signal(s, InfiniteLoop);
for (;;) {
printf("Loop\n");
}; /* infinite loop */
}
int main(void) {
for (n = 1; n <= 31; n++) {
signal(n, Nothing);
}
signal(SIGINT, InfiniteLoop);
signal(SIGALRM, HL_AlarmSig);
printf(" If you want to go into the infinite loop \n");
printf(" .......press ^C within 5 sec...... \n ");
r = alarm(5);
for (;;) {
};
printf(" Program Terminate !! \n");
return 0;
}
$ gcc -o signal4 signal4.c -Wall
$ ./signal4
If you want to go into the infinite loop
.......press ^C within 5 sec......
Every 5 sec I have to tell you this PID
....... THIS PROCESS ID = 21538 .......
Every 5 sec I have to tell you this PID
....... THIS PROCESS ID = 21538 .......
(ถ้ากด Ctrl+C โปรแกรมจะเข้าลูปอนันต์)
แสดงตัวอย่างโปรแกรม
// signal5.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
int n, status;
void HL_Child(int s) {
printf("**Child**I received signal No. %d .. you can't kill me \n", s);
signal(s, HL_Child);
}
void HL_Parent(int s) {
printf("***Parent*** I received signal number %d \n", s);
signal(s, HL_Parent);
}
void HL_Parent14(int s) {
printf("***Parent*** I received signal No. %d == Alarm Signal \n", s);
signal(s, HL_Parent14);
}
void HL_Child14(int s) {
printf("***Child*** I received signal No %d == Alarm Signal \n", s);
signal(s, HL_Child14);
}
void HL_Child3(int s) {
printf("***Child*** I received signal No %d .. I will exit \n", s);
exit(5);
}
int main(void) {
int i;
for (i = 1; i <= 31; i++)
signal(i, HL_Parent);
n = fork();
if (n == 0) /* Child Process */
{
for (i = 1; i <= 31; i++)
signal(i, HL_Child);
signal(SIGALRM, HL_Child14);
signal(3, HL_Child3);
alarm(2);
printf("--c-- Child ID = %d \n", getpid());
printf("--c-- Child Process goes into the infinite loop \n");
printf("--c-- Signal number 3 (^\\) can kill me \n");
for (;;) {
};
} else /* Parent Process */
{
printf("--p-- Parent ID = %d \n", getpid());
signal(SIGALRM, HL_Parent14);
/* alarm(2); */
printf("--p-- I am waiting for child exit or any signal \n");
wait(&status);
printf("--p-- Wait completed !! and I am waiting again ...\n");
wait(&status);
printf("--p-- Wait completed !! ...exit status = %d\n", status >> 8);
printf("--p-- I am pausing for check signal from child exit ... \n");
pause();
wait(&status);
printf("--p-- Wait completed !! ...exit status = %d \n", status >> 8);
printf("--p-- I will execute newprog now !! \n");
for (i = 1; i <= 31; i++)
signal(i, SIG_IGN);
signal(3, SIG_DFL);
signal(2, HL_Parent);
i = execl("newprog", "newprog", 0);
printf("--p-- Parent Process terminated !!\n");
exit(0);
}
return 0;
}
$ gcc -o signal5 signal5.c -Wall
$ ./signal5
--p-- Parent ID = 22119
--p-- I am waiting for child exit or any signal
--c-- Child ID = 22120
--c-- Child Process goes into the infinite loop
--c-- Signal number 3 (^\) can kill me
(กด Ctrl+C เพื่อส่งสัญญาณให้ child process)
^C***Parent*** I received signal number 2
**Child**I received signal No. 2 .. you can't kill me
(กด Ctrl+C เพื่อส่งสัญญาณให้ child process)
^C***Parent*** I received signal number 2
**Child**I received signal No. 2 .. you can't kill me
***Child*** I received signal No 14 == Alarm Signal
(กด Ctrl+Z เพื่อส่งสัญญาณให้ child process)
^Z**Child**I received signal No. 18 .. you can't kill me
***Parent*** I received signal number 18
(ถ้ากด Ctrl+\ เพื่อส่งสัญญาณให้ child process เพื่อเรียกฟังก์ชัน HL_Child3() )
^\***Parent*** I received signal number 3
***Child*** I received signal No 3 .. I will exit
***Parent*** I received signal number 20
--p-- Wait completed !! and I am waiting again ...
--p-- Wait completed !! ...exit status = 5
--p-- I am pausing for check signal from child exit ...
(กด Ctrl+C เพื่อสั่งหยุดโปรแกรม)
^C***Parent*** I received signal number 2
--p-- Wait completed !! ...exit status = 5
--p-- I will execute newprog now !!
--p-- Parent Process terminated !!
แสดงตัวอย่างโปรแกรมที่ใช้สำหรับแก้ปัญหาการจัดการคิวของการสิ้นสุดของโปรเซสลูกที่แตกต่างกรณีกัน (exit statues) ซึ่งเมื่อไหร่ก็ตามที่สถานะของโปรเซสลูกเปลี่ยนแปลงโปรเซสแม่ก็จะได้รับสัญญาณ SIGCHLD
/*
* signal6.c
*
* The parent process pulls a child exit status whenever the OS notifies
* a child status change.
*
* Created by Mij <[email protected]> on 04/01/05.
* Original source file available at http://mij.oltrelinux.com/devel/unixprg/
*
*/
/* for printf() and fgetc() */
#include <stdio.h>
/* for fork() */
#include <sys/types.h>
#include <unistd.h>
/* for srandom() and random() */
#include <stdlib.h>
/* for time() [seeding srandom()] */
#include <time.h>
/* for waitpid() */
#include <sys/wait.h>
/* for signal(), kill() and raise() */
#include <signal.h>
/* how many childs to raise */
#define NUM_PROCS 5
/* handler prototype for SIGCHLD */
void child_handler(int);
int main(int argc, char *argv[]) {
int i, exit_status;
/* execute child_handler() when receiving a signal of type SIGCHLD */
signal(SIGCHLD, &child_handler);
/* initialize the random num generator */
srandom(time(NULL));
printf("Try to issue a \'ps\' while the process is running...\n");
/* produce NUM_PROCS childs */
for (i = 0; i < NUM_PROCS; i++) {
if (!fork()) {
/* child */
/* choosing a random exit status between 0 and 99 */
exit_status = (int) (random() % 100);
printf(" -> New child %d, will exit with %d.\n", (int) getpid(),
exit_status);
/* try to skip signals overlapping */
sleep((unsigned int) (random() % 3));
/* choosing a value to exit between 0 and 99 */
exit(exit_status);
}
/* father */
sleep((unsigned int) (random() % 2));
}
/* checkpoint */
printf("parent: done with fork()ing.\n");
/* why this is not equivalent to sleep(20) here? */
for (i = 0; i < 10; i++) {
sleep(1);
}
/* all the child processes should be done now */
printf("I did not purge all the childs. Timeout; exiting.\n");
/* terminate myself => exit */
kill(getpid(), 15);
/* this won't be actually executed */
return 0;
}
/* handler definition for SIGCHLD */
void child_handler(int sig_type) {
int child_status;
pid_t child;
static int call_num = 0;
/* getting the child's exit status */
child = waitpid(0, &child_status, 0);
printf("<- Child %d exited with status %d.\n", child,
WEXITSTATUS(child_status));
/* did we get all the childs? */
if (++call_num >= NUM_PROCS) {
printf("I got all the childs this time. Going to exit.\n");
exit(0);
}
return;
}
$ gcc -o signal6 signal6.c -Wall
$ ./signal6
Try to issue a 'ps' while the process is running...
-> New child 12274, will exit with 68.
-> New child 12275, will exit with 57.
-> New child 12276, will exit with 96.
parent: done with fork()ing.
-> New child 12277, will exit with 18.
-> New child 12278, will exit with 72.
<- Child 12275 exited with status 57.
<- Child 12274 exited with status 68.
<- Child 12277 exited with status 18.
<- Child 12276 exited with status 96.
<- Child 12278 exited with status 72.
I got all the childs this time. Going to exit.
Last updated
Was this helpful?