系统调用(syscalls)
系统调用是用户空间访问内核的唯一手段。
e.g:
对getpid()的系统调用:
asmlinkage long sys_getpid(void)
{
return current->tgid;
}
说明:
asmlinkage通知编译器仅从栈中提取该函数;
调用getpid()在内核中被定义成sys_getpid()。
unix系统调用在出现错误时,把错误码写入errno全局变量,通过调用perror()库函数显示。
e.g:
copy_from_user() 和copy_to_user()
copy_from_user():为了从用户空间读取数据;
copy_to_user():为了向用户空间写入数据;
成功返回0,失败系统调用返回标准-EFAULT。
/*
* silly_copy - utterly worthless syscall that copies the len bytes from
* 'src' to 'dst' using the kernel as an intermediary in the copy for no
* good reason. But it makes for a good example!
*/
asmlinkage long sys_silly_copy(unsigned long *src,
unsigned long *dst,
unsigned long len)
{
unsigned long buf;
/* fail if the kernel wordsize and user wordsize do not match */
if (len != sizeof(buf))
return -EINVAL;
/* copy src, which is in the user's address space, into buf */
if (copy_from_user(&buf, src, len))
return -EFAULT;
/* copy buf into dst, which is in the user's address space */
if (copy_to_user(dst, &buf, len))
return -EFAULT;
/* return amount of data copied */
return len;
}
从用户空间访问系统调用:
宏_syscalln()的应用(n的范围从0到6,代表需要传递给系统调用的参数个数)
e.g:open()系统调用:
long open(const char *filename, int flags, int mode)
不靠库支持,直接调用此系统调用的宏的形式为:
#define __NR_open 5
_syscall3(long, open, const char *, filename, int, flags, int, mode)
执行系统调用的连锁反应:陷入内核,传递系统调用号和参数,执行正确的系统调用函数,并把返回值带回用户空间。
中断处理:
注册中断处理程序:
在<linux/interrupt.h>中,实现中断注册接口:
/* request_irq: allocate a given interrupt line */
int request_irq(unsigned int irq,
irqreturn_t (*handler)(int, void *, struct pt_regs *),
unsigned long irqflags,
const char *devname,
void *dev_id)
说明:
unsigned int irq:所要注册的中断号;
irqreturn_t (*handler)(int, void *, struct pt_regs *):中断服务程序的入口地址;
unsigned long irqflags:与中断管理有关的位掩码选项,可以为0,也可能是下列一个或多个标志的位掩码:
1. SA_INTERRUPT :快速中断处理程序,当使用它的时候处理器上所有的其他中断都被禁用;
2. SA_SHIRQ :该中断是在设备之间可共享的;
3. SA_SAMPLE_RANDOM :这个位表示产生的中断能够有贡献给 /dev/random和 /dev/urandom 使用的加密池。
const char *dev_name:设备描述,表示哪一个设备在使用这个中断;
void *dev_id:用作共享中断线的指针,一般设置为这个设备的device结构本身或者NULL。
释放中断处理程序:
void free_irq(unsigned int irq,void *dev_id);
编写中断处理程序:
中断处理程序声明:
static irqreturn_t intr_handler(int irq, void *dev_id, struct pt_regs *regs)
说明:
该类型与request_irq()参数中的handler所要求的参数类型相匹配。
int irq :中断号;
void *dev_id :与request_irq()的参数dev_id一致,可以根据这个设备id号得到相应设备的数据结构,进而的到相应设备的信息和相关数据;
struct pt_regs *regs :它指向一个数据结构,此结构保存的是中断之前处理器的寄存器和状态。主要用在程序调试,一般忽略。
返回值:中断程序的返回值是一个特殊类型——irqreturn_t。但是中断程序的返回值却只有两个值IRQ_NONE和IRQ_HANDLED。
IRQ_NONE:中断程序接收到中断信号后发现这并不是注册时指定的中断原发出的中断信号;
IRQ_HANDLED:接收到了准确的中断信号,并且作了相应正确的处理。
亦可以使用宏IRQ_RETVAL(x),若x为非0值,该宏返回IRQ_HANDLED,否则返回IRQ_NONE。
查看中断号:
命令:cat /proc/interrupts
实例:
来自RTC(real_time clock)驱动程序,存在于cat /usr/src/linux/drivers/char/rtc.c,用于设置系统时钟,提供alarm或定时器。
/* register rtc_interrupt on RTC_IRQ */
if (request_irq(RTC_IRQ, rtc_interrupt, SA_INTERRUPT, "rtc", NULL) {
printk(KERN_ERR "rtc: cannot register IRQ %d/n", RTC_IRQ);
return -EIO;
}
static irqreturn_t rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
/*
* Can be an alarm interrupt, update complete interrupt,
* or a periodic interrupt. We store the status in the
* low byte and the number of interrupts received since
* the last read in the remainder of rtc_irq_data.
*/
/*自旋锁保证rtc_irq_data 不被SMP机器上的其他CPU同时访问*/
spin_lock (&rtc_lock);
rtc_irq_data += 0x100;
rtc_irq_data &= ~0xff;
rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0);
/*如果设置了RTC周期性定时器,就要通过mod_timer对其更新*/
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
*/
/*自旋锁保证rtc_callback */
spin_lock(&rtc_task_lock);
if (rtc_callback)
rtc_callback->func(rtc_callback->private_data);
spin_unlock(&rtc_task_lock);
wake_up_interruptible(&rtc_wait);
kill_fasync (&rtc_async_queue, SIGIO, POLL_IN);
return IRQ_HANDLED;
}
说明:
rtc_irq_data 变量是无符号长整数,存放有关RTC的信息,每次中断时都会更新以反映中断的状态。
中断控制:
一些函数:
在<asm/system.h> 、<asm/irq.g>中定义:
Function Description
local_irq_disable() Disable local interrupt delivery
local_irq_enable() Enable local interrupt delivery
local_irq_save() Save the current state of local interrupt delivery and then disable it
local_irq_restore() Restore local interrupt delivery to the given state
disable_irq() Disable the given interrupt line and ensure no handler on the line is executing before returning
disable_irq_nosync() Disable the given interrupt line
enable_irq() Enable the given interrupt line
irqs_disabled() Returns nonzero if local interrupt delivery is disabled; otherwise returns zero
in_interrupt() Returns nonzero if in interrupt context and zero if in process context
in_irq() Returns nonzero if currently executing an interrupt handler and zero otherwise