HyperNews Linux KHG 讨论页面

支持函数

这里列出了设备驱动程序编写者可以使用的许多最常见的支持函数。如果您发现其他有用的支持函数,请指出来。我知道这不是一个完整的列表,但我希望它是一个有用的列表。

add_request()

static void add_request(struct blk_dev_struct *dev, struct request * req)

这是 ll_rw_block.c 中的一个静态函数,不能被其他代码调用。 但是,理解这个函数,以及理解ll_rw_block(),可能有助于您理解策略例程。

如果请求的设备具有空的请求队列,则请求将被放入队列并调用策略例程。 否则,将选择队列中的适当位置,并将请求插入队列,通过插入排序保持正确的顺序。

正确的顺序(电梯算法)定义为

  1. 读取优先于写入。
  2. 较低的次设备号优先于较高的次设备号。
  3. 较低的块号优先于较高的块号。
电梯算法由宏实现IN_ORDER(),它定义在 drivers/block/blk.h 中 [这可能最近有所更改,但这对于驱动程序编写者来说应该无关紧要...]

定义在: drivers/block/ll_rw_block.c
另请参阅 make_request(), ll_rw_block().

add_timer()

void add_timer(struct timer_list * timer)
#include <linux/timer.h>

在定时器列表中安装定时器结构timer在定时器列表中。

timer_list结构由定义。

struct timer_list {
        struct timer_list *next;
        struct timer_list *prev;
        unsigned long expires;
        unsigned long data;
        void (*function)(unsigned long);
};

为了调用add_timer(),您需要分配一个结构由结构,然后调用init_timer(),将指向您的结构由的指针传递给它。 它将使nextprev元素无效,这是正确的初始化。 如果需要,您可以分配多个结构由结构,并将它们链接到一个列表中。 请确保您正确初始化所有未使用的指针为NULL,否则定时器代码可能会非常混乱。

对于列表中的每个结构,您需要设置三个变量

expires
超时后的 jiffies 数(在 Linux/86 中为百分之一秒;在 Linux/Alpha 中约为千分之一秒)。
function
超时发生后要运行的内核空间函数。
data
作为参数传递给functionfunctionfunction被调用时。

创建此列表后,将指向列表的第一个(通常是唯一的)元素的指针作为参数传递给add_timer()add_timer()

注意:不是进程特定的。 因此,如果您想在超时时唤醒某个进程,您将必须使用 sleep 和 wake 原语。 您通过此机制安装的函数将在中断处理程序运行的同一上下文中运行。

定义在: kernel/sched.c
另请参阅 timer_table在 include/linux/timer.h 中,init_timer(), del_timer().

cli()

#define cli() __asm__ __volatile__ ("cli"::)
#include <asm/system.h>

阻止中断被确认。cli代表 “CLear Interrupt enable”。

另请参阅 sti()

del_timer()

void del_timer(struct timer_list * timer)
#include <linux/timer.h>

删除列表中的定时器结构timer在定时器列表中。

您要删除的定时器列表必须是您之前使用add_timer()add_timer()del_timer()安装的定时器列表的地址。 一旦您调用了结构由del_timer()

定义在: kernel/sched.c
另请参阅 timer_table在 include/linux/timer.h 中,init_timer(), add_timer().

从内核定时器列表中删除定时器,您可以释放

end_request()
static void end_request(int uptodate)

#include "blk.h"

uptodate
如果不等于 0,则表示请求已满足。
如果等于 0,则表示请求未满足。

如果请求已满足 (uptodate != 0), 从内核定时器列表中删除定时器,您可以释放),则维护请求列表,解锁缓冲区,并可能安排调度程序在下一个方便的时间运行 (need_resched = 1; 这在wake_up()中是隐含的,而不是显式的一部分从内核定时器列表中删除定时器,您可以释放),然后在wait_for_request事件上唤醒所有休眠进程,该事件在make_request(), ll_rw_page()ll_rw_swap_file().

中休眠。注意: 此函数是一个静态函数,在 drivers/block/blk.h 中为每个包含 blk.h 的非 SCSI 设备定义。(SCSI 设备以不同的方式执行此操作;高级 SCSI 代码本身为低级设备特定的 SCSI 设备驱动程序提供了此功能。)它包含几个依赖于静态设备信息的定义,例如设备号。 这比更通用的普通 C 函数稍快。

定义在: kernel/blk_drv/blk.h
另请参阅 ll_rw_block(), add_request(), make_request().

free_irq()

void free_irq(unsigned int irq)
#include <linux/sched.h>

