Linux 基本概念

并发(Concurrency)

线程、进程,线程安全,进程同步,可见性,一致性,锁,信号量,并发,并行

线程和进程

从操作系统概念上说,线程是最小的可执行单位,也就是系统调度的最小单位。进程是资源分配的最小单位。线程是依赖进程存在的,共享进程内的资源,如内存,cpu,io 等。在操作系统的发展过程中,为了提高系统的稳定性、吞吐量和安全性,操作系统内核和用户态做了隔离,例如 Linux 有内核线程,用户线程,内核进程,用户进程,从根本上 Linux 是没有线程的,线程对 Linux 系统来说是个特殊的进程。那么用户线程和内核线程是一一对应呢?从宏观上看是一一对应的,在用户态的每一个线程,都会在内核有对应的执行线程,但是由于资源的限制,用户态的线程和内核线程是多对一的关系。用户进程和内核进程也类似。具体怎样对应的,这里就不探讨了。
为了提高操作系统的实时性,操作系统是以时间片轮转来实现任务调度的。理论上时间片内是不可以被中断的,可认为是 cpu 最小的单位执行时间。现代操作系统为了提高用户体验,线程都是抢占式的,而中断一般在时间片用完的时候发生。线程、进程和 CPU 都是多对一的关系,所以存在进程线程切换的问题。
线程内部还是有自己内存空间的,所以有个概念叫线程内存模型。线程内部有自己私有的本地内存,故线程和线程之间的本地内存存在可见性问题。例如全局变量 A 在线程 1 修改后,线程 2 并不一定能拿到 A 的修改值,因为线程 1 会把全局变量 A 拷贝到本地内存,修改后并不会马上同步。在编译的时候,编译器为了优化,(例如利用超线程技术)可能会重排指令的执行顺序,这就会存在一致性了。

线程安全

在线程安全里面经常要讨论的两个问题就是:可见性和一致性。锁是什么东西呢?锁就是一道内存屏障,保证可见性和一致性的一种策略,由操作系统甚至更底层的硬件提供。加锁是消耗资源的,特别是在多核 CPU 上,现在多核 CPU 一般有 3 级缓存,一级缓存通常是单核独占的,而线程的本地内存很可能就保存在 cpu 的缓存里面,然而加锁就意味着保证可见性和一致性,需要中断同步数据,保证别人拿到的是最新修改值。由于用途不同,锁被设计成各种各样的,如互斥锁,读写锁,自旋锁,同步块,数据库的事务等,如果只要保证可见性的,可以不使用锁,在 java 里面可以使用 volatile 修饰全局变量。虽然在 c/c++,都有同样的修饰符,但是是不是一样的意思呢,请参考其他文章。

死锁(deadlock)

定义

多个进程竞争资源造成的互相等待情况。

资源

可重用性资源:可供重复使用多次的资源
不可抢占性资源:一旦系统把某资源分配给该进程后,就不能将它强行收回,只能在进程使用完后自动释放
可消耗资源:又叫临时性资源,它是在进程运行期间,由进程动态的创建和消耗的

死锁产生的原因

  1. 系统资源的竞争
    系统资源的竞争导致系统资源不足,以及资源分配不当,导致死锁。
    主要是竞争可重用不可抢占式的资源和可消耗的资源。
  2. 进程运行推进顺序不合适
    进程在运行过程中,请求和释放资源的顺序不当,会导致死锁。

死锁产生的条件

互斥条件 一个资源每次只能被一个进程使用,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
请求与保持条件 进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源 已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
不可剥夺条件 进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能 由获得该资源的进程自己来释放(只能是主动释放)。
循环等待条件 若干进程间形成首尾相接循环等待资源的关系
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。
形象地说,就是有两个酒鬼,一个有开瓶器,一个有酒,这两种资源都只能被一个人占有(互斥),且用完之前不能被另一个人抢去(不可剥夺),他们互相等对方手上的资源(循环等待),但又不肯放开自己手上的资源(请求与保持),因此陷入了死锁。

死锁避免

