The C programming language provides mechanisms for handling asynchronous events through signals and interrupt service routines (ISRs).
Signals
In C, a signal is a limited form of inter-process communication used to notify a process that a particular event has occurred. Signals are defined in the C11 standard under "Signal handling" (Section 7.14). Each signal is associated with a unique integer identifier (signal number), and the standard library provides functions to manage signal handling, such as `signal()` and `raise()`.
Signal Characteristics
When a signal is received, it interrupts the normal flow of the program and transfers control to a signal handler function. Key points about signals include:
- Asynchronous Nature: Signals can occur at any time during the execution of a program.
- Handler Installation: Using the `signal()` function, a program can install a custom handler for specific signals.
- Behavior on Receipt:
- The state of objects that are neither lock-free atomic objects nor of type `volatile sig_atomic_t` becomes unspecified.
- The floating-point environment's state is indeterminate if modified by the handler and not restored.
```c
#include
#include
#include
void handler(int sig) {
printf("Caught signal %d\n", sig);
exit(1);
}
int main() {
signal(SIGINT, handler);
while (1) {
printf("Running...\n");
sleep(1);
}
return 0;
}
```
This example installs a handler for the `SIGINT` signal (typically generated by pressing `Ctrl+C`). When the signal is received, the handler prints a message and terminates the program.
Interrupts
Interrupts are hardware or software signals that prompt the processor to temporarily halt its current operations and execute a function defined by an interrupt service routine (ISR). Interrupts are not explicitly covered by the C standard but play a crucial role in embedded systems.
Interrupt Characteristics
- Hardware-Driven: Generated by peripherals or external devices.
- Critical Sections: Certain regions in the code must disable interrupts to prevent race conditions.
- Real-Time Response: ISRs need to execute quickly to resume normal operation.
Interrupt Handling in Embedded Systems
In embedded systems, interrupt handling is typically managed by the microcontroller or processor's architecture. The C standard library does not provide built-in support for interrupts; instead, they are handled via platform-specific extensions and assembly code.
Example
```c
#include
<avr/interrupt.h>
#include <avr/io.h>
ISR(TIMER1_OVF_vect) { // Timer1 overflow interrupt service
routine PORTB ^= (1 << PB0); // Toggle LED connected to
PB0
}
int main(void) {
DDRB |= (1 << DDB0); // Set PB0
as output TIMSK1 |= (1 << TOIE1); // Enable Timer1
overflow interrupt sei(); // Enable global interrupts while (1) {
// Main loop
}
return 0;
}
```
This example is for an AVR microcontroller, where ISR macro defines the interrupt service routine for
Timer1 overflow. sei() enables global interrupts, allowing the ISR to toggle an LED on overflow.
Signals vs. Interrupts
While both signals and interrupts serve to manage asynchronous events, they are fundamentally
different:
- Signals: Defined by the C standard, used for inter-process communication, handled at the software level.
- Interrupts: Primarily hardware-driven, managed by ISRs, crucial for real-time systems and embedded programming.