系统调用和库函数的区别

前言

从用户的角度来看,系统调用和库函数似乎没有什么区别,它们都是以C函数的形式出现,并且两者都为应用程序提供服务。但从实现者角度来看,它们之间是有根本的区别。那么,它们之间到底有哪些不同呢?在说明之前,先简单了解以下系统调用和库函数。

系统调用

系统调用指运行在用户空间的程序向操作系统内核请求需要更高权限运行的服务。它通过软中断向内核态发出一个明确的请求。系统调用实现了用户态进程和硬件设备之间的大部分接口。

库函数

库函数用于提供用户态服务。它可能调用封装了一个或几个不同的系统调用(printf调用write),也可能直接提供用户态服务(atoi不调用任何系统调用)。

常见系统调用和库函数

  • 常见系统调用
    open, close, read, write, ioctl,fork,clone,exit,getpid,access,chdir,chmod,stat,brk,mmap等,需要包含unistd.h等头文件。
  • 常见库函数
    printf,scanf,fopen,fclose,fgetc,fgets,fprintf,fsacnf,fputc,calloc,free,malloc,realloc,strcat,strchr,strcmp,strcpy,strlen,strstr等,需要包含stdio.h,string.h,alloc.h,stdlib.h等头文件。

    区别

  • 系统调用通常不可替换,而库函数通常可替换
    普通的库函数调用由函数库或用户自己提供,因此库函数是可以替换的。例如,对于存储空间分配函数malloc,如果不习惯它的操作方式,我们完全可以定义自己的malloc函数。

  • 系统调用通常提供最小接口,而库函数通常提供较复杂功能
    例如sbrk系统调用分配一块空间给进程,而malloc则在用户层次对这以空间进行管理。

  • 系统调用运行在内核空间,而库函数运行在用户空间
    因为系统调用属于内核,和库函数不属于内核。因此,如果当用户态进程调用一个系统调用时,CPU需要将其切换到内核态,并执行一个内核函数。

  • 内核调用都返回一个整数值,而库函数并非一定如此
    在内核中,整数或0表示系统调用成功结束,而负数表示一个出错条件。而出错时,内核不会将其设置在errno,而是由库函数从系统调用返回后对其进行设置或使用。

  • POSIX 标准针对库函数而不是系统调用
    判断一个系统是否与POSIX需要看它是否提供一组合适的应用程序接口,而不管其对应的函数是如何实现的。因此从移值性来讲,使用库函数的移植性较系统调用更好。
  • 系统调用运行时间属于系统时间,库函数运行时间属于用户时间
  • 调用系统调用开销相对库函数来说更大
    很多库函数本身都调用了系统调用,那为什么直接调用系统调用的开销较大呢?这得益于双缓冲的实现,在用户态和内核态,都应用了缓冲技术,对于文件读写来说,调用库函数,可以大大减少调用系统调用的次数。而用户进程调用系统调用需要在用户空间和内核空间进行上下文切换,开销较大。如此以来,库函数的开销也就会比直接调用系统调用小了。另外一方面,库函数同样会对系统调用的性能进行优化。

总结

系统调用与库函数有联系也有区别,但是通常情况下,会建议使用库函数,主要出于以下几个方面的考虑:

  • 双缓冲技术
  • 移植性
  • 系统调用本身性能缺陷
守望 wechat
关注公众号[编程珠玑]获取更多原创技术文章
出入相友,守望相助!