Posts p1系统调用,基于linux0.11
Post
Cancel

p1系统调用,基于linux0.11

系统调用源码剖析

1. lib目录下的系统调用api

  • close()
1
2
3
4
#define __LIBRARY__
#include <unistd.h>

_syscall1(int,close,int,fd)
  • write()
1
2
3
4
#define __LIBRARY__
#include <unistd.h>

_syscall3(int,write,int,fd,const char *,buf,off_t,count)

2. _syscall 详解

_syscall 是一个宏,在include/unistd.h中定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#define _syscall0(type,name) \


#define _syscall1(type,name,atype,a) \


#define _syscall2(type,name,atype,a,btype,b) \



/*
	每个系统调用 2 + 2*n个参数
	type,  				name,			atype,a,btype,b,ctype,c
	1.系统调用返回值类型   2.系统调用的名称  3. 调用参数类型 名称
*/
#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; \
}

3. 以close展开为例 说明_syscall1(int close int fd)的宏展开

这里展开的内嵌汇编代码,其实就是, 把系统调用号 参数等 分别赋值给不同的寄存器

1
2
3
4
5
6
7
8
9
10
11
12
int close(int fd)   // 返回值为int   参数类型为int
{
    long __res;    // 定义返回值 为一个长整数
    __asm__ volatile ("int $0x80"    // 调用0x80中断
        : "=a" (__res)    // 输出  ex的值赋值给res
        : "0" (__NR_close),     // 输入 赋值 系统调用号赋值给eax
        "b" ((long)(fd)));  // 输入 将_NR_name 赋值给ebx
    if (__res >= 0)
        return (int) __res;  //返回
    errno = -__res;
    return -1;
}

4. 其中 __NR_close 就是系统调用的编号,在 include/unistd.h 中定义:

1
2
3
4
5
6
7
8
9
#ifdef __LIBRARY__

#define __NR_setup	0	/* used only by init, to get system going */
#define __NR_exit	1
#define __NR_fork	2
#define __NR_read	3
#define __NR_write	4
#define __NR_open	5
#define __NR_close	6

5. int 0x80详解 进入内核

  1. 在内核初始化时,主函数在 init/main.c 中,调用了 sched_init() 初始化函数:
1
2
3
4
5
6
7
8
void main(void)
{
//    ……
    time_init();
    sched_init();
    buffer_init(buffer_memory_end);
//    ……
}
  1. sched_init() 在 kernel/sched.c 中定义为:
1
2
3
4
5
void sched_init(void)
{
//    ……
    set_system_gate(0x80,&system_call);  // 将 system_call 函数地址写到 0x80 对应的中断描述符中
}
  1. set_system_gate 是个宏,在 include/asm/system.h 中定义为:
1
2
#define set_system_gate(n,addr) \
    _set_gate(&idt[n],15,3,addr)   // idt 表   addr为地址   3为dpl
  1. _set_gate

虽然看起来挺麻烦,但实际上很简单,就是填写 IDT(中断描述符表),将 system_call 函数地址写到 0x80 对应的中断描述符中,也就是在中断 0x80 发生后,自动调用函数 system_call。

1
2
3
4
5
6
7
8
9
10
#define _set_gate(gate_addr,type,dpl,addr) \
__asm__ ("movw %%dx,%%ax\n\t" \
    "movw %0,%%dx\n\t" \
    "movl %%eax,%1\n\t" \
    "movl %%edx,%2" \
    : \
    : "i" ((short) (0x8000+(dpl<<13)+(type<<8))), \
    "o" (*((char *) (gate_addr))), \
    "o" (*(4+(char *) (gate_addr))), \
    "d" ((char *) (addr)),"a" (0x00080000))
  1. system_call
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
!……
! # 这是系统调用总数。如果增删了系统调用,必须做相应修改
nr_system_calls = 72
!……

.globl system_call
.align 2
system_call:

! # 检查系统调用编号是否在合法范围内
    cmpl \$nr_system_calls-1,%eax
    ja bad_sys_call
    push %ds
    push %es
    push %fs
    pushl %edx
    pushl %ecx