系统对进程发出每一个系统能够满足的资源申请进行动态检查,并根据检查结果决定是否分配资源,如果分配后系统可能发生死锁,则不予分配,否则予以分配。
书上给出了两种死锁避免策略

  1. 进程启动拒绝
    若对每个资源,能满足现有所有进程再加上新进程的需求,则可以启动这个进程,否则拒绝
  2. 资源分配拒绝(银行家算法)

死锁预防

死锁预防是设法至少破坏产生死锁的四个必要条件之一,严格的防止死锁的出现。

  1. 互斥
    不可能禁止,比如文件只允许互斥的写访问
  2. 占有且等待
    可以要求进程一次性请求所有需要的资源,并且阻塞这个进程直到所有请求都同时满足,这样就不会再请求新资源了。
  3. 不可抢占
  4. 循环等待

持久化(Persistent)

Linux IO 模型

X
应用程序调用内核 IO 函数的过程如下图所示:
X
处于 OS 的安全性等的考虑,进程无法直接操作 I/O 设备,必须通过系统调用来请求内核完成 I/O 动作,而内核会为每个 I/O 设备维护一个 Buffer。

  1. 用户进程发起请求;
  2. 内核接收到请求后,从 I/O 设备中获取数据到 Buffer 中;
  3. 将 Buffer 中的数据拷贝到用户进程的地址空间,该用户进程获取到数据后响应给客户端。

在整个请求过程中,数据输入至 Buffer 需要时间,从 Buffer 复制数据到进程也需要时间,这个等待时间是限制 I/O 效率的罪魁祸首,根据等待方式的不同,I/O 动作可以分为以下五种模式:

  • 阻塞 I/O(Blocking I/O)
  • 非阻塞 I/O(Non-Blocking I/O)
  • I/O 复用(I/O Multiplexing)
  • 信号驱动的 I/O(Signal Driven I/O)
  • 异步 I/O(Asynchronous I/O)

存储器管理

文件系统

IO(pipe)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <stdio.h>
#include <syscall.h>

int testPipe(){
int fd[2];

int *read_fd = &fd[0];
int *write_fd = &fd[1];

if(pipe(fd) == -1){
printf("pipe create failed\n");
}

int pid = fork();
if(pid == -1){
printf("fork failed");
return -1;
}
else if(pid == 0){
int count;
char string[] = "hahaha";
close(*read_fd);
count = write(*write_fd, string, sizeof(string));
printf("写了%d个字符\n", count);
return 0;
}
else{
int count;
char buffer[100];
close(*write_fd);
count = read(*read_fd, buffer, sizeof(buffer));
printf("父进程接受到%d字节的数据:%s", count, buffer);
return 0;
}
}

虚拟内存

swap

虚拟化(Virtualization)

驱动管理

参考

Linux 应用

  1. 虚拟内存
    All about Linux swap space
    Swap
    Linux Performance: Why You Should Almost Always Add Swap Space
  2. 演进
    Linux 内核的发展 介绍 2.6.28 和 2.6.29 版本中的新特性
    对 Linux 内核的发展方向的展望 - Linux 4.2
  3. 运维
    老司机告诉你:正规的运维工作是什么的?
  4. 并发
    Linux 原子操作 atomic_cmpxchg()/Atomic_read()/Atomic_set()/Atomic_add()/Atomic_sub()
  5. 隔离
    cgroup - jerry017cn

操作系统

  1. 分时和实时操作系统
    List of open source real-time operating systems
  2. 操作系统概念
    【操作系统】操作系统综述(一)
  3. Operating Systems: Three Easy Pieces

Concurrency

  1. 【操作系统】进程管理(二)
  2. 【操作系统】处理机调度与死锁(三)

Persistent

  1. 【操作系统】存储器管理(四)
  2. 【操作系统】文件管理(六)

Virtualization

  1. 【操作系统】设备管理(五)

Linux 内核

  1. 哈工大操作系统
    操作系统之基础
    操作系统原理与实践
  2. NJU
    在开始愉快的 PA 之旅之前
    NJU-ProjectN
  3. The Linux Information Project
  4. The Linux Kernel Archives
  5. Linux 内核文档(中文版)
  6. Write Your Own Operating System
  7. 6.828: Operating System Engineering