linux学习16,很难吗?3分钟学会自己定义系统调用,不过要慎用

linux学习16,很难吗?3分钟学会自己定义系统调用,不过要慎用包括linux,大多现代操作系统都提供了用户进程和内核交互的接口。通过这些接口,用户进程能够在内核的监督下访问硬件设备,创建新进程或者与其他进程

欢迎大家来到IT世界,在知识的湖畔探索吧!

包括 linux,大多现代操作系统都提供了用户进程和内核交互的接口。通过这些接口,用户进程能够在内核的监督下访问硬件设备,创建新进程或者与其他进程通信。可以说,这些接口充当了用户进程和内核的中转站。

在内核的监督下,可以避免用户进程的肆意妄为,做出一些损害系统的事情。

系统调用

在 linux 中,除了异常和陷入,系统调用是用户空间访问内核空间的唯一合法手段。

系统调用介于用户空间和内核空间之间,充当信息转达中间人的角色。有了系统调用,内核可以基于用户所在组等权限信息决定是否响应用户空间的操作,从而保证系统的安全性。

linux学习16,很难吗?3分钟学会自己定义系统调用,不过要慎用

此外,系统调用层可以为硬件虚拟出一套通用的接口供用户空间的进程使用。例如,用户空间进程采取系统调用读写文件,就无需关心磁盘的类型、介质、甚至文件系统了,系统调用会根据不同的情况自适应的完成需求。

而且,如果用户空间进程不通过系统调用直接访问硬件,linux 内核就无法得知这些信息,也就无法实现多任务的协调安排,整个系统的稳定性就无从谈起。

linux学习16,很难吗?3分钟学会自己定义系统调用,不过要慎用

一般来说,用户空间进程并不直接使用系统调用,而是通过同样在用户空间实现的 API(Application Program Interface,应用程序接口)使用。定义一个 API 可以使用一个系统调用,也可以使用多个系统调用,不使用任何系统调用也是可以的。对于C语言程序开发来说,这些 API 主要由 C 库提供。

这样的设计其实很清晰,程序员并不需要关心系统调用,只需要使用好 API 就可以了。而 linux 内核则不需关心 API,只需要处理好系统调用即可。

系统调用处理程序

之前我们讨论过,进程运行在虚拟空间里,linux 内核空间则驻留在受保护的地址空间里,所以用户空间进程无法直接调用内核空间里的函数。

如果用户空间进程需要执行一个系统调用,只能以某种方式向 linux 内核申请,之后内核才有可能代表用户进程在内核空间执行系统调用。

例如,用户空间进程可以利用软中断机制实现,通过引发一个异常来促使系统切换到内核态执行异常处理程序,这里的“异常处理程序”其实就是系统调用处理程序。

linux学习16,很难吗?3分钟学会自己定义系统调用,不过要慎用

linux 内核已支持的系统调用都有一个唯一的系统调用号,例如:

 8 #define __NR_restart_syscall 0
 9 #define __NR_exit 1
 10 #define __NR_fork 2
 11 #define __NR_read 3
 12 #define __NR_write 4
 13 #define __NR_open 5
 14 #define __NR_close 6
 15 #define __NR_waitpid 7
 16 #define __NR_creat 8
 17 #define __NR_link 9
 18 #define __NR_unlink 10
 19 #define __NR_execve 11
 20 #define __NR_chdir 12
 21 #define __NR_time 13
 22 #define __NR_mknod 14
 23 #define __NR_chmod 15
 24 #define __NR_lchown 16
 25 #define __NR_break 17
 26 #define __NR_oldstat 18
 27 #define __NR_lseek 19
...

欢迎大家来到IT世界,在知识的湖畔探索吧!

linux学习16,很难吗?3分钟学会自己定义系统调用,不过要慎用

用户空间进程申请系统调用时,可以把系统调用号和参数通过寄存器传递给内核,这样内核就能知道该执行哪一个系统调用。程序员也可以直接调用 syscall() 函数申请系统调用,该函数的使用手册可以通过 man 命令查询:

linux学习16,很难吗?3分钟学会自己定义系统调用,不过要慎用

定义自己的系统调用

重新定义一个系统调用并不难,以 my_fun() 作为新定义的系统调用函数名为例。首先,将其加入系统调用表:

欢迎大家来到IT世界,在知识的湖畔探索吧! 18 ENTRY(sys_call_table)
 19 .long sys_restart_syscall /* 0 - old "setup()" system call*/
 20 .long sys_exit
 21 .long sys_fork
 22 .long sys_read
 23 .long sys_write
 24 .long sys_open /* 5 */
...
 343 .long sys_fallocate
 344 .long sys_timerfd_settime /* 325 */
 345 .long sys_timerfd_gettime
 346 .long sys_my_fun
linux学习16,很难吗?3分钟学会自己定义系统调用,不过要慎用

注意,为了不影响原系统调用,应将其加到表的最后。另外,虽然没有明确指明系统调用号,但是系统仍然可以根据表的顺序自动计算出 my_fun() 的系统调用号。

接着,将系统调用号加入到 asm/unistd_32.h 里:

 8 #define __NR_restart_syscall 0
 9 #define __NR_exit 1
 10 #define __NR_fork 2
 11 #define __NR_read 3
 12 #define __NR_write 4
 13 #define __NR_open 5
...
 329 #define __NR_signalfd 321
 330 #define __NR_timerfd_create 322
 331 #define __NR_eventfd 323
 332 #define __NR_fallocate 324
 333 #define __NR_timerfd_settime 325
 334 #define __NR_timerfd_gettime 326
 335 #define __NR_my_fun 327
 ...
linux学习16,很难吗?3分钟学会自己定义系统调用,不过要慎用

准备工作做好以后,就可以写实现 my_fun() 的C语言代码了:

欢迎大家来到IT世界,在知识的湖畔探索吧!#include <asm/page.h>
asmlinkage long sys_my_fun(void)
{
 // do something
 return 0;
}

写好 my_fun() 的C语言代码后,可以将其放在功能相关的文件里。至此,我们就实现了一个新的系统调用。需要说明的是,若想使用 my_fun() 系统调用,必须先将其编译进内核映像,不能编译成模块。

慎用系统调用

容易看出,定义一个新的系统调用非常简单,而且 linux 系统调用的性能也非常高效,但是仍然应尽量使用其他方法代替新建一个系统调用。

linux学习16,很难吗?3分钟学会自己定义系统调用,不过要慎用

新建系统调用需要系统调用号,虽然可以将其放在系统调用表最后,但是不能保证不会与以后的 linux 官方版本新定义的系统调用冲突。

而且,系统调用一旦加入内核,就被固化了,为了保持兼容性,可能之后很多年都不允许修改。此外,因为系统调用不能从文件系统直接访问,所以一些脚本也就不容易调用该功能了。

欢迎在评论区一起讨论,质疑。文章都是手打原创(本文部分参考linux内核原理和设计),每天最浅显的介绍C语言、linux等嵌入式开发,喜欢我的文章就关注一波吧,可以看到最新更新和之前的文章哦。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/21776.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们YX

mu99908888

在线咨询: 微信交谈

邮件:itzsgw@126.com

工作时间:时刻准备着!

关注微信