现代操作系统中,内核提供了用户进程和内核进程交互的一组接口,让app可以受限的访问硬件资源,提供进程间通信机制,实际上主要是为了保证系统稳定可靠,避免应用程序do whatever they want. 1.与内核通信 系统调用在用户空间进程和硬件设备之间添加了一个中间层,主要作用: ①为用户空间提供了一种硬件的抽象接口; ②保证了系统的稳定和安全,可以给予权限,用户对访问进行裁决; ③每个进程都运行在虚拟系统中; 在Linux中,系统调用是用户空间访问内核的唯一手段;除异常和陷入外,是内核唯一的合法入口;实际上像设备文件和/proc之类的方式,也是通过系统调用进行访问的。
2 API、POSIX和C库 应用程序通过在用户空间实现的应用编程接口(API),而不是直接通过系统调用来编程。 因为API实际上不需要和系统调用对应,一个API可以实现成一个系统调用,也可以通过调用多个系统调用来实现,也可以完全不用。POSIX、API、C库及系统调用关系如下 程序员只跟API打交道,内核只跟系统调用打交道;即内核提供机制,API提供策略。 C库实现了大部分的POSIX标准API.
3.系统调用 系统调用一般用返回0来表示成功,返回负数表明错误,错误码写入errno全局变量,用peeror()库函数可以把错误码转变成错误字符串. 举一例,获取进程ID号的系统调用getpid() 点击(此处)折叠或打开
①asmlinkage限定词是编译器指令,告知编译器仅从堆栈中提取函数的参数; ②内核返回long,用户空间返回int,是为了保证32位/64位系统兼容; ③get_pid在内核被定义为sys_getpid(),内核对系统调用都是如此定义的; (1)系统调用号 Linux中,每个系统调用号被赋予一个唯一的系统调用号,进程不会提及系统调用名称,而是用系统调用号来关联具体的系统调用。 一个系统调用号一旦被分配,不能随意变更;用sys_ni_syscall()来补缺已经删除的调用号; 系统调用号保持在unsigned long sys_call_table[NR_syscalls]; (2)系统调用的性能 Linux上下文切换时间很短,进出内核都被优化的简洁高效;系统调用处理程序和每个系统调用本身都非常简洁,所以Linux系统调用比许多其他操作系统都执行的快。 (3)系统调用处理程序 通过软中断引发一个异常,促使系统切换到内核态,执行异常处理程序代码;这个异常处理程序就是系统调用处理程序system_call()。 ①找到指定的系统调用 X86上是通过eax把系统调用号传给内核,system_call()通过查找sys_call_table[]找到对应的系统调用 ②参数传递 Ebx,ecx,edx,esi和edi依次存放前五个参数,若需要六个以上参数,用单独寄存器指向这些参数在用户空间地址的指针。通过eax存放返回值。
4.系统调用的实现 (1)决定用途,每个系统调用功能应该单一明确,不提倡多用途系统调用。系统调用参数,返回值和错误码都要明确,不要对机器字节长度和字节序做假设。 (2)参数验证:内核必须保证 ①指向用户空间内存的指针,内核不能直接访问; ②指针指向的内存在用户进程空间里,内核不能读其他进程空间; ③内存不能绕过访问限制:可读内存标记为可读,可写标记为可写,可执行标记为可执行 内核用copy_to_user()和copy_from_user()来从用户空间读写数据,都是把第二个参数指定位置数据传送到第一个参数指定位置,长度由第三个参数决定。执行失败,返回未传送字节,成功返回0。copy_to_user()和copy_from_user()都可能引起休眠。 ④检查权能,针对合法权限,比如if (!capable(CAP_SYS_BOOT)) return –EPERM; (3)内核执行系统调用时处于进程上下文,current指针指向引发系统调用的那个进程。能够休眠,所以系统调用必须是可重入的。 (4)往系统添加一个系统调用的一个简单实例 ①添加系统调用名字函数名字sys_mytest,一般在calls.S或者entry.S /* 320 */ .long sys_get_mempolicy .long sys_set_mempolicy .long sys_mytest ②在unistd.h添加系统调用号,322 #define __NR_get_mempolicy (__NR_SYSCALL_BASE+320) #define __NR_set_mempolicy (__NR_SYSCALL_BASE+321) #define __NR_mytest (__NR_SYSCALL_BASE+322) ③实现系统调用函数 点击(此处)折叠或打开
④在syscalls.h做系统调用函数声明 点击(此处)折叠或打开
⑤app测试 点击(此处)折叠或打开
①系统调用创建容易,且使用方便; ②Linux系统调用高性能显而易见 缺点是: ①需要一个系统调用号,这个需要官方分配 ②系统调用被加入稳定内核固化后,接口不能改变; ③需要将系统调用分别分配到各种体系结构去(与硬件相关) ④在脚本中不容易调用系统调用,也不能从文件系统直接访问系统调用 ⑤在主内核树之外很难维护 ⑥如果只进行简单信息交换,系统调用大材小用了。所以尽管建立一个系统调用非常容易,但是不建议这么做,替代方法: ①实现一个设备节点,并对此实现read()和write(),ioctl()来进行操作 ②像信号量这样的某些接口,可以用文件描述符来表示 ③把增加的信息作为一个文件放在sysfs的合适位置
Linux尽量使系统调用简洁,事实上Linux已经是一个相对稳定并且功能已经较为完善的操作系统。 |
|
来自: clover_xian > 《我的图书馆》