Any normal Unix process can terminate in following ways:
- Graceful exit – Normal termination of process. For eg:
- Returning from main, calling exit etc.
- Ungraceful exit – Abnormal termination of process. For eg:
- Abort signal. Due to some fatal error etc.
_exit, _EXIT and exit functions can be used to terminate the process normally. Usually exit functions do the cleanup but if needed a programmer can also define its own exit handler functions to do some specific cleanup actions needed by the program.
atexit () function
In C a process can register at max 32 functions to be called automatically when process calls exit function. To register any function to be called by exit “atexit ()” function is used. The functions which are registered are called exit handlers.
Syntax
#include <stdlib.h>
int atexit(void (*func)(void));
Important Points
- Exit handlers doesn’t take any arguments and it doesn’t return anything.
- Exit handlers are called in reverse order of registration.
- If a function is registered multiple times, then exit () will call that function same number of times.
- In case a program exits via signals then exit handler will not be called (as it’s abnormal termination). In this case signal needs to be caught and exited gracefully to call exit handlers.
Let’s investigate the below program to understand exit handler’s functionality.
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
void exitHandler1(void)
{
printf("Exit handler 1 called \n");
}
void exitHandler2(void)
{
printf("Exit handler 2 called \n");
}
void sigint_handler(int sig)
{
printf("Caught signal %d quitting now \n", sig);
exit(0);
}
int main()
{
struct sigaction sa;
sa.sa_handler = sigint_handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
if(sigaction(SIGINT, &sa, NULL))
{
perror("sigaction");
}
atexit(exitHandler1);
atexit(exitHandler2);
atexit(exitHandler1);
atexit(exitHandler1);
while(1)
{
char buf[300];
int *p = NULL;
printf("Enter the string \n");
scanf("%s", buf);
if (!strcmp(buf, "EXIT"))
break;
if (!strcmp(buf, "KILL")) {
printf ("Get kill instructions");
printf ("%d", *p);
}
printf("string entered [%s] \n", buf);
}
return 0;
}
Let’s analyze the output of above main function.
------ Exit handler not called as Signal not caught -------------
sandy@sakumar-pc2:$ ./a.out
Enter the string
KILL
Segmentation fault (core dumped)
------ Exit handler called in normal termination ----------------
sandy@sakumar-pc2:$ ./a.out
Enter the string
ABC
string entered [ABC]
Enter the string
EXIT
Exit handler 1 called
Exit handler 1 called
Exit handler 2 called
Exit handler 1 called
----- Exit handler called as signal is caught (ctrl + C) -------------------
sandy@sakumar-pc2:$ ./a.out
Enter the string
^CCaught signal 2 quitting now
Exit handler 1 called
Exit handler 1 called
Exit handler 2 called
Exit handler 1 called