liunx guide

linux interface

application interface

system call interface

library function interface

linux component

bootloader

  • 引导程序(Bootloader):引导程序是管理计算机启动过程的软件,对于大多数用户而言,只是弹出一个屏幕,但其实内部操作系统做了很多事情

kernel

  • 内核(Kernel):内核是操作系统的核心,负责管理
    CPU、内存和外围设备等

init system

  • 初始化系统(Init System):这是一个引导用户空间并负责控制守护程序的子系统。一旦从引导加载程序移交了初始引导,它就是用于管理引导过程的初始化系统。

daemon

  • 后台进程(Daemon):后台进程顾名思义就是在后台运行的程序,比如打印、声音、调度等,它们可以在引导过程中启动,也可以在登录桌面后启动

Graphical server

  • 图形服务器(Graphical server):这是在监视器上显示图形的子系统。通常将其称为 X 服务器或 X。

desktop environment

  • 桌面环境(Desktop environment):这是用户与之实际交互的部分,有很多桌面环境可供选择,每个桌面环境都包含内置应用程序,比如文件管理器、Web 浏览器、游戏等

applications

  • 应用程序(Applications):桌面环境不提供完整的应用程序,就像 Windows 和 macOS 一样,Linux
    提供了成千上万个可以轻松找到并安装的高质量软件。

shell

shell 命令行使用速度快、功能更强大、而且易于扩展、并且不会带来肢体重复性劳损(RSI)

shell 脚本其实也是一段程序,shell 脚本中可以对变量进行赋值,也包含循环控制语句比如 if、for、while 等,shell 的设计目标是让其看起来和 C 相似(There is no doubt that C is father)。由于

shell 也是一个用户程序,所以用户可以选择不同的 shell.

Linux 的命令行也就是 shell,它由大量标准应用程序组成。这些应用程序主要有下面六种

  • 文件和目录操作命令
    过滤器
    文本程序
    系统管理
    程序开发工具,例如编辑器和编译器
    其他

process and thread

basic concept

  • 在 Linux 系统中,进程通过非常简单的方式来创建,fork
    系统调用会创建一个源进程的拷贝(副本)。调用 fork 函数的进程被称为 父进程(parent process),使用 fork 函数创建出来的进程被称为 子进程(child process)
  • Linux 中有一种特殊的守护进程被称为 计划守护进程(Cron daemon) ,计划守护进程可以每分钟醒来一次检查是否有工作要做,做完会继续回到睡眠状态等待下一次唤醒。
  • 那么该如何区分父进程和子进程呢?子进程只是父进程的拷贝,所以它们几乎所有的情况都一样,包括内存映像、变量、寄存器等。区分的关键在于 fork 函数调用后的返回值,如果 fork 后返回一个非零值,这个非零值即是子进程的
    进程标识符(Process Identiier, PID)

Inter-Process communication,IPC(进程间的通信)

  • signal(信号)
  • 异步事件信号
  • 信号是 UNIX
    系统最先开始使用的进程间通信机制,因为 Linux 是继承于
    UNIX 的,所以 Linux 也支持信号机制,通过向一个或多个进程发送 异步事件信号 来实现,信号可以从键盘或者访问不存在的位置等地方产生;信号通过 shell 将任务发送给子进程

你可以在 Linux 系统上输入 kill -l 来列出系统使用的信号

  • pipe(管道)
  • sort(产生输入)
  • head(读取输出)
  • share memory(共享内存)
  • 两个进程之间还可以通过共享内存进行进程间通信,其中两个或者多个进程可以访问公共内存空间。两个进程的共享工作是通过共享内存完成的,一个进程所作的修改可以对另一个进程可见(很像线程间的通信)。
  • 创建共享内存段或者使用已创建的共享内存段(shmget())

将进程附加到已经创建的内存段中(shmat())

从已连接的共享内存段分离进程(shmdt())

