系统调用和库函数

一、系统调用

下图我们可以看到用户和操作系统的关系:
系统调用和库函数_第1张图片

(1)什么是系统调用

系统调用就是内核的一种出口,它是操作系统提供给用户程序调用的一组”特殊“的接口。逻辑上系统调用可以被看作一个内核与用户空间程序的接口。

它的执行流程就是,把用户进程的请求传递给内核,待内核处理完毕后将处理结果返回给用户空间。

(陷入进内核态0x80)
系统调用和库函数_第2张图片

(2)系统调用和API

系统调用和API很多人会理解错误,我们通过上面的描述知道了什么是系统调用,那么API是什么呢?

API(Application Programming Interface):应用程序接口,是一些预先定义的函数,跟内核没有必然的联系。它提供应用程序与开发人员基于某软件或硬件的以访问一组例程的能力,而又无需访问源码或者了解具体实现细节。

Linux的API遵循POSIX标准。我们应用程序来编程的时候是通过操作系统提供的应用编程接口(API)而不是直接通过系统调用来编程的。有的时候API和系统调用形式是一样的。

下图我们可以看到系统调用和API的关系。中间的全部是API,内核态都是系统调用,我们可以发现有的API并没有调用系统调用,而有的API调用了系统调用。
系统调用和库函数_第3张图片

区别: api是函数的定义,规定了这个函数的功能,跟内核无直接关系。而系统调用是通过中断向内核发请求,实现内核提供的某些服务。

联系: 一个api可能会需要一个或多个系统调用来完成特定功能。通俗点说就是如果这个api需要跟内核打交道就需要系统调用,否则不需要。
程序员调用的是API(API函数),然后通过与系统调用共同完成函数的功能。

因此,API是一个提供给应用程序的接口,一组函数,是与程序员进行直接交互的。

系统调用则不与程序员进行交互的,它根据API函数,通过一个软中断机制向内核提交请求,以获取内核服务的接口。

并不是所有的API函数都一一对应一个系统调用,有时,一个API函数会需要几个系统调用来共同完成函数的功能,甚至还有一些API函数不需要调用相应的系统调用(因此它所完成的不是内核提供的服务)

(3)系统调用和系统命令

系统命令和系统调用又是什么样的关系呢?

系统命令相对于应用编程接口是更高的一层,每一个系统调用都可以看作一个可执行程序,例如ls、hostname等命令,它相当于封装和调用了系统调用,我们可以通过strace命令查看某个命令调用的系统调用,下面是ls命令调用的系统调用(很长,所以没有列举完毕):
关于strace 命令我们可以查看下面这篇文章:http://man.linuxde.net/strace

lyf@ubuntu:~/code/my_github$ strace ls
execve("/bin/ls", ["ls"], [/* 71 vars */]) = 0
brk(NULL)                               = 0x1a95000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=102559, ...}) = 0
mmap(NULL, 102559, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f094b007000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260Z\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=130224, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f094b006000
mmap(NULL, 2234080, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f094abda000
mprotect(0x7f094abf9000, 2093056, PROT_NONE) = 0
mmap(0x7f094adf8000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e000) = 0x7f094adf8000
mmap(0x7f094adfa000, 5856, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f094adfa000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\t\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1868984, ...}) = 0
mmap(NULL, 3971488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f094a810000
mprotect(0x7f094a9d0000, 2097152, PROT_NONE) = 0
mmap(0x7f094abd0000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c0000) = 0x7f094abd0000
mmap(0x7f094abd6000, 14752, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f094abd6000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libpcre.so.3", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0000\25\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=456632, ...}) = 0
mmap(NULL, 2552072, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f094a5a0000

(3)系统调用和内核函数

我们先来看下内核函数是一个什么东西。

  • 内核函数在形式上与普通函数是一样的,但是不同的是他是在内核上进行实现的,需要满足一些内核编程的要求。

  • 系统调用本身并非是内核函数,因为它相当于用户进程进入内核的一个接口,但是它是由内核函数进行实现的。

  • 一旦我们进入内核后,不同的系统调用会找到各自相对应的内核函数,而这些内核函数就被称之为系统调用的“服务例程”

例如下面两个例子:
系统调用getpid在内核种相对应的服务例程为sys_getpid(),而sys_getpid()在内核的具体实现为:

//这个代码和版本有关
asmlinkage long sys_getpid(void)
{
    return current->pid;
}

由于这个程序运行在用户态下,所以我们需要增添一个头文件
其中的SYS_getpid是内核函数,其他调用的都是库函数
系统调用和库函数_第4张图片

(4)系统调用是如何实现的

在系统调用内,主要通过两个东西来实现,一个是系统调用号,一个是系统调用表。

关于系统调用,我们可以看以下两篇文章,这两篇文章很详细的解释了系统调用是如何实现的:
https://blog.csdn.net/zxxssdsd/article/details/17025747
https://blog.csdn.net/chosen0ne/article/details/7721550
系统调用和库函数_第5张图片

系统调用和库函数_第6张图片

(5)如何调用系统调用的

系统调用和库函数_第7张图片

系统调用和库函数_第8张图片

系统调用和库函数_第9张图片

二、库函数

下面是库函数的简单介绍:
库函数(Library function)是把函数放到库里,供别人使用的一种方式。.方法是把一些常用到的函数编完放到一个文件里,供不同的人进行调用。调用的时候把它所在的文件名用#include<>加到里面就可以了。一般是放到lib文件里的。

我们通过文章最前面的图可以知道库函数就是运行在用户态的函数。

三、思考

(1)库函数和系统调用的区别是什么?

库是可重用的模块处于用户态
系统调用是os提供的服务 处于内核态 不能直接调用 而要使用类似int 0x80的软中断陷入内核 所以库函数中有很大部分是对系统调用的封装

你可能感兴趣的:(Linux)