zoukankan      html  css  js  c++  java
  • 课本笔记

    Linux内核简介

    • Unix从一个失败的多用户操作系统Multics中衍生来的。
    • Unix强大的原因:
      • 简洁
      • 几乎所有的东西都被当做文件来对待,可以通过相同的系统调用接口来进行调用。
      • 因为它是由c语言编写,所以具有强大的移植能力
      • 进程创建迅速,且拥有独特的fork()系统调用
      • 具备清晰的层次化
    • Unix操作系统多样的功能:
      • 抢占式多任务
      • 多线程
      • 虚拟内存
      • 换页
      • 动态链接
      • TCP/IP网络
    • Linux是类Unix系统,但它不是Unix。
    • Linux内核是自由软件。

    操作系统和内核简介

    • 内核是操作系统的内在核心,有时候其被称为管理者或者是操作系统核心。
    • 应用程序通过系统调用与内核通信。
    • 内核可以通过中断号查找相应的中断服务程序,并调用这个程序响应和处理中断。
    • 每个处理器在任何指定时间点上的活动必然概括为下列三者之一:
      • 运行于用户空间,执行用户进程
      • 运行于内核空间,处于进程上下文,代表某个特定的进程执行。
      • 运行于内核空间,处于中断上下文,与任何进程无关,处理某个特定的中断。

    Linux内核和传统Unix内核的比较

    • Unix内核通常需要硬件系统提供页机制(MMU)以管理内存。
    • 操作系统内核的两大阵营:单内核和微内核
      • 单内核:内核从整体上作为一个单独的大过程来实现,同时也运行在一个单独的地址空间上运行。内核可以直接调用函数。大多数的Unix系统设计为单模块。
        • 简单
        • 性能高
      • 微内核:其的功能被划分为多个独立的过程,每个过程叫做一个服务器。所有的服务器都保持独立并运行在各自的地址空间上,通过消息传递处理内核通信——IPC机制(进程间通信机制),消息传递需要一定的周期。
    • Linux是一个单内核,即Linx内核运行在单独的内核地址空间上。
    • Linux内核与传统的Unix系统之间存在一些显著的差异:
      • Linux支持动态加载内核模块
      • Linux支持对称多处理机制(SMP机制)
      • Linux内核可以抢占
      • Linux对线程支持的实现比较有意思
      • Linux提供具有设备类的面向对象的设备模型、热插拔时间,以及用户控件的设备文件系统
      • Linux忽略了一些拙劣的Unix特性
      • Linux体现了自由。

    Linux内核版本

    • 主版本号.从版本号.修订版本号.稳定版本号

    从内核出发

    • 使用Git获取Linux版本中的一种版本

      git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
    • 更新版本

      git pull
    • 安装内核源代码
      • 若为bzip2的压缩形式:tar xvjf linux-x.y.z.tar.bz2
      • GNU的zip:tar xvzf linux-x..y.z.tar.gz
    • 使用补丁:patch -pl< ../patch-x.y.z

    • 内核的配置与安装通过Makefile与config所定。

    第五章 系统调用

    一、与内核通信

    系统调用在用户控件进程和硬件设备之间添加了一个中间层,作用如下“

    • 为用户空间提供了一种硬件的抽象接口
    • 系统调用保证了系统的稳定和安全
    • 每个进程都运行在虚拟系统中,而在用户控件和系统的其余部分提供这样一层公共接口

    在Linux中,系统调用是用户控件访问内核的唯一手段;除异常和陷入外,他们是内核唯一的合法入口。

    ※系统调用与API的不同在于运行级别的切换。

    二、API、POSIX和C库

    POSIX、API、C库和系统调用之间的关系如下图:
    enter description here
    POSIX-Unix世界中最流行的应用编程接口是给予POSIX标准的。
    Linux的系统调用作为C库的一部分提供。
    C库实现了Unix系统的主要API,包括标准C库函数和系统调用接口,即POSIXdM大部分API。

    Unix的接口设计——“提供机制而不是策略”

    三、系统调用

    系统调用- syscall
    通常通过C库中定义的函数调用来进行系统调用。
    几种常见机制:

    - 通过返回一个long型的返回值来表示成功或者错误
    - 用一个负的返回值来表明错误
    - 返回0代表成功
    - 系统调用出现错误的时候会把错误码写入errno全局变量
    - 通过perror()库函数可以把该变量翻译成用户可以理解的错误字符串

    定义系统调用:

    例:asmlinkage long sys_getpid(void)
    
    1. asmlingkage,这是一个编译指令,通知编译器仅从栈中提取该函数的参数。所有的系统调用都需要这个限定词。
    2. 返回值long。
        为了保证32位和64位系统的兼容,系统调用在用户空间返回值int,内核空间long
    3. 命名规则:sys_xxx

    1.系统调用号

    每个系统调用号独一无二,一旦分配就不能再有任何变更。
    执行系统调用时,通过系统调用号指明,进程不会提及系统调用的名称。
    未实现系统调用——sys_ni_syscall(),返回-ENOSYS,针对无用的系统调用。

    2.系统调用的性能——执行速度快

    原因?

    • 上下文切换时间短
    • 系统调用处理程序和每个系统调用本身都很简洁

    四、系统调用处理程序

    因为用户控件不能直接执行内核代码,需要切换内核态,它需要用某种方式告知内核,自己需要执行一个系统调用,请求切换到内核态,这个通知内核的机制是软中断
    ——通过引发一个异常来促使系统切换到内核态中去执行异常处理程序,即系统调用处理程序system_call()。
    中断号128,指令如下:

    int 128
    或者
    int 0x80
    
    退出是iret

    ※新指令sysenter,比int中断指令更快更专业。

    1.指定恰当的系统调用

    2.参数调用

    这两节讲述的内容在视频中亦有体现,不在此重复。
    新知识点:
    system_call()函数通过将给定的系统调用号与NR_syscalls作比较来检查其有效性。大于等于返回-ENOSYS,否则执行相应系统调用:

    call *sys_call_table(,%rax,8)

    过程:
    int 0x80 → 中断向量表中找到对应中断向量 →系统调用表,

    JMP(EAX*4 + system_xxx)

    乘以4是因为系统调用表中的表象是以64位存放的。
    enter description here

    五、系统调用的实现

    1.实现系统调用

    • 决定它的用途
      不提倡采用多用途的系统调用
    • 确定参数、返回值、错误码
      力求简洁
    • 越通用越好,不做错误假设
    • 时刻注意可移植性和健壮性

      2.参数验证

      必须检查每个参数,保证他们不但合法有效,而且正确。
      最重要——检查用户提供的指针:

      • 指针指向的内存区域必须属于用户空间
      • 指针指向的内存区域在进程的地址空间内
      • 决不能绕过内存访问限制
        内核无论何时都不能轻率地接受来自用户空间的指针。

    ※1.检查读写
    (1)向用户空间写入数据——copy_to_user()
    参数:

    进程空间中的目的内存地址
    内核空间内的源地址
    需要拷贝的数据长度

    (2)从用户控件读取数据——copy_from_user()
    把第二个参数指定的位置上的数据拷贝到第一个参数指定的位置上
    第三个参数——拷贝数据长度。

    以上两个函数成功返回0,失败返回没能完成拷贝的数据的字节数。
    这两个函数都有可能引起阻塞——当包含用户数据的页被换出到硬盘上而不是物理内存上的时候。

    ※2.检查是否有合法权限
    老版本:suser()
    新版本:权能机制,capable(),返回非0有权操作,返回0则无权操作。

    六、系统调用上下文

    内核在执行系统调用时处于进程上下文。
    在进程上下文中,内核可以:

    • 休眠
      说明系统调用可以使用内核提供的绝大部分功能
    • 可以被抢占
      要求保证该系统调用是可重入的

    1.绑定一个系统调用的最后步骤

    1,在系统调用表的最后加入一个表项。
    2,对于所支持的各种体系结构,系统调用号都必须定义于<asm/unistd.h>中
    3,系统调用必须被编译进内核映像,不能被编译成模块。——放进kernel/下的一个相关文件中即可,例如sys.c。

    2.从用户空间访问系统调用

    Linux本身提供了一组宏,用于直接对系统调用进行访问。

    _syscalln()     //n的范围从0到6,代表需要传递给系统调用的参数个数。

    例如:

    long open(const char *filename, int flags, int mode)
    =
    #define NR_open 5
    _syscall3(long, open,const char*, filename, int, flags, int, mode)
    应用程序可以直接用open()了。

    对于每个宏来说,都有(2+2xn)个参数:

    1.系统调用的返回值类型
    2.系统调用的名称
    以后按照系统调用参数的顺序排列每个参数的类型和名称。

    _NR_open在<asm/unistd.h>中定义。
    这个宏会被扩展成为内嵌汇编的C函数。

    3.为什么不通过系统调用的方式实现

    好处:

    - 系统调用创建容易并且使用方便
    - linux系统调用的高性能

    问题:

    - 占用系统调用号
    - 固化,不允许改动接口
    - 需要分别注册到每个需要支持的体系结构中
    - 脚本中不易调用,文件系统中也不能直接访问
    - 在主内核树外难以维护使用
    - ……

    替代:

    - 某些接口,例如信号量,用文件描述符表示
    - 把增加的信息作为一个文件放在sysfs的合适位置。

    总而言之——
    Linux尽量避免每出现一种新的抽象就简单地加入一个新的系统调用。

  • 相关阅读:
    邀您参加 | BigData & Alluxio 交流会-成都站
    mongodb之使用explain和hint性能分析和优化
    mongodb 3.x 之实用新功能窥看[2] ——使用$lookup做多表关联处理
    mongodb 3.x 之实用新功能窥看[1] ——使用TTLIndex做Cache处理
    asp.net mvc 之旅 —— 第六站 ActionFilter的应用及源码分析
    asp.net mvc 之旅 —— 第五站 从源码中分析asp.net mvc 中的TempData
    分布式架构中一致性解决方案——Zookeeper集群搭建
    搭建高可用的redis集群,避免standalone模式带给你的苦难
    asp.net mvc 之旅—— 第四站 学会用Reflector调试我们的MVC框架代码
    使用强大的可视化工具redislive来监控我们的redis,别让自己死的太惨~~~
  • 原文地址:https://www.cnblogs.com/20135213lhj/p/5299248.html
Copyright © 2011-2022 走看看