释放先前使用request_irq()irqaction()获取的 irq。 接受一个参数

irq
要释放的中断级别。

定义在: kernel/irq.c
另请参阅 request_irq(), irqaction().

get_user()

#define get_user(ptr) ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr))))
#include <asm/segment.h>

允许驱动程序访问用户空间中的数据,用户空间与内核位于不同的段中。 自动派生参数的类型和返回类型。 这意味着您必须正确使用类型。 草率的类型定义将根本无法工作。

[Caution!] 注意: 如果访问的内存已被换出,这些函数可能会导致隐式 I/O,因此此时可能会发生抢占。 即使临界区受到cli()/sti()cli()/sti()cli()/sti()对的保护,也不要将这些函数包含在代码的临界区中,因为隐式 I/O 会破坏您的

这些函数接受一个参数

addr
从中获取数据的地址。
返回
用户空间中该偏移量处的数据。

定义在: include/asm/segment.h
另请参阅 memcpy_*fs(), put_user(), cli(), sti().

inb(), inb_p()

inline unsigned int inb(unsigned short port)
inline unsigned int inb_p(unsigned short port)
#include <asm/io.h>

从端口读取一个字节。inb()尽可能快地运行,而inb_p()在返回之前暂停。 如果您不尽可能快地从某些设备读取数据,它们会更愉快。 这两个函数都接受一个参数

port
要从中读取字节的端口。
返回
该字节在 32 位整数的低字节中返回,而高 3 字节未使用,可能是垃圾值。

定义在: include/asm/io.h
另请参阅 outb(), outb_p().

init_timer()

用于初始化结构由结构的内联函数,用于add_timer().

定义在: include/linux/timer.h
另请参阅 add_timer().

irqaction()

int irqaction(unsigned int irq, struct sigaction *new)
#include <linux/sched.h>

硬件中断实际上很像信号。 因此,能够像信号一样注册中断是有意义的。sa_restorer()字段struct sigaction未使用,但其他方面是相同的。sa.handler()函数的 int 参数可能意味着不同的含义,具体取决于 IRQ 是否使用SA_INTERRUPT标志安装。 如果未使用SA_INTERRUPT标志安装,则传递给处理程序的参数是指向寄存器结构的指针;如果使用SA_INTERRUPT标志安装,则传递的参数是 IRQ 的编号。 有关设置为使用SA_INTERRUPTSA_INTERRUPT标志的处理程序示例,请查看 drivers/char/serial.c 中如何安装rs_interrupt()

timer_listSA_INTERRUPT标志用于确定中断是否应为“快速”中断。 通常,从中断返回后,会检查全局标志need_resched。 如果已设置 (!= 0),则运行schedule(),这可能会调度另一个进程运行。 它们也在启用所有其他中断的情况下运行。 但是,通过设置sigaction结构成员sa_flagsSA_INTERRUPTSA_INTERRUPTschedule().

irqaction()irqaction()

irq
irq
new
指向 sigaction 结构的指针。
返回
-EBUSY如果中断已被获取,
-EINVAL如果sa.handler()new
0 表示成功。

定义在: kernel/irq.c
另请参阅 request_irq(), free_irq()

IS_*(inode)

IS_RDONLY(inode) ((inode)->i_flags & MS_RDONLY)
IS_NOSUID(inode) ((inode)->i_flags & MS_NOSUID)
IS_NODEV(inode) ((inode)->i_flags & MS_NODEV)
IS_NOEXEC(inode) ((inode)->i_flags & MS_NOEXEC)
IS_SYNC(inode) ((inode)->i_flags & MS_SYNC)
#include <linux/fs.h>

这五个测试用于查看 inode 是否在挂载了相应标志的文件系统上。

kfree*()

#define kfree(x) kfree_s((x), 0)
void kfree_s(void * obj, int size)
#include <linux/malloc.h>

释放先前使用kmalloc()分配的内存。 有两个可能的参数

obj
指向要释放的内核内存的指针。
size
为了加快速度,如果您知道大小,请使用kfree_s()并提供正确的大小。 这样,内核内存分配器就知道对象属于哪个 bucket 缓存,并且不必搜索所有 bucket。(有关此术语的更多详细信息,请阅读 mm/kmalloc.c。)

