1、linux系统调用概述
A、定义
linux内核提供一系列具备预定功能的内核函数,通过一组接口呈现给用户,这些由Linux系统本身提供的接口,就是系统调用(System Call)。
B、抽象
操作系统是从硬件抽象出来的虚拟机,在该虚拟机上用户可以运行应用程序。它负责直接与硬件交互,向用户程序提供公共服务,并使它们同硬件特性隔离。因为程序不应该依赖于下层的硬件,只有这样应用程序才能很方便的在各种不同的linux系统之间移植。
系统调用就是Linux操作系统向用户程序提供支持的接口,通过这些接口应用程序向操作系统请求服务,控制转向操作系统,而操作系统在完成服务后,将控制和结果返回给用户程序。
换而言之,操作系统的主要功能是为应用程序的运行创建良好的环境,为了达到这个目的,用户可以通过进程控制相关的系统调用来创建进程、实现进程调度、进程管理等。
C、为什么需要系统调用
那为什么用户程序不能直接访问系统内核提供的服务呢?
一种计算机硬件要运行Linux系统,至少需要提供两种运行模式:高优先级的核心模式和低优先级的用户模式。linux内核运行在高优先级,称之为核心态;其它外围应用软件运行在低优先级,称之为用户态。将程序的运行空间分为内核空间和用户空间(也就是内核态和用户态),它们分别运行在不同的级别上,在逻辑上是相互隔离的。因此,用户进程在通常情况下不允许访问内核数据,也无法使用内核函数,它们只能在用户空间操作用户数据,调用用户空间的函数。由于用户进程在较低的特权级上运行,它们将不能意外或故意的破坏其它进程或内核。程序造成的破坏会被局部化而不影响系统中其它活动或者进程。当用户进程需要完成特权模式下才能完成的某些功能时,必须严格按照系统调用提供接口才能进入特权模式,然后执行调用所提供的有限功能。
D、Linux内核功能的划分
Linux系统调用部分是非常精简的系统调用,它继承了UNIX 系统调用中最基本和最有用的部分。这些系统调用按照功能逻辑大致可分为进程控制、进程间通信、文件系统控制、系统控制、存储管理、网络管理、socket控制、用户管理等几类。
随着内核版本的变化,系统调用的数量也在增加,个别的系统调用接口声明也发生了变化。
2、系统调用过程
以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)、添加系统调用对应的内核函数定义
(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);
}