栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

linux系统调用

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

linux系统调用

前言:本文只讨论linux中的系统调用,不考虑windows等其他操作系统。

两个问题:
1.系统调用时,进程调用的是进程吗?
答:进程调用的是操作系统的内核函数,不是进程。
2.系统调用时,会出现上下文切换吗?
答:会出现上下文切换,但和进程调度时的上下文切换还是有区别的。

Part1:

早期,内存使用的是哈佛结构,即程序和数据分开存储,这种划分是硬件层面的。到了现代,内存普遍采用的是冯诺依曼结构,即程序和数据存储在一起。但是,在linux中,内存又被按逻辑划分为成了两部分:内核部分和进程部分。内核部分归操作系统内核使用,进程部分归进程使用。

Part2:

百度关于“系统调用”的解释:

操作系统的主要功能是为管理硬件资源和为应用程序开发人员提供良好的环境来使应用程序具有更好的兼容性,为了达到这个目的,内核提供一系列具备预定功能的内核函数,通过一组称为系统调用(system call)的接口呈现给用户。系统调用把应用程序的请求传给内核,调用相应的内核函数完成所需的处理,将处理结果返回给应用程序。
现代的操作系统通常都具有多任务处理的功能,通常靠进程来实现。由于操作系统快速的在每个进程间切换执行,所以一切看起来就会像是同时的。同时这也带来了很多安全问题,例如,一个进程可以轻易的修改进程的内存空间中的数据来使另一个进程异常或达到一些目的,因此操作系统必须保证每一个进程都能安全的执行。这一问题的解决方法是在处理器中加入基址寄存器和界限寄存器。这两个寄存器中的内容用硬件限制了对储存器的存取指令所访问的储存器的地址。这样就可以在系统切换进程时写入这两个寄存器的内容到该进程被分配的地址范围,从而避免恶意软件。
为了防止用户程序修改基址寄存器和界限寄存器中的内容来达到访问其他内存空间的目的,这两个寄存器必须通过一些特殊的指令来访问。通常,处理器设有两种模式:“用户模式”与“内核模式”,通过一个标签位来鉴别当前正处于什么模式。一些诸如修改基址寄存器内容的指令只有在内核模式中可以执行,而处于用户模式的时候硬件会直接跳过这个指令并继续执行下一个。
同样,为了安全问题,一些I/O操作的指令都被限制在只有内核模式可以执行,因此操作系统有必要提供接口来为应用程序提供诸如读取磁盘某位置的数据的接口,这些接口就被称为系统调用。
当操作系统接收到系统调用请求后,会让处理器进入内核模式,从而执行诸如I/O操作,修改基址寄存器内容等指令,而当处理完系统调用内容后,操作系统会让处理器返回用户模式,来执行用户代码。
系统调用在本质上是应用程序请求 OS 内核完成某功能时的一种过程调用,但它是一种特殊的过程调用,它与一般的过程调用有下述几方面的明显差别:
(1) 运行在不同的系统状态。一般的过程调用,其调用程序和被调用程序都运行在相同的状态——系统态或用户态;而系统调用与一般调用的最大区别就在于:调用程序是运行在用户态,而被调用程序是运行在系统态。
(2) 状态的转换通过软中断进入。由于一般的过程调用并不涉及到系统状态的转换,可直接由调用过程转向被调用过程。但在运行系统调用时,由于调用和被调用过程是工作在不同的系统状态,因而不允许由调用过程直接转向被调用过程。通常都是通过软中断机制,先由用户态转换为系统态,经核心分析后,才能转向相应的系统调用处理子程序。
(3) 返回问题。在采用了抢占式(剥夺)调度方式的系统中,在被调用过程执行完后,要对系统中所有要求运行的进程做优先权分析。当调用进程仍具有最高优先级时,才返回到调用进程继续执行;否则,将引起重新调度,以便让优先权最高的进程优先执行。此时,将把调用进程放入就绪队列。
(4) 嵌套调用。像一般过程一样,系统调用也可以嵌套进行,即在一个被调用过程的执行期间,还可以利用系统调用命令去调用另一个系统调用。当然,每个系统对嵌套调用的深度都有一定的限制,例如最大深度为 6。但一般的过程对嵌套的深度则没有什么限制 [1] 。

