An interrupt
The hardware communicates with the operating system through interrupts, and quickly responds to hardware interrupts by registering an interrupt handler at the hardware driver.
The hardware interrupt priority is high, interrupting the program that is currently executing. There are two types of situations:
Hardware interrupts are handled in interrupt handlers
Hardware interrupts are deferred and processed
This specific hardware-related, processing in the interrupt handler, interrupts the currently executing program, all interrupts will be blocked, if the time taken is too long inappropriate,
The responsiveness of the system can be affected by the interaction of the systems. The balance needs to be judged:
If a task is sensitive to time, it is executed in the interrupt handler;
If a person is associated with a hardware, put it in an interrupt handler to execute;
If a task is guaranteed not to be interrupted by another interrupt, it is executed in the interrupt handler;
The remainder is considered in the deferred mechanism of execution-the second half.
Second interrupt execution mechanism--soft interrupt
Soft interrupts are statically allocated during compilation, and soft interrupts are provided to the table before the program executes.
Here's a look at the process:
To add a soft interrupt type:
Linux3.5.3 Code:
enum{ hi_softirq=0, Timer_softirq, Net_tx_softirq, Net_rx_softirq, Block_softirq, BLOCK_IOPOLL_SOFTIRQ, Tasklet_softirq, Sched_softirq, Hrtimer_softirq, RCU_SOFTIRQ,/ * Preferable RCU should always being the last SOFTIRQ */ Nr_softirqs};
Soft Interrupt Table:
static struct softirq_action softirq_vec[Nr_softirqs]
Soft interrupt Structure
struct softirq_action{ void (*action) (struct softirq_action *);};
Register the soft interrupt handler function:
void Open_softirq (int nr, void (*action) (struct softirq_action *)) { //correlation table in corresponding type softirq_vec[nr].action = action ;}
To trigger a soft interrupt:
void Raise_softirq (unsigned int nr) { unsigned long flags; Stop but save interrupt flag Local_irq_save (flags); Suspend the corresponding soft interrupt state Raise_softirq_irqoff (NR); Resume interrupt Local_irq_restore (flags);}
To perform a soft interrupt:
void Irq_exit (void) { invoke_softirq (); DO_SOFTIRQ ();} void __do_softirq (void) { struct softirq_action *h; __U32 pending; int max_restart = Max_softirq_restart; int CPU; Get CPU soft interrupt status flag bit 32-bit represents up to 32 soft interrupts pending = Local_softirq_pending (); restart:/ * Reset the pending bitmask before Enabling IRQs * /set_softirq_pending (0); Local_irq_enable (); h = Softirq_vec; do { //is triggered to execute soft interrupt handler if (pending & 1) {h->action (h); } Next soft interrupt h++; The next soft interrupt status flag bit pending >>= 1; } while (pending); Local_irq_disable (); Pending = Local_softirq_pending (); if (pending &&--max_restart) goto restart; if (pending) wakeup_softirqd (); Lockdep_softirq_exit (); __local_bh_enable (Softirq_offset);}
The basic structure of a soft interrupt is expressed as:
Three interrupt postpone execution mechanism--tasklet
One of the types in a soft interrupt table is: TASKLET_SOFTIRQ
Tasklet is the use of soft interrupts to implement interrupt-deferred processing mechanism. It is often used more tasklet than soft interrupts.
tasklet data structure:
struct tasklet_struct{ //list the next tasklet struct tasklet_struct *next; Tasklet status unsigned long state; Reference counter atomic_t count; Tasklet processing function void (*func) (unsigned long); Processing function parameters unsigned long data;};
State
enum{ tasklet_state_sched,/ * Tasklet is scheduled for execution */ Tasklet_state_run * Tasklet is Runnin G (SMP only) */};
Count: 0 allows activation of execution
declaration Tasklet: can be dynamic or static mode
Static:
#define Declare_tasklet (Name, func, data) struct tasklet_struct name = {NULL, 0, atomic_init (0), func, data} #define DECL Are_tasklet_disabled (Name, func, data) struct tasklet_struct name = {NULL, 0, Atomic_init (1), Func, data}
Dynamic:
void Tasklet_init (struct tasklet_struct *t,void (*func) (unsigned long), unsigned Long data) { t->next = NULL; t->state = 0; Atomic_set (&t->count, 0); T->func = func; T->data = data;}
You also need to write tasklet processing functions.
Dispatch Tasklet:
void Tasklet_hi_schedule (struct tasklet_struct *t) { unsigned long flags; Local_irq_save (flags); T->next = NULL; *__this_cpu_read (tasklet_vec.tail) = t; __this_cpu_write (Tasklet_vec.tail, & (T->next)); Raise_softirq_irqoff (TASKLET_SOFTIRQ); Local_irq_restore (flags);}
Execute the Tasklet handler:
Keep looking at the above dispatch Tasklet program execution:
inline void Raise_softirq_irqoff (unsigned int nr) { __raise_softirq_irqoff (NR); if (!in_interrupt ()) wakeup_softirqd ();} Use KSOFTIRQD kernel thread to handle static void Wakeup_softirqd (void) {/ * interrupts is disabled:no need to stop preemption * / struct Task_struct *tsk = __this_cpu_read (KSOFTIRQD); if (tsk && tsk->state! = task_running) wake_up_process (tsk);}
KSOFTIRQD Kernel Thread:
Soft interrupts are triggered at a high frequency, and soft interrupts are re-triggered during processing, and execution can cause the user's space process to be starved for processing time;
The immediate processing of a re-triggered soft interrupt can lead to an excessive occupy processing time and is not appropriate for immediate processing;
Workaround for this:
As long as there is a soft interrupt that is triggered and waits for a soft interrupt to be processed and re-triggered in the process, the execution will be handled; The soft interrupt is processed immediately and the user space is not executed.
L do not handle soft interrupts that are triggered during the process and are processed when the next interrupt is executed. Soft interrupts are not processed immediately, the system is idle and unreasonable, and the user space is guaranteed execution time.
There are two ways there are problems that can only be taken in this way:
The kernel uses threading to handle soft interrupts, the thread priority is low, it can be preempted, the soft interrupt is processed, and the user space program can be executed.
There is such a thread on each CPU: ksoftirqd/0 or KSOFTIRQD/1 ...
static __init int spawn_ksoftirqd (void) { void *cpu = (void *) (long) smp_processor_id (); int err = Cpu_callback (&CPU_NFB, cpu_up_prepare, CPU); ... return 0;} Early_initcall (SPAWN_KSOFTIRQD); static int __cpuinit cpu_callback (struct notifier_block *nfb, unsigned long Action, void *hcpu) { int hotcpu = (unsigned long) hcpu; struct task_struct *p; Switch (action) {case cpu_up_prepare: Case cpu_up_prepare_frozen: p = kthread_create_on_node (run_ KSOFTIRQD, hcpu, cpu_to_node (HOTCPU), "ksoftirqd/%d", hotcpu); Kthread_bind (P, hotcpu); Per_cpu (KSOFTIRQD, hotcpu) = p; break; ......}
Four interrupt-deferred execution mechanism--Work queue
The work queue is executed by the kernel thread that interrupts the lower part of the program and executes in the thread, and the work queue can create threads to handle the corresponding task.
Worker threads are created by work queues: worker thread, and the system provides default threads to handle worker queues.
Worker thread data structure:
struct Workqueue_struct { unsigned int flags; /* w:wq_* Flags */ Union { struct cpu_workqueue_struct __percpu *pcpu; struct cpu_workqueue_struct *single; unsigned long v; } cpu_wq; /* i:cwq ' s */ struct list_head list; /* W:list of all workqueues */ ...}
CPU Task Force column data structure:
struct Cpu_workqueue_struct {
Per CPU task queue Information struct GLOBAL_CWQ
Each CPU task Queue struct workqueue_struct *wq; ......};
Working data structure:
struct Work_struct { atomic_long_t data; struct List_head entry; work_func_t func;};
To declare a work queue:
Static:
#define DECLARE_WORK (n, F)
Dynamic:
#define Init_work (_work, _func) do { __init_work ((_work), (_func), 0); } while (0)
You need to write a work queue handler function:
typedef void (*work_func_t) (struct work_struct *work);
Scheduling work Queues:
int schedule_work (struct work_struct *work) { return queue_work (SYSTEM_WQ, work);} int queue_work (struct workqueue_struct *wq, struct work_struct *work) { ret = queue_work_on (Get_cpu (), Wq, work);}
Wake worker queue thread processing.
Execute worker Queue handler:
static int worker_thread (void *__worker) {do { struct work_struct *work = list_first_entry (&gcwq-> worklist, struct work_struct, entry); Process_one_work (worker, work); } while (Keep_working (GCWQ));}
You can create new worker queues and threads to process.
Balance is a critical issue!
Notes Linux kernel Learning (v) Interrupt-deferred processing mechanism