GO语言的系统调用

GO语言的系统调用,第1张

内核

内核态:
 通常一个内核由负责响应中断的中断服务程序,负责管理多个进程从而分享处理器时间的调度程序,负责管理进程地址空间的内存管理程序和网络,进程间通信等系统服务程序共同组成。其独立于普通应用程序,一般处于系统态,拥有受保护的内存空间和访问硬件设备的所有权限,这种系统态和被保护起来的内存空间统称为内核空间。

用户态:
 应用程序在用户空间执行,它们只能看到允许它们使用的部分系统资源,并且只能使用某些特定的系统功能,不能直接访问硬件,也不能访问内核划给别人的内存范围。
 当内核运行时,系统以内核态进入内核空间执行,而执行一个普通用户程序时,系统将以用户态进入用户空间执行。

从用户态切换到内核态的三种方式:
(1).系统调用:

     首先我们都知道应用程序通过系统调用界面陷入内核,这是应用程序完成其工作的基本行为方式;
(2).中断进入内核:

     当外围设备完成用户的请求 *** 作后,会像CPU发出中断信号,此时,CPU就会暂停执行下一条即将要执行的指令,转而去执行中断信号对应的处理程序,如果先前执行的指令是在用户态下,则自然就发生从用户态到内核态的转换。
(3).异常进入内核:

     当CPU正在执行运行在用户态的程序时,突然发生某些预先不可知的异常事件,这个时候就会触发从当前用户态执行的进程转向内核态执行相关的异常事件,典型的如缺页异常,异常与中断不同,它在产生时必须考虑与处理器始时钟同步,所以异常又称之为同步中断。
 

注意:系统调用的本质其实也是中断,相对于外围设备的硬中断,这种中断称为软中断,这是 *** 作系统为用户特别开放的一种中断,如Linux int 80h中断。
所以,从触发方式和效果上来看,这三种切换方式是完全一样的,都相当于是执行了一个中断响应的过程。但是从触发的对象来看,系统调用是进程主动请求切换的,而异常和硬中断则是被动的。

进程上下文:它是内核所处的一种 *** 作模式,此时内核代表进程在执行,比如执行系统调用或者内核线程,进程上下文可以睡眠。

中断上下文

中断处理程序是被内核调用来响应中断的,而它们运行于称之为中断上下文的特殊上下文中,由于中断上下文不可以睡眠,所以又称之为原子上下文,它有严格的时间限制,所以分为上半部和下半部,由于它极有可能是打断了其他中断线上的另一个中断处理程序,所以尽量把工作从中断处理程序中分离出来,放在下半部执行,因为下半部可以在更合适的时间运行;

上半部

那些工作应该放在上半部,那些应该放在下半部? 没有严格的规则,只有一些提示:

1、对时间非常敏感,放在上半部。

2、与硬件相关的,放在上半部。

3、不能被其他中断打断的工作,放在上半部。

以上三点之外的,考虑放在下半部。

下半部

下半部在Linux中有以下实现机制:

1、BH(在2.5中删除)

2、任务队列(task queue,在2.5删除)

3、软中断(softirq,2.3开始。本文重点)

4、tasklet(2.3开始)

5、工作队列(work queue,2.5开始)

软中断是怎么实现的?

软中断不会抢占另外一个软中断,唯一可以抢占软中断的是中断处理程序。 软中断可以在不同CPU上并发执行(哪怕是同一个软中断)

1、软中断是编译期间静态分配的

系统执行软中断,一个注册的软中断必须被标记后才会执行(触发软中断),通常中断处理程序会在返回前标记它的软中断。

在下列地方,待处理的软中断会被执行:

(1)、从一个硬件中断代码处返回。

(2)、在ksoftirqd内核线程。

(3)、在那些显示检查和执行待处理的软中断代码中。

软中断处理程序注意:

(1)、软中断处理程序执行的时候,允许响应中断,但自己不能休眠。

(2)、如果软中断在执行的时候再次触发,则别的处理器可以同时执行,所以加锁很关键。

内核线程

内核线程是直接由内核本身启动的进程。内核线程实际上是将内核函数委托给独立的进程,它与内核中的其他进程”并行”执行。内核线程经常被称之为内核守护进程。

ksoftirqd

每个处理器都有一个这样的线程。所有线程的名字都叫做ksoftirq/n,区别在于n,它对应的是处理器的编号。在一个双CPU的机器上就有两个这样的线程,分别叫做ksoftirqd/0和ksoftirqd/1。

为了保证只要有空闲的处理器,它们就会处理软中断,所以给每个处理器都分配一个这样的线程。 

Go 语言库对Syscall的封装

我们知道Go是一门面向系统级开发的Native编程语言,与C/C++ 类似,Go的编译器会直接将程序编译、链接成本地可执行文件。

理论上,它可以完成任何C/C++语言能完成的。作为支撑该特性的重要方面,Go以标准库形式提供了syscall包,用来支持OS级系统调用。

首先,Go对各种系统调用接口进行了封装,提供给用户一组Go语言函数,方便在程序中直接调用,如:

func Read(fd int, p []byte) (n int, err error)
func Write(fd int, p []byte) (n int, err error)

同时,Go还通过以下函数提供了对Syscall的直接调用支持:

func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)

func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)

其中,带有Raw前缀的一组 *** 作表示直接调用syscall (注:以Linux为例,在AMD64中是通过syscall指令实现,在X86中是int 0x80软中断,而ARM中则是采用SWI软中断实现系统调用),而不带Raw前缀的 *** 作则在真正调用syscall前会先调用runtime·entersyscall,并在syscall返回后插入runtime·exitsyscall

这4个函数全都是用汇编语言实现的,并且和具体的硬件架构及OS相关,比如Linux下ARM架构的相应实现,在 src/pkg/syscall/asm_linux_arm.s中。至于其他的如Read/Write这类的函数,其内部基本上都调用上面的4个函数实现的。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/995984.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存