Linux系统调用浅析

时间:2021-12-28 15:49:38

1、linux系统调用概述

A、定义

linux内核提供一系列具备预定功能的内核函数,通过一组接口呈现给用户,这些由Linux系统本身提供的接口,就是系统调用(System Call)。

B、抽象

操作系统是从硬件抽象出来的虚拟机,在该虚拟机上用户可以运行应用程序。它负责直接与硬件交互,向用户程序提供公共服务,并使它们同硬件特性隔离。因为程序不应该依赖于下层的硬件,只有这样应用程序才能很方便的在各种不同的linux系统之间移植。
系统调用就是Linux操作系统向用户程序提供支持的接口,通过这些接口应用程序向操作系统请求服务,控制转向操作系统,而操作系统在完成服务后,将控制和结果返回给用户程序。
换而言之,操作系统的主要功能是为应用程序的运行创建良好的环境,为了达到这个目的,用户可以通过进程控制相关的系统调用来创建进程、实现进程调度、进程管理等。

C、为什么需要系统调用

那为什么用户程序不能直接访问系统内核提供的服务呢?
一种计算机硬件要运行Linux系统,至少需要提供两种运行模式:高优先级的核心模式和低优先级的用户模式。linux内核运行在高优先级,称之为核心态;其它外围应用软件运行在低优先级,称之为用户态。将程序的运行空间分为内核空间和用户空间(也就是内核态和用户态),它们分别运行在不同的级别上,在逻辑上是相互隔离的。因此,用户进程在通常情况下不允许访问内核数据,也无法使用内核函数,它们只能在用户空间操作用户数据,调用用户空间的函数。由于用户进程在较低的特权级上运行,它们将不能意外或故意的破坏其它进程或内核。程序造成的破坏会被局部化而不影响系统中其它活动或者进程。当用户进程需要完成特权模式下才能完成的某些功能时,必须严格按照系统调用提供接口才能进入特权模式,然后执行调用所提供的有限功能。

D、Linux内核功能的划分

Linux系统调用浅析

Linux系统调用部分是非常精简的系统调用,它继承了UNIX 系统调用中最基本和最有用的部分。这些系统调用按照功能逻辑大致可分为进程控制、进程间通信、文件系统控制、系统控制、存储管理、网络管理、socket控制、用户管理等几类。
随着内核版本的变化,系统调用的数量也在增加,个别的系统调用接口声明也发生了变化。

2、系统调用过程

Linux系统调用浅析

以x86平台已有系统调用为例来演示
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(){
    long ID1, ID2, ID3;
    ID1 = getpid(); /*系统调用getpid()*/
    /*C内嵌汇编模拟库函数实现,触发软中断并传入系统调用编号及参数*/
    asm volatile("int $0x80\n"    : "=a" (ID2)    /* eax亦是返回值*/
        : "0" (20));                /* 20 置入 eax */
    /*库函数封装好的上述汇编代码,syscall()为不定参数:传入系统调用编号及参数*/
    ID3 = syscall(20);   /*使用系统函数syscall()*/
    /*三张方式的动作都是一样的:得到当前进程*/
    printf(" PID=%ld, %ld, %ld\n", ID1, ID2, ID3);
    return 0;
}

3、添加新的系统调用

(1)、修改系统调用表
(2)、添加系统调用对应的内核函数声明
(3)、添加系统调用对应的内核函数定义

这里以ARM Linux-2.6.35为例来演示如何向内核中添加系统调用
(1)、修改系统调用表
    在/arch/arm/kernel/calls.S声明了系统调用函数
    /* 366 */    CALL(sys_mysyscall)
(2)、添加系统调用对应的内核函数声明
    /include/linux/syscalls.h
    asmlinkage long sys_mysyscall(char *str, const size_t n);
(3)、添加系统调用对应的内核函数定义
    找一个.c文件来添加内核函数的定义,或是自己编写一个.c文件
    这里在/fs/open.c中添加测试用的内核函数定义
    SYSCALL_DEFINE2(mysyscall, char __user *, dst, size_t, n){
        const char str[]="Hello SystemCall";
        if(n < sizeof(str))
            return -ENOMEM;
        copy_to_user(dst,str, sizeof(str));
        return 0;
    }

编写一个应用程序来测试
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#define __NR_mysyscall (__NR_SYSCALL_BASE+366)
inline int mysyscall(char *str, const size_t n)
{    return syscall (__NR_mysyscall, str, n); }
int main(int nr, char **arg)
{
    char buf[1024]="";
    mysyscall(buf, 1024);
    fprintf(stdout, "Test: %s\n", buf);
    exit(0);
}