对共享内存段执行控制操作(shmctl())

  • FIFO(先入先出队列)
  • 先入先出队列 FIFO 通常被称为
    命名管道(Named Pipes),命名管道的工作方式与常规管道非常相似,但是确实有一些明显的区别。未命名的管道没有备份文件:操作系统负责维护内存中的缓冲区,用来将字节从写入器传输到读取器。一旦写入或者输出终止的话,缓冲区将被回收,传输的数据会丢失。相比之下,命名管道具有支持文件和独特 API ,命名管道在文件系统中作为设备的专用文件存在。当所有的进程通信完成后,命名管道将保留在文件系统中以备后用。命名管道具有严格的 FIFO 行为
  • message queue(消息队列)
  • 一听到消息队列这个名词你可能不知道是什么意思,消息队列是用来描述内核寻址空间内的内部链接列表。可以按几种不同的方式将消息按顺序发送到队列并从队列中检索消息。每个消息队列由 IPC 标识符唯一标识。消息队列有两种模式,一种是 严格模式, 严格模式就像是
    FIFO 先入先出队列似的,消息顺序发送,顺序读取。还有一种模式是 非严格模式,消息的顺序性不是非常重要。
  • socket(套接字)
  • 还有一种管理两个进程间通信的是使用 socket,socket 提供端到端的双相通信。一个套接字可以与一个或多个进程关联。就像管道有命令管道和未命名管道一样,套接字也有两种模式,套接字一般用于两个进程之间的网络通信,网络套接字需要来自诸如 TCP(传输控制协议) 或较低级别 UDP(用户数据报协议) 等基础协议的支持。
  • 套接字有以下几种分类

顺序包套接字(Sequential
Packet Socket): 此类套接字为最大长度固定的数据报提供可靠的连接。此连接是双向的并且是顺序的。

数据报套接字(Datagram
Socket):数据包套接字支持双向数据流。数据包套接字接受消息的顺序与发送者可能不同。

流式套接字(Stream
Socket):流套接字的工作方式类似于电话对话,提供双向可靠的数据流。

原始套接字(Raw
Socket): 可以使用原始套接字访问基础通信协议。

implementation of processes and threads(进程和线程的实现)

  • processes
  • linux通常使用structure来创建任务(不区分轻量级进程、进程和线程)
  • 每个进程都会有一个task_struct进程描述符与之对应;进程描述符(调度参数、打开文件描述符等等,它从一开始进程创建就一致存储在内核堆栈之中)
  • 调度参数(scheduling
    parameters):进程优先级、最近消耗 CPU 的时间、最近睡眠时间一起决定了下一个需要运行的进程
  • 内存映像(memory
    image):我们上面说到,进程映像是执行程序时所需要的可执行文件,它由数据和代码组成。
  • 信号(signals):显示哪些信号被捕获、哪些信号被执行
  • 寄存器:当发生内核陷入 (trap) 时,寄存器的内容会被保存下来。
  • 系统调用状态(system call state):当前系统调用的信息,包括参数和结果
  • 文件描述符表(file descriptor table):有关文件描述符的系统被调用时,文件描述符作为索引在文件描述符表中定位相关文件的 i-node 数据结构
  • 统计数据(accounting):记录用户、进程占用系统 CPU 时间表的指针,一些操作系统还保存进程最多占用的 CPU 时间、进程拥有的最大堆栈空间、进程可以消耗的页面数等。
  • 内核堆栈(kernel
    stack):进程的内核部分可以使用的固定堆栈
  • 其他: 当前进程状态、事件等待时间、距离警报的超时时间、PID、父进程的 PID 以及用户标识符等
  • 利用 PID来区分不同的进程,内核会讲所有进程的任务结构组成一个双向链表(pid会直接被映射为进程的任务结构,不需要遍历双向链表)
  • 进程位于内存被称为
    PIM(Process In Memory) ,这是冯诺伊曼体系架构的一种体现,加载到内存中并执行的程序称为进程。简单来说,一个进程就是正在执行的程序。
  • threads
  • 线程是轻量级进程,轻量级体现在所有进程切换都需要清除所有表,进程间的共享信息比较麻烦,一般是通过管道或者共享内存,线程的切换不需要昂贵的开销
  • 线程分为两种:用户级线程和内核级线程
  • 用户级线程
  • 用户级线程避免使用内核,通常,每个线程会显示调用开关,发送信号或者执行某种切换操作来放弃 CPU,同样,计时器可以强制进行开关,用户线程的切换速度通常比内核线程快很多。在用户级别实现线程会有一个问题,即单个线程可能会垄断 CPU 时间片,导致其他线程无法执行从而 饿死。如果执行一个 I/O 操作,那么 I/O 会阻塞,其他线程也无法运行。
  • 一种解决方案是,一些用户级的线程包解决了这个问题。可以使用时钟周期的监视器来控制第一时间时间片独占。然后,一些库通过特殊的包装来解决系统调用的 I/O 阻塞问题,或者可以为非阻塞 I/O 编写任务。
  • 内核级线程

memory mangement

I/O mangement