[kfree_s()[可能现在已过时。]

定义在: mm/kmalloc.c, include/linux/malloc.h
另请参阅 kmalloc().

kmalloc()

void * kmalloc(unsigned int len, int priority)
#include <linux/kernel.h>

kmalloc()过去限制为 4096 字节。 现在在 Linux/Intel 上限制为 131056 字节 ((32*4096)-16),在 Alpha 等具有 8Kb 页面的平台上限制为两倍。 Bucket,过去都是 2 的精确幂,现在是 2 的幂减去一些小数,但小于或等于 128 的数字除外。 有关更多详细信息,请参阅 mm/kmalloc.c 中的实现。

kmalloc()irqaction()

len
要分配的内存长度。 如果超过最大值,kmalloc 将记录错误消息 “kmalloc of too large a block (%d bytes).” 并返回NULL.
priority
GFP_KERNELGFP_ATOMIC。 如果选择GFP_KERNELGFP_KERNELkmalloc(),kmalloc() 可能会休眠,从而允许发生抢占。 这是调用kmalloc()的正常方式。 但是,在某些情况下,如果没有可用页面,最好立即返回,而无需尝试休眠以查找页面。 其中一种情况是交换代码,因为它可能导致竞争条件,另一种情况是网络代码,在网络代码中,事情发生的速率可能比通过交换到磁盘以腾出空间来为网络代码提供更多内存的速度快得多。 使用GFP_ATOMIC的最重要原因是,如果它是从中断中调用的,此时您无法休眠,也无法接收其他中断。
返回
NULLNULL
成功时指向已分配内存的指针。
定义在: mm/kmalloc.c
另请参阅 kfree()

ll_rw_block()

void ll_rw_block(int rw, int nr, struct buffer_head *bh[])
#include <linux/fs.h>

没有设备驱动程序会调用此代码:它仅通过缓冲区缓存调用。 但是,理解此函数可能有助于您理解策略例程的功能。

在健全性检查之后,如果设备的请求队列上没有挂起的请求,ll_rw_block()“插入”队列,以便请求在所有请求都进入队列之前不会发出,并按电梯算法排序。make_request()然后为每个请求调用。 如果队列必须插入,则该设备的策略例程不活动,并且在禁用中断的情况下调用它。 策略例程负责重新启用中断。

定义在: devices/block/ll_rw_block.c
另请参阅 make_request(), add_request().

MAJOR()

#define MAJOR(a) (((unsigned)(a))>>8)
#include <linux/fs.h>

这采用 16 位设备号,并通过移出次设备号来给出关联的主设备号。

另请参阅 MINOR().

make_request()

static void make_request(int major, int rw, struct buffer_head *bh)

这是 ll_rw_block.c 中的一个静态函数,不能被其他代码调用。 但是,理解这个函数,以及理解ll_rw_block(),可能有助于您理解策略例程。

make_request()首先检查请求是否为预读或预写,以及缓冲区是否已锁定。 如果是,它会简单地忽略请求并返回。 否则,它会锁定缓冲区,并且对于 SCSI 设备除外,它会检查以确保写入请求不会填满队列,因为读取请求应优先。

如果队列中没有可用空间,并且请求既不是预读也不是预写,make_request()wait_for_request事件上休眠,并在唤醒时重试。 当在队列中找到空间时,请求信息将被填写,并调用add_request()以实际将请求添加到队列。 定义在: devices/block/ll_rw_block.c
另请参阅 add_request(), ll_rw_block().

MINOR()

#define MINOR(a) ((a)&0xff)
#include <linux/fs.h>

这采用 16 位设备号,并通过屏蔽主设备号来给出关联的次设备号。

另请参阅 MAJOR().

memcpy_*fs()

inline void memcpy_tofs(void * to, const void * from, unsigned long n)
inline void memcpy_fromfs(void * to, const void * from, unsigned long n)
#include <asm/segment.h>

在用户空间和内核空间之间复制大于一个字节、字或长字的内存块。 非常小心地使参数的顺序正确!

[Caution!] 注意: 如果访问的内存已被换出,这些函数可能会导致隐式 I/O,因此此时可能会发生抢占。 即使临界区受到cli()/sti()cli()/sti()cli()对的保护,也不要将这些函数包含在代码的临界区中,因为隐式 I/O 会破坏

这些函数接受三个参数

要将数据复制到的地址。
from
从中复制数据的地址。
n
要复制的字节数。

定义在: include/asm/segment.h
另请参阅 get_user(), put_user(), cli(), sti().

outb(), outb_p()

inline void outb(char value, unsigned short port)
inline void outb_p(char value, unsigned short port)
#include <asm/io.h>

向端口写入一个字节。outb()尽可能快地运行,而outb_p()在返回之前暂停。 如果您不尽可能快地向某些设备写入数据,它们会更愉快。 这两个函数都接受两个参数

value
要写入的字节。
port
要向其写入字节的端口。

定义在: include/asm/io.h
另请参阅 inb(), inb_p().

printk()

int printk(const char* fmt, ...)
#include <linux/kernel.h>

printk()是内核的printf()版本,但有一些限制。 它不能处理浮点数,并且还有一些其他限制,这些限制记录在 kernel/vsprintf.c 中。 它接受可变数量的参数

fmt
格式字符串,printf()风格。
...
...printf()风格。
返回
printk()

[Caution!]注意:printk()可能会导致隐式 I/O,如果访问的内存已被换出,因此此时可能会发生抢占。 此外,printk()将设置中断使能标志,所以 永远不要在受 cli() 保护的代码中使用它。 因为它会导致 I/O,所以即使它没有设置中断使能标志,在受保护的代码中使用它也是不安全的。

定义在: kernel/printk.c。

put_user()

#define put_user(x,ptr) __put_user((unsigned long)(x),(ptr),sizeof(*(ptr)))
#include <asm/segment.h>

允许驱动程序在用户空间中写入数据,用户空间与内核位于不同的段中。 自动派生参数的类型和存储大小。 这意味着您必须正确使用类型。 草率的类型定义将根本无法工作。

[Caution!] 注意: 如果访问的内存已被换出,这些函数可能会导致隐式 I/O,因此此时可能会发生抢占。 即使临界区受到cli()/sti()cli()/sti()cli()/sti()对的保护,也不要将这些函数包含在代码的临界区中,因为隐式 I/O 会破坏您的

这些函数接受两个参数

val
要写入的值
addr
要将数据写入的地址。

定义在: asm/segment.h
另请参阅 memcpy_*fs(), get_user(), cli(), sti().

register_*dev()

int register_chrdev(unsigned int major, const char *name, struct file_operations *fops)
int register_blkdev(unsigned int major, const char *name, struct file_operations *fops)
#include <linux/fs.h>
#include <linux/errno.h>

向内核注册设备,让内核检查以确保没有其他驱动程序已抓取相同的主设备号。 接受三个参数

major
要注册的设备的主设备号。
name
唯一标识驱动程序的字符串。 用于 /proc/devices 文件的输出中。
fops
指向该设备的file_operations结构的指针。 这必须不是NULL,否则内核稍后会崩溃。
返回
-EINVAL如果 major >=MAX_CHRDEVMAX_BLKDEV(定义在),分别用于字符设备或块设备。
-EBUSY如果主设备号已被分配。
0 表示成功。

定义在: fs/devices.c
另请参阅 unregister_*dev()

request_irq()

int request_irq(unsigned int irq, void (*handler)(int), unsigned long flags, const char *device)
#include <linux/sched.h>
#include <linux/errno.h>

向内核请求 IRQ,并在成功时安装 IRQ 中断处理程序。 接受四个参数

irq
要请求的 IRQ。
handler
发生 IRQ 时要调用的处理程序。 处理程序函数的参数将是调用它来处理的 IRQ 的编号。
flags
SA_INTERRUPTSA_INTERRUPT以请求“快速”中断,或设置为 0 以请求正常的“慢速”中断。
device
一个字符串,包含设备驱动程序的名称,device
返回
-EINVAL如果irq如果handler = NULL.
-EBUSY如果irqhandler
0 表示成功。
如果您的中断处理需要更多功能,请使用irqaction()函数。 这使用了sigaction结构的大部分功能来提供类似于为用户级程序提供的信号服务。sigaction()

定义在: kernel/irq.c
另请参阅 free_irq(), irqaction().

select_wait()

inline void select_wait(struct wait_queue **wait_address, select_table *p)
#include <linux/sched.h>

将进程添加到正确的select_wait队列。 此函数接受两个参数

wait_address
要添加到等待循环列表的wait_queue指针的地址。
p pNULL, select_wait不执行任何操作,否则当前进程将进入休眠状态。 这应该是传递给您的select_table *wait变量,该变量已传递给您的select()函数。

定义在: linux/sched.h
另请参阅 *sleep_on(), wake_up*()

*sleep_on()

void sleep_on(struct wait_queue ** p)
void interruptible_sleep_on(struct wait_queue ** p)
#include <linux/sched.h>

在事件上休眠,在列表中放置一个wait_queue条目,以便可以在该事件上唤醒进程。sleep_on()进入不可中断的休眠状态:进程可以运行的唯一方法是被wake_up(). interruptible_sleep_on()进入可中断的休眠状态,可以通过信号唤醒,进程超时将导致进程唤醒。 调用wake_up_interruptible()是唤醒进程并使其在离开的地方继续运行所必需的。 两者都接受一个参数

p
指向记录唤醒进程所需信息的正确wait_queue结构的指针。

定义在: kernel/sched.c
另请参阅 select_wait(), wake_up*().

sti()

#define sti() __asm__ __volatile__ ("sti"::)
#include <asm/system.h>

允许中断被确认。sti代表 “SeT Interrupt enable”。

定义在: asm/system.h
另请参阅 cli().

sys_get*()

int sys_getpid(void)
int sys_getuid(void)
int sys_getgid(void)
int sys_geteuid(void)
int sys_getegid(void)
int sys_getppid(void)
int sys_getpgrp(void)

这些系统调用可用于获取下表中所述的信息,或者可以直接从进程表中提取信息,如下所示
foo = current->pid;
pid进程 ID
uid用户 ID
gid组 ID
euid有效用户 ID
egid有效组 ID
ppid进程父进程的进程 ID
pgid进程父进程的组 ID

不应使用系统调用,因为它们速度较慢并且占用更多空间。 因此,它们不再作为符号导出到整个内核。

定义在: kernel/sched.c

unregister_*dev()

int unregister_chrdev(unsigned int major, const char *name)
int unregister_blkdev(unsigned int major, const char *name)
#include <linux/fs.h>
#include <linux/errno.h>

删除设备在内核中的注册,让内核将主设备号分配给其他设备。 接受两个参数

major
要注册的设备的主设备号。 必须与传递给register_*dev().
name
唯一标识驱动程序的字符串。 必须与传递给register_*dev().
返回
-EINVAL如果 major >=MAX_CHRDEVMAX_BLKDEV(定义在如果(定义在major),分别用于字符设备或块设备,或者如果尚未为主要设备name与注册设备时使用的名称不同。
0 表示成功。

定义在: fs/devices.c
另请参阅 register_*dev()

wake_up*()

void wake_up(struct wait_queue ** p)
void wake_up_interruptible(struct wait_queue ** p)
#include <linux/sched.h>

唤醒已被匹配的*sleep_on()函数。wake_up()可用于唤醒队列中可能处于TASK_INTERRUPTIBLETASK_UNINTERRUPTIBLE状态的任务,而wake_up_interruptible()只会唤醒处于TASK_INTERRUPTIBLE状态的任务,并且在仅具有可中断任务的队列中,速度将比wake_up()快得微不足道。 这些函数接受一个参数

p
指向要唤醒的进程的wait_queue结构的指针。

注意,wake_up()不会切换任务,它只会使唤醒的进程可运行,以便下次调用schedule()时,它们将成为运行的候选者。

定义在: kernel/sched.c
另请参阅 select_wait(), *sleep_on()

Copyright (C) 1992, 1993, 1994, 1996 Michael K. Johnson, johnsonm@redhat.com.


消息

14. Question: down/up() - 信号量; set/clear/test_bit() 作者:Erez Strauss
13. Disagree: printk 描述中的错误! 作者:Theodore Ts'o
12. Question: 设备驱动程序中的文件访问? 作者:Paul Osborn
11. None: reguest_region() 和 release_region() 的手册页 (?) 作者:mharrison@i-55.com
10. Question: register_*dev() 可以分配未使用的主设备号吗? 作者:rgerharz@erols.com
1. Note: Register_*dev() 可以分配未使用的主设备号。 作者:Reinhold J. Gerharz
9. Question: memcpy_*fs(): “fs” 是哪种方式? 作者:Reinhold J. Gerharz
1. Note: memcpy_tofs() 和 memcpy_fromfs() 作者:David Hinds
8. Note: init_wait_queue() 作者:Michael K. Johnson
7. Question: request_irq(...,void *dev_id) 作者:Robert Wilhelm
1. None: dev_id 似乎用于 IRQ 共享 作者:Steven Hunyady
6. Idea: 应该提到 udelay 作者:Klaus Lindemann
5. Idea: vprintk 会很好... 作者:Robert Baruch
1. Feedback: 回复:vprintk 会很好...
4. Question: add_timer 函数勘误表? 作者:Tim Ferguson
1. Ok: add_timer 函数勘误表 作者:Tom Bjorkholm
3. Question: 非常短的等待 作者:Kenn Humborg
2. None: 将 kill_xxx() 系列添加到支持函数? 作者:Burkhard Kohl
1. News: 分配大量内存 作者:Michael K. Johnson
1. Question: Linux 2.0 的 bigphysarea? 作者:Greg Hager