操作系统课程实验-linux-0-11系统调用

实验二 系统调用

一、一个系统调用的过程

wirite函数的执行过程

1. write声明及定义

/* include/unistd.h */

// 298 write系统调用的声明
int write(int fildes, const char * buf, off_t count);
/* linux/lib/write.c */

// write系统调用的定义
_syscall3(int,write,int,fd,const char *,buf,off_t,count)

此处的_syscall3为宏定义,在include/unistd.h文件中

/* include/unistd.h */

#define _syscall3(type,name,atype,a,btype,b,ctype,c) \
type name(atype a,btype b,ctype c) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
: "=a" (__res) \
: "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b)),"d" ((long)(c))); \
if (__res>=0) \
return (type) __res; \
errno=-__res; \
return -1; \
}

实际上write函数的定义为:

int write(int fd, const char *buf, off_t count)
{
long __res;
// 查询IDT表,执行相应函数 linux/include/asm/system.h
__asm__ volatile ("int $0x80" \
// 返回值赋给 __res
: "=a" (__res) \
// __NR_write->eax fd->ebx buf->ecx count->edx
: "0" (__NR_write),"b" ((long)(fd)),"c" ((long)(buf)),"d" ((long)(count))); \
if (__res>=0)
return (int) __res;
errno=-__res;
return -1;
}

这里的int 0x80为关键。Linux提供了主动进入内核的方法,使用中断。对于Linux内核来说,中断信号通常分为两类:硬件中断和软件中断(异常)。每个中断是由0-255之间的一个数字来标识,称为中断号。

  • INT0-INT31(0x00-0x1F)Intel公司固定设定或保留用
  • INT32-INT47(0x20-0x2F) 对应于8259A中断芯片的IRQ0-IRQ15
  • INT128(0x80) 用户程序发出的软件中断,称为系统调用

2. 系统调用函数(int 0x80中断处理函数)

当执行main函数时,会初始化好int 0x80的中断处理函数

执行过程

main()->sched_init()->set_system_gate(0x80,&system_call)

# kernel/system_call.s

# 80 行处
system_call:
cmpl $nr_system_calls-1,%eax
ja bad_sys_call
# _syscall3 设置的参数
push %ds
push %es
push %fs
pushl %edx
pushl %ecx # push %ebx,%ecx,%edx as parameters
pushl %ebx # to the system call
movl $0x10,%edx # set up ds,es to kernel space
mov %dx,%ds
mov %dx,%es
movl $0x17,%edx # fs points to local data space
mov %dx,%fs
call sys_call_table(,%eax,4)

这里的system_call便是int 0x80的中断处理函数,调用set_system_gate会设置IDT(中断描述符表)

最终会执行call sys_call_table(,%eax,4)

3. 实际执行函数

sys_call_table为一个函数数组,存放着实际执行函数的地址(include/linux/sys.h),使用系统调用号作为下标(__NR_write = 4)调用函数。

system_call会提前把参数(ebx,ecx,edx)push到堆栈中,当调用时pop出来的就是当初设置的参数。

/* linux/fs/read_write.c */
int sys_write(unsigned int fd,char * buf,int count)
{
struct file * file;
struct m_inode * inode;

if (fd>=NR_OPEN || count <0 || !(file=current->filp[fd]))
return -EINVAL;
if (!count)
return 0;
inode=file->f_inode;
if (inode->i_pipe)
return (file->f_mode&2)?write_pipe(inode,buf,count):-EIO;
if (S_ISCHR(inode->i_mode))
return rw_char(WRITE,inode->i_zone[0],buf,count,&file->f_pos);
if (S_ISBLK(inode->i_mode))
return block_write(inode->i_zone[0],&file->f_pos,buf,count);
if (S_ISREG(inode->i_mode))
return file_write(inode,file,buf,count);
printk("(Write)inode->i_mode=%06o\n\r",inode->i_mode);
return -EINVAL;
}

上述才是我们调用write函数时,实际执行的函数。

二、自定义系统调用(实验部分)

添加iam()whoami()保存到内核

1. iam()

步骤

  1. 修改include/linux/sys.h 参考write函数,先向函数数组后中添加sys_iam,以及extern int sys_iam()
  2. 修改kernel/system_call.snr_system_calls1
  3. 修改include/unistd.h 添加功能号__NR_iam = 72,作为下标访问函数数组
  4. 使用iam() 需要定义函数,类似write_syscall1(int, iam, const char*, name)

2. whoami()

iam相同

3. 代码