Linux库函数与系统调用的区别

转自https://www.cnblogs.com/liwei0526vip/p/8998751.html

一、系统调用

系统调用可以理解为操作系统为用户提供的一系列操作接口(API),这些接口提供了对系统硬件设备功能的操作。

例如:利用库函数printf()在屏幕上打印信息,程序中调用了printf()函数,而库函数printf()调用了系统调用write()函数,实现了终端信息打印功能。

二、库函数

库函数可以理解为对系统调用的的一层封装。系统调用作为内核提供给用户程序的接口,它的执行效率和功能是比较高效和精简的。

但有时我们需要一些更复杂的功能,这时库函数可能包含多个系统调用,把这些处理过程封装成一个函数再提供给程序员。更方便程序员编码。

所以库函数有可能包含一个系统调用,有可能包含好几个,也有可能没有系统调用(比如有些操作不需要涉及内核的功能strcpy,strlen)

三、系统调用意义

  • 避免了用户直接对底层硬件进行编程。如:将hello world打印到终端,终端对系统来说是硬件资源,如果没有系统调用。用户程序需要自己编写终端设备的驱动,以及控制终端如何显示代码。
  • 隐藏背后的技术细节。比如读写文件,如果使用了系统调用,用户程序无须关心数据在磁盘的那个磁道和扇区,以及数据要加载到内存什么位置。
  • 保证系统的安全性和稳定性。用户程序是运行在用户空间的,不能直接操作内核地址空间。如果随意访问内核底层的数据,那么系统的安全性和稳定性就没法保证了。而系统调用的功能是由内核来实现的,用户只需要调用接口,无需关心细节,避免了系统的安全隐患。用户可以通过系统调用运行在内核空间

总而言之:我们只需要把系统调用当作一个内核提供给我们的操作接口。这个接口实现了一定的功能,并且系统调用是很安全的。

四、库函数与系统调用的区别

函数库调用是应用程序的一部分,而系统调用是操作系统的一部分。

  • 所有C库函数都是相同的,而系统调用依赖操作系统,各个操作系统的系统调用是不同的。
  • 库函数调用是调用函数库的一个程序。而系统调用是调用系统内核的服务。
  • 库函数调用运行在用户地址空间,而系统调用运行在内核空间,因此库函数调用的运行时间属于用户时间,系统调用运行时间属于系统时间。
  • 库函数调用属于过程调用,开销较小,而系统调用需要切换内核上下文环境然后切换回来(用户->内核->用户),开销较大

库函数调用大概花费时间为半微妙。而系统调用所需要的时间大约是库函数调用的70倍(35微秒)因为系统调用会有内核上下文切换的开销。所以尽可能的减少系统调用的数量,但是,许多c函数库中的程序通过系统调用来实现。

五、正确理解库函数高效于系统调用

上述说明库函数调用性能远高于系统调用的前提是,库函数中没有使用系统调用。而包含了系统调用的库函数,其性能确实也要高于系统调用。比如文件IO函数fread、fwrite、fputc、fgetc等,这些函数通常情况下性能确实比系统调用高。原因在于这些库函数使用了缓冲区,减少了系统调用的次数。因而显得性能较高。

六、系统调用是如何运行的

上述内容基本说清楚了库函数与系统调用的概念以及它们之间的关系,下面我们来理解系统调用到底是如何运行的。

当一个进程正在运行,遇到读写文件操作,会发生一个中断,中断后系统会把当前用户进程的一些寄存器信息保存在内核堆栈中,接着去处理中断服务程序,这里是要去执行系统调用,Linux 中通过执行 int $0x80 来执行系统调用的中断,但内核实现了很多系统调用,这时需要传递「系统调用号」来指明需要哪个系统调用。

为了更清楚的说明系统调用的过程,我们这里参考网上的一段代码来实现系统调用:

int main()
{
    time_t tt; 
    struct tm *t; 
    asm volatile (
        "mov $0,%%ebx\n\t"
        "mov $0xd,%%eax\n\t"
        "int $0x80\n\t"
        "mov %%eax,%0\n\t"
        : "=m" (tt)
    );  
    t = localtime(&tt);
    printf("Time: %d-%02d-%02d %02d:%02d:%02d\n",
           t->tm_year + 1900,
           t->tm_mon + 1, t->tm_mday,
           t->tm_hour, t->tm_min, t->tm_sec);
}

[linuxblogs@host ~]$ gcc a.c -oa && ./a
Time: 2018-05-06 03:23:46

首先通过 mov $0xd %%eax 来将系统调用放入 %eax 寄存器中,time() 的系统调用号是 13,然后执行 int $0x80 系统就会去执行 time() 这个系统调用了。其实代码中的汇编部分就是实现 time() 系统调用的功能,汇编代码不懂没关系(我也不太懂),这里主要是为了说清楚系统调用的整个过程。

猜你喜欢

转载自blog.csdn.net/wllen_/article/details/81255423