! # push %ebx,%ecx,%edx,是传递给系统调用的参数
    pushl %ebx

! # ds, es指向GDT,内核地址空间
    movl $0x10,%edx
    mov %dx,%ds
    mov %dx,%es
    movl $0x17,%edx
! # fs指向LDT,用户地址空间
    mov %dx,%fs
    call sys_call_table(,%eax,4)      // 根据eax跳转执行 系统调用号   每个函数4个字节
    pushl %eax
    movl current,%eax
    cmpl $0,state(%eax)
    jne reschedule
    cmpl $0,counter(%eax)
    je reschedule

系统调用 以close 为例

1. linux/lib/close.c API

1
2
3
4
#define __LIBRARY__
#include <unistd.h>

_syscall1(int,close,int,fd)

2. _syscall 函数

_syscall 是一个宏,在include/unistd.h中定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#define _syscall0(type,name) \


#define _syscall1(type,name,atype,a) \


#define _syscall2(type,name,atype,a,btype,b) \



/*
	每个系统调用 2 + 2*n个参数
	type,  				name,			atype,a,btype,b,ctype,c
	1.系统调用返回值类型   2.系统调用的名称  3. 调用参数类型 名称
*/
#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; \
}

3. 以close展开为例 说明_syscall1(int close int fd)的宏展开

这里展开的内嵌汇编代码,其实就是, 把系统调用号 参数等 分别赋值给不同的寄存器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#define __NR_close	6

int close(int fd)   // 返回值为int   参数类型为int
{
    long __res;    // 定义返回值 为一个长整数
    __asm__ volatile ("int $0x80"    // 调用0x80中断
        : "=a" (__res)    // 输出  ex的值赋值给res
        : "0" (__NR_close),     // 输入 赋值 系统调用号赋值给eax
        "b" ((long)(fd)));  // 输入 将_NR_name 赋值给ebx
    if (__res >= 0)
        return (int) __res;  //返回
    errno = -__res;
    return -1;
}

4. 初始化sys_call_table 表 inx 0x80中断初始化问题等。

略 同上

sys_call_table表见 include/linux/sys.h

5. sys_call_table 表中函数的实现 以sys_close为例

linux-0.11/fs/open.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int sys_close(unsigned int fd)
{	
	struct file * filp;

	if (fd >= NR_OPEN)
		return -EINVAL;
	current->close_on_exec &= ~(1<<fd);
	if (!(filp = current->filp[fd]))
		return -EINVAL;
	current->filp[fd] = NULL;
	if (filp->f_count == 0)
		panic("Close: file count is 0");
	if (--filp->f_count)
		return (0);
	iput(filp->f_inode);
	return (0);
}

5. 在liunx-0.11/kernel/who.c 中编写自己的系统调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <asm/segment.h>
#include <errno.h>
#include <string.h>

char _myname[24];

int sys_iam(const char *name)
{
    char str[25];
    int i = 0;

    do
    {
        // get char from user input
        str[i] = get_fs_byte(name + i);
    } while (i <= 25 && str[i++] != '\0');

    if (i > 24)
    {
        errno = EINVAL;
        i = -1;
    }
    else
    {
        // copy from user mode to kernel mode
        strcpy(_myname, str);
    }

    return i;
}

int sys_whoami(char *name, unsigned int size)
{
    int length = strlen(_myname);
    printk("%s\n", _myname);

    if (size < length)
    {
        errno = EINVAL;
        length = -1;
    }
    else
    {
        int i = 0;
        for (i = 0; i < length; i++)
        {
            // copy from kernel mode to user mode
            put_fs_byte(_myname[i], name + i);
        }
    }
    return length;
}

6. 测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* iam.c */
#define __LIBRARY__
#include <unistd.h> 
#include <errno.h>
#include <asm/segment.h> 
#include <linux/kernel.h>
_syscall1(int, iam, const char*, name);
   
int main(int argc, char *argv[])
{
    /*调用系统调用iam()*/
    iam(argv[1]);
    return 0;
}

This post is licensed under CC BY 4.0 by the author.

Contents

Trending Tags