百度关于“内核函数”的解释:

在 OS 的核心(内核)中都设置了一组用于实现各种系统功能的子程序(过程),并将它们提供给应用程序调用,即内核支持函数。由于这些函数是 OS 系统本身程序模块中的一部分,为了保护操作系统程序不被用户程序破坏,一般都不允许用户程序访问操作系统的程序和数据,所以也不允许应用程序采用一般的过程调用方式来直接调用这些过程,而是向应用程序提供了一系列的系统调用命令(即程序接口),让应用程序通过系统调用去调用所需的内核支持函数的过程。

Part3:

总结:

出于安全考虑,进程的权限被限制,有些操作是无法自己亲自执行,比如IO。但是linux系统提供了一种机制,使得进程可以借助操作系统内核来实现。这种机制就是系统调用。比如,当进程需要读取磁盘上的数据时,便会通过系统调用,由内核函数(不可能是进程!)将数据从磁盘中读取到内核的内存空间中,再将数据从内核的内存空间拷贝到进程的内存空间。
那进程又是怎样实现系统调用呢?答案是通过软中断来实现。软中断时会有上下文切换吗?
前面说在linux中,内存又被分为两部分:内核空间和进程空间。内核空间归操作系统内核使用,进程空间归进程使用。这是一种笼统的说法。事实上,linux内核在创建进程的时候,在创建task_struct的同时,会为进程创建相应的堆栈。每个进程会有两个栈:一个用户栈,存在于用户空间,一个内核栈,存在于内核空间。
当进程在用户空间运行时,cpu堆栈指针寄存器里面的内容是用户堆栈地址,使用用户栈;当进程在内核空间时,cpu堆栈指针寄存器里面的内容是内核栈空间地址,使用内核栈。
当进程因为中断或者系统调用而陷入内核态之行时,进程所使用的堆栈也要从用户栈转到内核栈。进程陷入内核态后,先把用户态堆栈的地址保存在内核栈之中,然后设置堆栈指针寄存器的内容为内核栈的地址,这样就完成了用户栈向内核栈的转换;当进程从内核态恢复到用户态之行时,在内核态之行的最后将保存在内核栈里面的用户栈的地址恢复到cpu堆栈指针寄存器即可。这样就实现了内核栈和用户栈的互转。
那么,我们知道从内核转到用户态时用户栈的地址是在陷入内核的时候保存在内核栈里面的,但是在陷入内核的时候,我们是如何知道内核栈的地址的呢?
关键在进程从用户态转到内核态的时候,进程的内核栈总是空的。这是因为,当进程在用户态运行时,使用的是用户栈,当进程陷入到内核态时,内核栈保存进程在内核态运行的相关信息,但是一旦进程返回到用户态后,内核栈中保存的信息无效,会全部删除。因此每次进程从用户态陷入内核的时候得到的内核栈都是空的。所以在进程陷入内核的时候,直接把内核栈的栈顶地址给堆栈指针寄存器就可以了。(如果不是出于某种设计理念,linux大可不必在创建进程的时候,就为进程在内核空间中开辟内核栈,选择在进程由用户态陷入内核态时在内核空间中为进程开辟内核栈也行。)
综上所述,进程从用户态陷入内核态到从内核态恢复到用户态的过程中,出现了上下文切换。不过,这和发生在进程之间的调度时的上下文切换还是有区别的。值得注意的是,在采用抢先式调度的系统中,当系统调用返回时,要重新进行调度分析是否有更高优先级的进程就绪,这时候就有可能出现进程之间的调度了。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/460786.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号