这里列出了设备驱动程序编写者可以使用的许多最常见的支持函数。如果您发现其他有用的支持函数,请指出来。我知道这不是一个完整的列表,但我希望它是一个有用的列表。
这是 ll_rw_block.c 中的一个静态函数,不能被其他代码调用。 但是,理解这个函数,以及理解ll_rw_block(),可能有助于您理解策略例程。
如果请求的设备具有空的请求队列,则请求将被放入队列并调用策略例程。 否则,将选择队列中的适当位置,并将请求插入队列,通过插入排序保持正确的顺序。
正确的顺序(电梯算法)定义为
定义在: drivers/block/ll_rw_block.c
另请参阅 make_request(), ll_rw_block().
在定时器列表中安装定时器结构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(),将指向您的结构由的指针传递给它。 它将使next和prev元素无效,这是正确的初始化。 如果需要,您可以分配多个结构由结构,并将它们链接到一个列表中。 请确保您正确初始化所有未使用的指针为NULL,否则定时器代码可能会非常混乱。
对于列表中的每个结构,您需要设置三个变量
创建此列表后,将指向列表的第一个(通常是唯一的)元素的指针作为参数传递给add_timer()add_timer()
注意: 这不是进程特定的。 因此,如果您想在超时时唤醒某个进程,您将必须使用 sleep 和 wake 原语。 您通过此机制安装的函数将在中断处理程序运行的同一上下文中运行。
定义在: kernel/sched.c
另请参阅 timer_table在 include/linux/timer.h 中,init_timer(), del_timer().
阻止中断被确认。cli代表 “CLear Interrupt enable”。
另请参阅 sti()
删除列表中的定时器结构timer在定时器列表中。
您要删除的定时器列表必须是您之前使用add_timer()add_timer()del_timer()安装的定时器列表的地址。 一旦您调用了结构由del_timer()
定义在: kernel/sched.c
另请参阅 timer_table在 include/linux/timer.h 中,init_timer(), add_timer().
#include "blk.h"
如果请求已满足 (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().
释放先前使用request_irq()或irqaction()获取的 irq。 接受一个参数
定义在: kernel/irq.c
另请参阅 request_irq(), irqaction().
允许驱动程序访问用户空间中的数据,用户空间与内核位于不同的段中。 自动派生参数的类型和返回类型。 这意味着您必须正确使用类型。 草率的类型定义将根本无法工作。
注意: 如果访问的内存已被换出,这些函数可能会导致隐式 I/O,因此此时可能会发生抢占。 即使临界区受到cli()/sti()cli()/sti()cli()/sti()对的保护,也不要将这些函数包含在代码的临界区中,因为隐式 I/O 会破坏您的
这些函数接受一个参数
定义在: include/asm/segment.h
另请参阅 memcpy_*fs(), put_user(), cli(),
sti().
从端口读取一个字节。inb()尽可能快地运行,而inb_p()在返回之前暂停。 如果您不尽可能快地从某些设备读取数据,它们会更愉快。 这两个函数都接受一个参数
定义在: include/asm/io.h
另请参阅 outb(), outb_p().
用于初始化结构由结构的内联函数,用于add_timer().
定义在: include/linux/timer.h
另请参阅 add_timer().
硬件中断实际上很像信号。 因此,能够像信号一样注册中断是有意义的。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_flags为SA_INTERRUPTSA_INTERRUPTschedule().
irqaction()irqaction()
定义在: kernel/irq.c
另请参阅 request_irq(), free_irq()
这五个测试用于查看 inode 是否在挂载了相应标志的文件系统上。
释放先前使用kmalloc()分配的内存。 有两个可能的参数
[kfree_s()[可能现在已过时。]
定义在: mm/kmalloc.c, include/linux/malloc.h
另请参阅 kmalloc().
kmalloc()过去限制为 4096 字节。 现在在 Linux/Intel 上限制为 131056 字节 ((32*4096)-16),在 Alpha 等具有 8Kb 页面的平台上限制为两倍。 Bucket,过去都是 2 的精确幂,现在是 2 的幂减去一些小数,但小于或等于 128 的数字除外。 有关更多详细信息,请参阅 mm/kmalloc.c 中的实现。
kmalloc()irqaction()
没有设备驱动程序会调用此代码:它仅通过缓冲区缓存调用。 但是,理解此函数可能有助于您理解策略例程的功能。
在健全性检查之后,如果设备的请求队列上没有挂起的请求,ll_rw_block()“插入”队列,以便请求在所有请求都进入队列之前不会发出,并按电梯算法排序。make_request()然后为每个请求调用。 如果队列必须插入,则该设备的策略例程不活动,并且在禁用中断的情况下调用它。 策略例程负责重新启用中断。
定义在: devices/block/ll_rw_block.c
另请参阅 make_request(), add_request().
这采用 16 位设备号,并通过移出次设备号来给出关联的主设备号。
另请参阅 MINOR().
这是 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().
这采用 16 位设备号,并通过屏蔽主设备号来给出关联的次设备号。
另请参阅 MAJOR().
在用户空间和内核空间之间复制大于一个字节、字或长字的内存块。 非常小心地使参数的顺序正确!
注意: 如果访问的内存已被换出,这些函数可能会导致隐式 I/O,因此此时可能会发生抢占。 即使临界区受到cli()/sti()cli()/sti()cli()对的保护,也不要将这些函数包含在代码的临界区中,因为隐式 I/O 会破坏
这些函数接受三个参数
定义在: include/asm/segment.h
另请参阅 get_user(), put_user(), cli(),
sti().
向端口写入一个字节。outb()尽可能快地运行,而outb_p()在返回之前暂停。 如果您不尽可能快地向某些设备写入数据,它们会更愉快。 这两个函数都接受两个参数
定义在: include/asm/io.h
另请参阅 inb(), inb_p().
printk()是内核的printf()版本,但有一些限制。 它不能处理浮点数,并且还有一些其他限制,这些限制记录在 kernel/vsprintf.c 中。 它接受可变数量的参数
注意:printk()可能会导致隐式 I/O,如果访问的内存已被换出,因此此时可能会发生抢占。 此外,printk()将设置中断使能标志,所以 永远不要在受 cli() 保护的代码中使用它。 因为它会导致 I/O,所以即使它没有设置中断使能标志,在受保护的代码中使用它也是不安全的。
定义在: kernel/printk.c。
允许驱动程序在用户空间中写入数据,用户空间与内核位于不同的段中。 自动派生参数的类型和存储大小。 这意味着您必须正确使用类型。 草率的类型定义将根本无法工作。
注意: 如果访问的内存已被换出,这些函数可能会导致隐式 I/O,因此此时可能会发生抢占。 即使临界区受到cli()/sti()cli()/sti()cli()/sti()对的保护,也不要将这些函数包含在代码的临界区中,因为隐式 I/O 会破坏您的
这些函数接受两个参数
定义在: asm/segment.h
另请参阅 memcpy_*fs(), get_user(), cli(),
sti().
向内核注册设备,让内核检查以确保没有其他驱动程序已抓取相同的主设备号。 接受三个参数
定义在: fs/devices.c
另请参阅 unregister_*dev()
向内核请求 IRQ,并在成功时安装 IRQ 中断处理程序。 接受四个参数
定义在: kernel/irq.c
另请参阅 free_irq(), irqaction().
将进程添加到正确的select_wait队列。 此函数接受两个参数
定义在: linux/sched.h
另请参阅 *sleep_on(), wake_up*()
在事件上休眠,在列表中放置一个wait_queue条目,以便可以在该事件上唤醒进程。sleep_on()进入不可中断的休眠状态:进程可以运行的唯一方法是被wake_up(). interruptible_sleep_on()进入可中断的休眠状态,可以通过信号唤醒,进程超时将导致进程唤醒。 调用wake_up_interruptible()是唤醒进程并使其在离开的地方继续运行所必需的。 两者都接受一个参数
定义在: kernel/sched.c
另请参阅 select_wait(), wake_up*().
允许中断被确认。sti代表 “SeT Interrupt enable”。
定义在: asm/system.h
另请参阅 cli().
这些系统调用可用于获取下表中所述的信息,或者可以直接从进程表中提取信息,如下所示
foo = current->pid;
pid | 进程 ID |
uid | 用户 ID |
gid | 组 ID |
euid | 有效用户 ID |
egid | 有效组 ID |
ppid | 进程父进程的进程 ID |
pgid | 进程父进程的组 ID |
不应使用系统调用,因为它们速度较慢并且占用更多空间。 因此,它们不再作为符号导出到整个内核。
定义在: kernel/sched.c
删除设备在内核中的注册,让内核将主设备号分配给其他设备。 接受两个参数
定义在: fs/devices.c
另请参阅 register_*dev()
唤醒已被匹配的*sleep_on()函数。wake_up()可用于唤醒队列中可能处于TASK_INTERRUPTIBLE或TASK_UNINTERRUPTIBLE状态的任务,而wake_up_interruptible()只会唤醒处于TASK_INTERRUPTIBLE状态的任务,并且在仅具有可中断任务的队列中,速度将比wake_up()快得微不足道。 这些函数接受一个参数
注意,wake_up()不会切换任务,它只会使唤醒的进程可运行,以便下次调用schedule()时,它们将成为运行的候选者。
定义在: kernel/sched.c
另请参阅 select_wait(), *sleep_on()
Copyright (C) 1992, 1993, 1994, 1996 Michael K. Johnson, johnsonm@redhat.com.