CS 355 - Systems Programming:
Unix/Linux with C

Terminal Control and Signals

Reference: Molay, Understanding Unix/Linux Programming, Chapter 6

Software tools vs. device-specific program

Software tools: read stdin or files, write to stdout
$ sort > outputfile
$ sort x.txt > /dev/lp
$ who | tr '[a-z]' '[A-Z]'
Device-specific programs: control device for particular application
User programs is a common example of a device-specific program that interacts with the terminal.

Terminal modes

Canonical mode a.k.a. cooked mode
Buffered until the user presses Enter. Buffering enables the driver to perform basic editing with the erase and word-erase keys.
Noncanonical mode a.k.a. crmode
No buffering, but the the driver handles Ctrl-C and translating between newline and carriage return.
Non-anything mode a.k.a. raw mode
The driver passes the input directly into the program.

Setting a specific terminal mode: use the tcsetattr() function.

Signals

A signal is a kernel mechanism that can send a message to a running process. Requests for signals to be sent can come from:

Signals caused by something a process does (e.g. division by zero) are called synchronous signals. Signals caused by events outside the process (e.g. user pressing Ctrl-C) are called asynchronous signals.

A list of signals is available in /usr/include/signal.h:

#define   SIGHUP    1   /* hangup */
#define   SIGINT    2   /* interrupt */
#define   SIGQUIT   3   /* quit */
#define   SIGILL    4   /* illegal instruction (not reset when caught) */
#define   SIGTRAP   5   /* trace trap (not reset when caught) */
#define   SIGABRT   6   /* abort() */
#define   SIGEMT    7   /* EMT instruction */
#define   SIGFPE    8   /* floating point exception */
#define   SIGKILL   9   /* kill (cannot be caught or ignored) */
#define   SIGBUS   10   /* bus error */
#define   SIGSEGV  11   /* segmentation violation */
#define   SIGSYS   12   /* bad argument to system call */
#define   SIGPIPE  13   /* write on a pipe with no one to read it */
#define   SIGALRM  14   /* alarm clock */
#define   SIGTERM  15   /* software termination signal from kill */

What can be done about a signal (e.g. SIGINT):

Accept the default action
This usually terminates the process. As shown below, this can be changed. To restore the original action:
signal(sig_num, SIG_DFL);
Ignore the signal
signal(sig_num, SIG_IGN);
Call a function
signal(sig_num, function_name);

How to use signal()

Using the signal() to handle signals:

result = signal(int sig_num,void (*action)(int));

where sig_num is the signal to respond to and action is either a function pointer, SIG_IGN to ignore the signal, or SIG_DFL to reset the signal to its default action.

Example:

#include <stdio.h>
#include <signal.h>

main() {
   void  f(int);             /* declare the handler     */
   int   i;

   signal(SIGINT, f);        /* install the handler     */
   for(i=0; i<5; i++ ){      /* do something else       */
      printf("hello\n");
      sleep(1);
   }
}

void f(int signum) {         /* this function is called */
   printf("OUCH!\n");
}

More on process management in Unix

Ctrl-C terminates the current process.

Ctrl-Z suspends the current process.

Use ps or ps -A to see the list of current processes:

stankurkovsky@cloudshell:~$ ps
    PID TTY          TIME CMD
    316 pts/0    00:00:00 start-shell.sh
    319 pts/0    00:00:00 tmux: client
   1037 pts/1    00:00:00 a.out
   1040 pts/1    00:00:00 ps

You may also use top or htop to see a detailed list of current processes.

Use kill to kill a process. kill -KILL can be used to terminate a stubborn process.

stankurkovsky@cloudshell:~$ kill -KILL 1037
[1]+  Killed                  ./a.out