系统调用如何转换为CPU指令?

Nis*_*ant 13 c system-calls

假设有一个简单的程序,如:

#include<stdio.h>

void main() 
{ 
    int x;
    printf("Cool");
    fd = open("/tmp/cool.txt", O_READONLY)
}
Run Code Online (Sandbox Code Playgroud)

open是一个系统调用.我想当shell运行它时,它会进行一些其他系统调用来实现它吗?一个声明怎么样int x- 在某些时候它应该在背景中有一些额外的系统调用来从计算机获取内存?

我不确定系统调用和普通内容之间的界限是什么......最终,一切都需要操作系统的帮助吗?!

或者就像C生成一个可执行程序(代码),它可以在处理器上运行,在达到系统调用之前不需要OS协助 - 此时它必须做一些事情来加载OS指令等......

有点模糊:)请澄清.

Ale*_*amo 30

我没有按顺序回答问题,所以我在问题的答案前面加上答案.我冒昧地编辑了一下.您没有指定处理器体系结构,但我假设您想了解x86,因此处理器级别的详细信息将与x86相关.其他架构的行为可能不同(内存管理,系统调用的方式等).我也使用Linux作为例子.

c编译器是否生成可以直接在处理器上运行的可执行代码,而不需要OS协助,直到达到系统调用,此时它必须做一些事情来加载OS指令?

对,那是正确的.编译器生成可以在处理器上直接运行的本机机器代码.但是,从编译器获得的可执行文件包含代码和其他所需数据,例如,有关在内存中加载代码的位置的说明.在Linux上,ELF格式通常用于可执行文件.

如果进程完全加载到内存中并且具有足够的堆栈空间,则在进行系统调用之前不需要进一步的OS辅助.进行系统调用时,它只是调用操作系统的机器代码中的指令.程序本身不需要以任何方式"加载OS指令".处理器处理将执行转移到OS代码.

对于x86架构上的Linux,机器代码进行系统调用的一种方法是使用软件中断向量128将执行转移到操作系统.在x86汇编(Intel语法)中,表示为int 0x80.然后,Linux将根据调用程序在进行系统调用之前放入处理器寄存器的值执行任务:系统调用编号位于eax处理器寄存器中,系统调用参数位于其他处理器寄存器中.操作系统完成后,它将在eax寄存器中返回一个结果,并且可能修改了系统调用参数指向的缓冲区等.但是,请注意,这不是进行系统调用的唯一方法.

但是,如果进程不完全在内存中,并且执行移动到此时不在内存中的代码的一部分,则处理器会导致页面错误,从而将执行移动到操作系统,然后操作系统加载所需的部分将进程转移到内存中并将执行转移回进程,然后可以继续正常执行,甚至不会注意到发生的任何事情.

我不确定下一点,所以带上一粒盐.关于堆栈溢出的维基百科文章(计算机错误,不是这个站点:)似乎表明堆栈通常是固定大小的,所以int x;不应该导致操作系统运行,除非堆栈的那部分不在内存中(参见前面的内容)段).如果你有一个具有动态堆栈大小的系统(如果它甚至可能,但据我所知,它是),int x;当堆栈空间用完时也可能导致页面错误,提示操作系统分配更多堆栈这个过程的空间.

页面错误导致执行移动到操作系统,但不是通常意义上的系统调用.当您希望它为您执行某些工作时,系统调用是对操作系统的显式调用.页面错误和其他此类事件是隐含的.硬件中断不断将执行从您的进程转移到操作系统,以便它可以对它们做出反应.之后,它会将执行转移回您的流程或其他流程.

在多任务操作系统上,即使您只有一个处理器/核心,也可以一次运行多个程序.这是通过一次只运行一个程序,但快速切换程序来实现的.硬件定时器中断确保控制以及时的方式传回操作系统,这样一个进程就无法为自己占用CPU.当控制权传递给操作系统并且它已经完成了它所需要的操作时,它可能总是启动与被中断的操作不同的进程.操作系统完全透明地处理所有这些,因此您不必考虑它,并且您的过程不会注意到它.从您的过程的角度来看,它是连续执行的.

简而言之:只有在您明确要求时,您的程序才会执行系统调用.操作系统还可以在需要时将进程的一部分交换进出内存,并且通常在后台执行与您的进程相关且无关的事情,但您通常不需要考虑这一点.(但是,通过保持程序尽可能小,以及类似的事情,可以减少页面错误的数量)

在这种情况下open()是一个显式系统调用,但我想当shell运行它时,它会进行一些其他系统调用来实现它.

不,shell与open()你的c程序中的调用无关.你的程序进行一次系统调用,而shell根本不会出现.

shell只会在程序启动时影响你的程序.当您使用shell启动程序时,shell会执行fork系统调用以分离第二个进程,然后进行execve系统调用以将其自身替换为您的程序.之后,您的程序处于控制之中.在控件进入main()函数之前,它会执行一些初始化代码,由编译器放在那里.如果要查看进程所调用的系统,可以使用Linux strace来查看它们.strace ls例如,只需说明ls在执行期间查看系统调用的内容.如果仅使用main()立即返回的函数编译ac程序,则可以看到strace初始化代码所进行的系统调用.

该过程如何从计算机等获取内存?它必须再次涉及一些系统调用吗?我不确定系统调用和普通内容之间的界限是什么.最终的一切都需要OS帮助,对吗?

是的,系统调用.当您的程序通过execve系统调用加载到内存中时,它会为您的进程获取足够的内存.当您需要更多内存和调用时malloc(),brk如果内部缓存内存耗尽,它将进行系统调用以增加进程的数据段.

并非一切都需要操作系统的明确帮助.如果您有足够的内存,将所有输入都存储在内存中,并将输出数据写入内存,则根本不需要操作系统.也就是说,只要您只对内存中已有的数据进行计算,不需要更多内存,也不需要与外界通信,就不需要操作系统.另一方面,一个根本不与外界通信的程序是一个相当无用的程序,因为它无法获得任何输入,也无法提供任何输出.即使您计算pi的百万分之一小数,如果您不将其输出给用户也没关系.

这个答案变得非常大,所以如果我错过了什么或者没有给出足够清楚的解释,请给我留言,我会尽力详细说明.如果有人发现任何错误,请务必指出它们.