Interrupt and interrupt handling (1)
(1): interrupted
An interrupt is essentially a special electrical signal sent to a processor by a hardware device. After the processor receives the interruption, it will immediately report the arrival of the signal to the operating system, and then the operating system will process the new data. Different devices have different interruptions, and each interruption uses a unique digital sign. These interrupt values are called the interrupt request line (IRQ ). Interruption occurs anytime, anywhere. That is to say, interruption is not considered to be synchronized with the CPU clock.
Exception: The exception must be generated in sync with the processor clock, and the exception also becomes a synchronization interruption. When the processor executes wrong commands due to programming errors, or when special circumstances occur during execution, it must rely on the kernel for processing, the processor will generate an exception. The interruption is caused by hardware, and the exception is caused by software.
(2): interrupt handling program
In response to a specific interrupt, the kernel executes a function called the interrupt handler or interrupt service routine. Each device that generates an interruption has a corresponding interrupt processing program. The real difference between the interrupt handler and other kernel functions is that the interrupt handler is called by the kernel to respond to the middle end, and they run in a special context called the interrupt context, the interrupt context is also an atomic context, and the code executed in this context cannot be blocked.
(3) Comparison between the upper half and the lower half
Because the interrupt processing program requires fast operation and a large amount of work, terminal processing is divided into two parts. The interrupt handler is the upper half. Once an interrupt is received, it starts execution immediately, but only works with a strict time limit. The work that can be executed later will be postponed to the lower half.
(4) Registration of interrupt handling procedures
Interrupt handlers are part of the drivers that manage hardware. Each device has a related driver. If the device is interrupted, the corresponding driver registers an interrupt handler.
The driver can register an interrupt handler through the request_irp () function, which is defined in the linux/interrupt. h file and activates a given disconnection to handle the interrupt.
static inline int __must_checkrequest_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev){ return request_threaded_irq(irq, handler, NULL, flags, name, dev);}
The first parameter indicates the interrupt number to be allocated. For most other devices, the interrupt number can either be obtained by probe or dynamically determined by programming.
The second handler parameter is a pointer pointing to the actual interrupt processing program that handles the interrupt. This function is called as soon as the operating system receives an interruption.
The prototype of the handler function.
typedef irqreturn_t (*irq_handler_t)(int, void *);
1: interrupt handler flag
The third parameter flags can be 0 or a bitmask of one or more of the following flags. It is defined in the linux/interrupt. h file. Below are several important indicators:
/* * These flags used only by the kernel as part of the * irq handling routines. * * IRQF_DISABLED - keep irqs disabled when calling the action handler * IRQF_SAMPLE_RANDOM - irq is used to feed the random generator * IRQF_SHARED - allow sharing the irq among several devices * IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur * IRQF_TIMER - Flag to mark this interrupt as timer interrupt * IRQF_PERCPU - Interrupt is per cpu * IRQF_NOBALANCING - Flag to exclude this interrupt from irq balancing * IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is * registered first in an shared interrupt is considered for * performance reasons) * IRQF_ONESHOT - Interrupt is not reenabled after the hardirq handler finished. * Used by threaded interrupts which need to keep the * irq line disabled until the threaded handler has been run. */#define IRQF_DISABLED 0x00000020#define IRQF_SAMPLE_RANDOM 0x00000040#define IRQF_SHARED 0x00000080#define IRQF_PROBE_SHARED 0x00000100#define IRQF_TIMER 0x00000200#define IRQF_PERCPU 0x00000400#define IRQF_NOBALANCING 0x00000800#define IRQF_IRQPOLL 0x00001000#define IRQF_ONESHOT 0x00002000
Ir__disable-after this flag is set, it means that the kernel will disable all other interruptions during processing of the interrupt handler itself. If this parameter is not set, the interrupt handler can run at the same time as any interrupt except itself.
Ir1__sample_random-this flag indicates that the interruption caused by this device contributes to the kernel entropy pool. The kernel entropy pool is responsible for exporting real random numbers from various random events. If this flag is specified, the interruption interval from the device is filled into the entropy pool as entropy.
Ir1__timer-this sign is especially prepared for the Interrupt Processing of the system timer.
Ir__shared-this flag indicates that a disconnection can be shared among multiple interrupt handlers. This flag must be specified for each handler that terminates online registration. Otherwise, only one handler can be added to each thread.
The fourth parameter name is the ASCII text representation of the interrupt-related device.
The fifth parameter dev is used for shared disconnection. When an interrupt handler needs to be released, dev will provide a unique identifier (cookie) to delete the specified one from the many interrupt handlers that are disconnected in the sharing process.
If request_irq () is successfully executed, 0 is returned. If a non-0 value is returned, an error occurs. The most common error is-EBUSY, which indicates that the specified disconnection is already in use.
Note that the request_irq () function may sleep. Therefore, you cannot call this function in the interrupted context or other code that does not allow blocking.
2: An Example of Interruption
Request a disconnection in a driver and install the interrupt handler through request_irq:
request_irq();if(request_irq(irqn,my_interrupt,IRQF_SHARED,"my_device",my_dev)){ printk(KERN_ERR "my_device: cannot register IRQ %d",irqn); return -EIO;}
When writing an interrupt handler, the initialization sequence of the hardware and registration of the interrupt handler must be correct to prevent the interrupt handler from being executed before it is set to initialization.
3: Release the interrupt handler
When you uninstall the driver, you need to cancel the interrupt handler and release the disconnection. the above action needs to be called:
void free_irq(unsigned int irq,void *dev)
If the specified disconnection is not shared, this function will disable the disconnection while deleting the handler. if the disconnection is shared, only the interrupt handler corresponding to dev is deleted, and the disconnection itself is disabled only when the last handler is deleted.
(5): Compile the Interrupt Processing Program
Here is a statement of the interrupt handler:
static irqreturn_t intr_handler(int irq,void *dev);
Note that the parameter type matches the parameter type required by handler in the request_irq () parameter. The first parameter irq is the interrupt Number of the handler to interrupt.
The second parameter dev is a common pointer, which must be consistent with the dev parameter passed to request_irq () when the interrupt handler is registered.
The Return Value of the interrupt handler is a special type: irqreturn_t. the interrupt handler may return two special values: IRQ_NONE and IRQ_HANDLED. when the interrupt handler detects an interrupt, but the device corresponding to the interrupt is not the source specified during the registration of the handler, IRQ_NONE is returned. when the interrupt handler is called correctly and the corresponding device is indeed interrupted, IRQ_HANDLED is returned.
Note:
In Linux, the interrupt handler does not need to be re-imported. when a given interrupt handler is being executed, the corresponding Disconnection will be blocked on all processors. to prevent receiving another interruption on the same interrupt line. it can be seen that the same interrupt handler will never be called at the same time to handle nested interruptions.
1: Shared interrupt handling program
There are several differences between shared interrupt handlers and non-shared interrupt handlers:
1): The flags parameter of request_irq () must be set to the ir1__shared flag. 2): For each registered interrupt handler, the dev parameter must be unique. the pointer pointing to any device structure can meet this requirement. the interrupt handler cannot be passed a NULL value. 3): the interrupt processing program must be able to identify whether the device actually has an interruption. this requires both hardware support and related processing logic in the processing program.
In addition, when you specify the IRQF_SHARED flag to call request_irq (), it is only possible to succeed in the following two cases: the disconnection is not currently registered, or all registered handlers on this line specify ir1__shared.
2: interrupt handler instance
Next, let's take a look at the RTC (real-time clock) interrupt processing program. the program is located in drivers/char/rtc. c file. this device is used for system clock and provides an alarm or periodic timer.
First, register the interrupt handler during rtc initialization. Let's take a look.
Function rtc_init (void ):
/* Register rtc_interrupt */if (request_irq (rtc_irq, rtc_interrupt, ir1__shared, "rtc", (void *) & rtc_port) {rtc_has_irq = 0; printk (KERN_ERR "rtc: cannot register IRQ % d", rtc_irq); return-EIO ;}
We can see that the interrupt number is provided by rtc_irq. this variable is used to specify the RTC interrupt for the given architecture. the second parameter is our interrupt handler rtc_interrupt-he will be disconnected from other interrupt handlers because he has set the IRQF_SHARED flag. the fourth parameter indicates that the driver is named "rtc". Because the device allows shared disconnection, the device transmits a real parameter value for each device to the dev parameter.
The following describes the specific interrupt processing functions:
# Ifdef RTC_IRQ/** A very tiny interrupt handler. it runs with IRQF_DISABLED set, * but there is possibility of conflicting with the set_rtc_mmss () * call (the rtc irq and the timer irq can easily run at the same * time in two different CPUs ). so we need to serialize * accesses to the chip with the rtc_lock spinlock that each * architecture shocould implement in the timer code. * (See. /arch/XXXX/ker Nel/time. c for the set_rtc_mmss () function .) ** a very lightweight interrupt processing function. it runs together with the ir1__disabled set, * but it is likely that it conflicts with the set_rtc_mmss () call (rtc interrupt and timer interrupt are easy * run on two different CPUs at the same time ). so we need to use the rtc_lock spin lock to serialize * Access to the chip, so that every architecture should be implemented in the timer code. **/static irqreturn_t rtc_interrupt (int irq, void * dev_id) {/** Can be an alarm interrupt, update complete interrupt, * or a periodic interrupt. we store the status in the * low byte and th E number of interrupts passed ed since * the last read in the remainder of rtc_irq_data. ** it can be an alarm interrupt, an update interrupt, or a periodic interrupt. * We store these statuses in the low bytes of rtc_irq_data, and save the last read interrupt * to other bytes of rtc_irq_data. * // spin lock spin_lock (& rtc_lock); rtc_irq_data + = 0x100; rtc_irq_data & = ~ 0xff; if (is_hpet_enabled () {/** In this case it is hpet rtc interrupt handler * calling us, with the interrupt information * passed as arg1, instead of irq. ** in this case, the hpet rtc interrupt handler function calls us. * The interrupt information is used as the parameter 1 rather than irq */rtc_irq_data | = (unsigned long) irq & 0xF0;} else {rtc_irq_data | = (CMOS_READ (RTC_INTR_FLAGS) & 0xF0);} if (rtc_status & RTC_TIMER_ON) mod_timer (& rtc_irq_timer, jiffies + HZ/rtc_freq + 2 * HZ/100); spin_unlock (& rtc_lock ); /* Now do the rest of the actions * // * execute other operations Now */spin_lock (& rtc_task_lock); if (rtc_callback) rtc_callback-> func (rtc_callback-> private_data); spin_unlock (& rtc_task_lock); Round (& rtc_wait); Round (& signature, SIGIO, POLL_IN); return IRQ_HANDLED;} # endif
This function is called as soon as the computer receives an RTC interrupt. the first call is to ensure that rtc_irq_data is not accessed by other processors on the SMP machine at the same time. The second call is to avoid the same situation of rtc_callback.
A callback function will be executed later. The RTC driver allows you to register a callback function and execute it when every RTC is interrupted.
Finally, IRQ_HANDLED is returned.