📄 kernel-hacking.tmpl
字号:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []><book id="lk-hacking-guide"> <bookinfo> <title>Unreliable Guide To Hacking The Linux Kernel</title> <authorgroup> <author> <firstname>Rusty</firstname> <surname>Russell</surname> <affiliation> <address> <email>rusty@rustcorp.com.au</email> </address> </affiliation> </author> </authorgroup> <copyright> <year>2005</year> <holder>Rusty Russell</holder> </copyright> <legalnotice> <para> This documentation is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. </para> <para> This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. </para> <para> You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA </para> <para> For more details see the file COPYING in the source distribution of Linux. </para> </legalnotice> <releaseinfo> This is the first release of this document as part of the kernel tarball. </releaseinfo> </bookinfo> <toc></toc> <chapter id="introduction"> <title>Introduction</title> <para> Welcome, gentle reader, to Rusty's Remarkably Unreliable Guide to Linux Kernel Hacking. This document describes the common routines and general requirements for kernel code: its goal is to serve as a primer for Linux kernel development for experienced C programmers. I avoid implementation details: that's what the code is for, and I ignore whole tracts of useful routines. </para> <para> Before you read this, please understand that I never wanted to write this document, being grossly under-qualified, but I always wanted to read it, and this was the only way. I hope it will grow into a compendium of best practice, common starting points and random information. </para> </chapter> <chapter id="basic-players"> <title>The Players</title> <para> At any time each of the CPUs in a system can be: </para> <itemizedlist> <listitem> <para> not associated with any process, serving a hardware interrupt; </para> </listitem> <listitem> <para> not associated with any process, serving a softirq or tasklet; </para> </listitem> <listitem> <para> running in kernel space, associated with a process (user context); </para> </listitem> <listitem> <para> running a process in user space. </para> </listitem> </itemizedlist> <para> There is an ordering between these. The bottom two can preempt each other, but above that is a strict hierarchy: each can only be preempted by the ones above it. For example, while a softirq is running on a CPU, no other softirq will preempt it, but a hardware interrupt can. However, any other CPUs in the system execute independently. </para> <para> We'll see a number of ways that the user context can block interrupts, to become truly non-preemptable. </para> <sect1 id="basics-usercontext"> <title>User Context</title> <para> User context is when you are coming in from a system call or other trap: like userspace, you can be preempted by more important tasks and by interrupts. You can sleep, by calling <function>schedule()</function>. </para> <note> <para> You are always in user context on module load and unload, and on operations on the block device layer. </para> </note> <para> In user context, the <varname>current</varname> pointer (indicating the task we are currently executing) is valid, and <function>in_interrupt()</function> (<filename>include/linux/interrupt.h</filename>) is <returnvalue>false </returnvalue>. </para> <caution> <para> Beware that if you have preemption or softirqs disabled (see below), <function>in_interrupt()</function> will return a false positive. </para> </caution> </sect1> <sect1 id="basics-hardirqs"> <title>Hardware Interrupts (Hard IRQs)</title> <para> Timer ticks, <hardware>network cards</hardware> and <hardware>keyboard</hardware> are examples of real hardware which produce interrupts at any time. The kernel runs interrupt handlers, which services the hardware. The kernel guarantees that this handler is never re-entered: if the same interrupt arrives, it is queued (or dropped). Because it disables interrupts, this handler has to be fast: frequently it simply acknowledges the interrupt, marks a 'software interrupt' for execution and exits. </para> <para> You can tell you are in a hardware interrupt, because <function>in_irq()</function> returns <returnvalue>true</returnvalue>. </para> <caution> <para> Beware that this will return a false positive if interrupts are disabled (see below). </para> </caution> </sect1> <sect1 id="basics-softirqs"> <title>Software Interrupt Context: Softirqs and Tasklets</title> <para> Whenever a system call is about to return to userspace, or a hardware interrupt handler exits, any 'software interrupts' which are marked pending (usually by hardware interrupts) are run (<filename>kernel/softirq.c</filename>). </para> <para> Much of the real interrupt handling work is done here. Early in the transition to <acronym>SMP</acronym>, there were only 'bottom halves' (BHs), which didn't take advantage of multiple CPUs. Shortly after we switched from wind-up computers made of match-sticks and snot, we abandoned this limitation and switched to 'softirqs'. </para> <para> <filename class="headerfile">include/linux/interrupt.h</filename> lists the different softirqs. A very important softirq is the timer softirq (<filename class="headerfile">include/linux/timer.h</filename>): you can register to have it call functions for you in a given length of time. </para> <para> Softirqs are often a pain to deal with, since the same softirq will run simultaneously on more than one CPU. For this reason, tasklets (<filename class="headerfile">include/linux/interrupt.h</filename>) are more often used: they are dynamically-registrable (meaning you can have as many as you want), and they also guarantee that any tasklet will only run on one CPU at any time, although different tasklets can run simultaneously. </para> <caution> <para> The name 'tasklet' is misleading: they have nothing to do with 'tasks', and probably more to do with some bad vodka Alexey Kuznetsov had at the time. </para> </caution> <para> You can tell you are in a softirq (or tasklet) using the <function>in_softirq()</function> macro (<filename class="headerfile">include/linux/interrupt.h</filename>). </para> <caution> <para> Beware that this will return a false positive if a bh lock (see below) is held. </para> </caution> </sect1> </chapter> <chapter id="basic-rules"> <title>Some Basic Rules</title> <variablelist> <varlistentry> <term>No memory protection</term> <listitem> <para> If you corrupt memory, whether in user context or interrupt context, the whole machine will crash. Are you sure you can't do what you want in userspace? </para> </listitem> </varlistentry> <varlistentry> <term>No floating point or <acronym>MMX</acronym></term> <listitem> <para> The <acronym>FPU</acronym> context is not saved; even in user context the <acronym>FPU</acronym> state probably won't correspond with the current process: you would mess with some user process' <acronym>FPU</acronym> state. If you really want to do this, you would have to explicitly save/restore the full <acronym>FPU</acronym> state (and avoid context switches). It is generally a bad idea; use fixed point arithmetic first. </para> </listitem> </varlistentry> <varlistentry> <term>A rigid stack limit</term> <listitem> <para> Depending on configuration options the kernel stack is about 3K to 6K for most 32-bit architectures: it's about 14K on most 64-bit archs, and often shared with interrupts so you can't use it all. Avoid deep recursion and huge local arrays on the stack (allocate them dynamically instead). </para> </listitem> </varlistentry> <varlistentry> <term>The Linux kernel is portable</term> <listitem> <para> Let's keep it that way. Your code should be 64-bit clean, and endian-independent. You should also minimize CPU specific stuff, e.g. inline assembly should be cleanly encapsulated and minimized to ease porting. Generally it should be restricted to the architecture-dependent part of the kernel tree. </para> </listitem> </varlistentry> </variablelist> </chapter> <chapter id="ioctls"> <title>ioctls: Not writing a new system call</title> <para> A system call generally looks like this </para> <programlisting>asmlinkage long sys_mycall(int arg){ return 0; } </programlisting> <para> First, in most cases you don't want to create a new system call. You create a character device and implement an appropriate ioctl for it. This is much more flexible than system calls, doesn't have to be entered in every architecture's <filename class="headerfile">include/asm/unistd.h</filename> and <filename>arch/kernel/entry.S</filename> file, and is much more likely to be accepted by Linus. </para> <para> If all your routine does is read or write some parameter, consider implementing a <function>sysfs</function> interface instead. </para> <para> Inside the ioctl you're in user context to a process. When a error occurs you return a negated errno (see <filename class="headerfile">include/linux/errno.h</filename>), otherwise you return <returnvalue>0</returnvalue>. </para> <para> After you slept you should check if a signal occurred: the Unix/Linux way of handling signals is to temporarily exit the system call with the <constant>-ERESTARTSYS</constant> error. The system call entry code will switch back to user context, process the signal handler and then your system call will be restarted (unless the user disabled that). So you should be prepared to process the restart, e.g. if you're in the middle of manipulating some data structure. </para> <programlisting>if (signal_pending()) return -ERESTARTSYS; </programlisting> <para> If you're doing longer computations: first think userspace. If you <emphasis>really</emphasis> want to do it in kernel you should regularly check if you need to give up the CPU (remember there is cooperative multitasking per CPU). Idiom: </para> <programlisting>cond_resched(); /* Will sleep */ </programlisting> <para> A short note on interface design: the UNIX system call motto is "Provide mechanism not policy". </para> </chapter> <chapter id="deadlock-recipes"> <title>Recipes for Deadlock</title> <para> You cannot call any routines which may sleep, unless: </para> <itemizedlist> <listitem> <para> You are in user context. </para> </listitem> <listitem> <para> You do not own any spinlocks. </para> </listitem> <listitem> <para> You have interrupts enabled (actually, Andi Kleen says that the scheduling code will enable them for you, but that's probably not what you wanted). </para> </listitem> </itemizedlist> <para> Note that some functions may sleep implicitly: common ones are the user space access functions (*_user) and memory allocation functions without <symbol>GFP_ATOMIC</symbol>. </para> <para> You should always compile your kernel <symbol>CONFIG_DEBUG_SPINLOCK_SLEEP</symbol> on, and it will warn you if you break these rules. If you <emphasis>do</emphasis> break the rules, you will eventually lock up your box. </para> <para> Really. </para> </chapter> <chapter id="common-routines"> <title>Common Routines</title> <sect1 id="routines-printk"> <title> <function>printk()</function> <filename class="headerfile">include/linux/kernel.h</filename> </title> <para> <function>printk()</function> feeds kernel messages to the console, dmesg, and the syslog daemon. It is useful for debugging and reporting errors, and can be used inside interrupt context, but use with caution: a machine which has its console flooded with printk messages is unusable. It uses a format string mostly compatible with ANSI C printf, and C string concatenation to give it a first "priority" argument: </para> <programlisting>printk(KERN_INFO "i = %u\n", i); </programlisting>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -