乐趣区

关于linux:Linux系统调用

相干概念

程序 vs 过程 vs 命令 : Linux 零碎上所有的操作由 过程 实现,过程 的运行是动静的,在此之前是一个动态的 程序 。用户用一个程序来启动一个过程,这个程序能够是他人写好的(最终被编译成可执行文件),比方lspwdcat,也能够是咱们本人写的。
零碎调用 : 无论如何,程序最初运行起来都是过程,并且一个程序想要在零碎上跑,要用到 零碎调用 , 这是零碎给用户提供的 API 接口。
trace 命令: Linux 有个命令 strace,罕用来跟踪过程执行时零碎调用和所接管的信号。通过manstrace 查看具体形容。
Glibc: 作为一个开发者,兴许平时并没有间接应用零碎调用,而是 Glibc 库。Glibc 是 Linux 下应用的开源的规范 C 库它是 GNU 公布的 libc 库。Glibc即零碎调用的封装。

介绍零碎调用

而后本文开始介绍这些零碎调用,先上图

过程治理

linux 操作系统应用 fork 从父过程中创立子过程,子过程 execve 运行程序 (二进制文件), 父过程waitpid 期待子过程完结。
所有过程都是父过程 fork 进去的,对于操作系统而言第一个鼻祖过程是哪来的呢? 系统启动的时候先创立一个所有用户过程的“祖宗过程”。

内存治理

在操作系统中,每个过程都有本人的 过程内存空间 。其中布局就有 代码段 数据段
一个过程的内存空间是很大的,32 位的是 4G,64 位的就更大了。物理空间是无限的,所以过程的空间不能当时调配好的,肯定是须要的时候再调配。
brkmmap 是官员堆分配内存的零碎调用,分配内存数量比拟小的时候,应用 brk 会和原来的堆的数据连在一起。当调配的内存数量比拟大的时候,应用 mmap,会从新划分一块区域。

文件治理

文件系统相当于公司的资料库,用于保留一些永恒性质的数据。能做到长期保留,文件之所以能做到这一点,一方面是因为 介质 ,另一方面是因为 格局
对于文件的操作,无非是 创立 creat关上 open读 read写 write等等
Linux 中所有皆文件 就包含 二进制文件 文本文件 stdout 文件Socket 文件 设施文件 目录文件 ,包含过程运行起来在/proc 下生成的过程号也是文件。对于每一个文件,Linux 调配了 文件描述符,这是一个整数。

信号处理

信号是异步解决机制,用于紧急突发状况。每一种信号都有默认动作,当然用户也能编写信号处理函数,通过 sigaction 零碎调用进行解决。

过程间通信

本地过程之间实现数据的互通,比拟常见的解决机制有 音讯队列 共享内存
通过 msgget 创立一个新的队列,msgsnd将音讯发送到音讯队列,而音讯接管方能够应用 msgrcv 从队列中取音讯; 咱们能够通过 shmget 创立一个共享内存块,通过 shmat 将共享内存映射到本人的内存空间,而后就能够读写了。

网络通信

内核中有 TCP/IP 网络协议栈的实现,能够通过 socket 来实现跨零碎的过程间通信。

查看源码

下载内核源码,找到 ./include/asm-x86_64/unistd.h 文件,外面对于零碎调用的定义

hinzer@ubuntu:linux-2.6.11$ head ./include/asm-x86_64/unistd.h
#ifndef _ASM_X86_64_UNISTD_H_
#define _ASM_X86_64_UNISTD_H_

#ifndef __SYSCALL
#define __SYSCALL(a,b) 
#endif

/*
 * This file contains the system call numbers.
 *

查看过程

过程通过零碎调用从用户态到内核态,用户态 - 零碎调用 - 保留寄存器 - 内核态执行零碎调用 - 复原寄存器 - 返回用户态 。对于利用开发,下层还通过glibc 库(对系统调用的一个封装库)。

从 glibc 提供的 open 函数登程,分析如何从 glibc 的 open 调用到内核的sys_open!!!

glibc 封装
# 以下相干文件
./sysdeps/unix/syscalls.list   # 列出所有 glibc 的函数对应的零碎调用
./sysdeps/unix/make-syscalls.sh # 依据下面的配置文件,对于每一个封装好的零碎调用,生成一个文件
./sysdeps/unix/syscall-template.S  # 定义了这个零碎调用的调用形式
./sysdeps/hppa/sysdep.h   # 通过 `vim -t PSEUDO` 找到 PSEUDO 这个宏的定义。

通过剖析: open 函数的代码逻辑, 得出结论: 对于任何的零碎调用,会调用DO_CALL。这也是一个宏,这个宏 32 位和 64 位的定义是不一样的。

32 位零碎调用过程

持续剖析 glibc 源码,发现宏 DO_CALL 定义处 unix/sysv/linux/i386/sysdep.h
了解: 用户调用通过软中断进入内核态,在零碎调用表中找到对应的内核零碎调用,执行内核调用之前保留用户态寄存器,执行内核调用后返回并复原用户态。

64 位零碎调用过程

DO_CALL 定义在源码地位 unix/sysv/linux/x86_64/sysdep.h, 还是将零碎调用名称转换为零碎调用号,放到寄存器 rax。和 32 位不同的是,这里是真正进行调用,不是用中断了,而是改用 syscall 指令了,而且传递参数的寄存器也变了。
了解: 用户调用通过 syscall 指令进入内核态,在零碎调用表中找到对应的内核零碎调用,执行内核调用之前保留用户态寄存器,执行内核调用后返回并复原用户态。

整个流程

补充

零碎调用表

数据结构定义在arch/x86/entry/syscall_64.c, 零碎调用列表输入在arch/x86/entry/syscalls/syscall_64.tbl

# 零碎调用号 abi 类型 函数名                      零碎调用名
2       common  open                    sys_open
零碎调用函数申明

申明在include/linux/syscalls.h, 找到有 sys_open 的申明

零碎调用函数实现

内核零碎调用实现和申明统一,其中fs/open.c

编译规定

接下来,在编译的过程中,须要依据 syscall_32.tbl 和 syscall_64.tbl 生成本人的 unistd_32.h 和 unistd_64.h。在文件 arch/x86/entry/syscalls/Makefile

参考资料

  • 趣谈 Linux 操作系统 – 05 零碎调用
  • 趣谈 Linux 零碎专栏 – 09 零碎调用
  • 如何下载查看 glibc 源代码
  • glibc 源码剖析(一)零碎调用
  • linux 下实现一个零碎调用
退出移动版