操作系统知识点积累

一、内存管理

malloc有分配物理内存吗?

  • 概述
    通常,malloc并没有一开始就分配物理内存,它只是分配虚拟内存,创建对应的页表,但是没有在页表项中填写物理页的页号;只有当要访问的页面没有映射到物理页时,才会发生缺页中断,从物理内存管理器分配物理内存

malloc与kmalloc区别

在物理内存层面二者通常都是落实到buddy(伙伴系统)。不过分配物理内存分配通常是在缺页中断发生时才进行。

malloc与kmalloc分配的都暂时只是虚拟内存,此时只是分配虚拟内存,创建对应的页表,但是没有在页表项中填写物理页的页号;只有当要访问的页面没有映射到物理页时,才会发生缺页中断,从物理内存管理器分配物理内存

二者的区别在于虚拟内存的分配与管理方法:
kmalloc是通过slab算法实现虚拟内存的分配与管理,且它在内核中使用;
malloc是用户态程序先通过mmap或者brk系统调用获得较大块的虚拟内存后,再像slab一样对其进行细分维护,根据用户需要返回相应内存的指针。malloc维护虚拟内存是通过自己的空闲链表实现,而不是slab算法(小于128K时通过brk在堆分配,大于128K时通过mmap在映射区分配)

地址总线上传输的地址

地址线上传输的都是物理地址!访问内存前都需要先经过地址转换,将虚拟地址转换为物理地址,然后才通过地址线访问物理内存……

二、进程线程

线程的三种实现方式?

用户线程实现(重要)

  • 线程的三种实现方式
    在这里插入图片描述
  • 用户线程实现
    内核中仍然只有PCB,而没有TCB!操作系统并不感知用户态下有多线程的支持
    在这里插入图片描述
  • 用户线程的特征
    在这里插入图片描述
  • 用户线程的不足
    在这里插入图片描述

内核线程实现

  • 内核线程实现
    在这里插入图片描述
  • 内核线程的特征
    在这里插入图片描述

轻量级进程(混合实现)

  • 轻权进程
    solaris有实现轻权进程,不过效果不如预期理想!
    在这里插入图片描述
    仍然不是很懂? => 可参考《深入理解JVM》第12章关于Java线程实现

进程/线程调度算法

先来先服务、短作业优先、时间片轮转、高响应比、多级队列等…
ucore—15至16讲:处理机调度

多进程多线程的适用场景

多线程适用于IO密集型
IO密集型工作常由于IO阻塞而导致频繁的线程切换,由于线程切换开销小,所以比进程占优势

多进程适用与CPU密集型

三、进程/线程同步与通信

死锁的必要条件?

互斥:任何时刻只能有一个进程使用一个资源实例(共享资源不可能死锁);
持有并等待:进程至少持有一个资源,并且正在等待其他进程持有的资源;
非抢占:资源只有在使用后自愿放弃,不可剥夺;
循环等待:等待进程的集合中存在循环

死锁处理方法?

  • 方法概述
    死锁预防与死锁避免的区别
    死锁预防(从源头保证不可能发生死锁):是设法至少破坏产生死锁的四个必要条件之一,严格的防止死锁的出现
    死锁避免(本可能发生死锁,但是通过一定方法避免):在系统运行过程中注意避免死锁的最终发生,它不那么严格的限制产生死锁的必要条件的存在,因为即使死锁的必要条件存在,也不一定发生死锁 => 比如银行家算法
    在这里插入图片描述

  • 死锁预防:限制资源申请方式
    在这里插入图片描述
    互斥:把互斥的共享资源封装成可同时使用的资源(互斥在资源内部实现)
    持有并等待:进程请求资源时,要求它不持有任何其他资源;或者仅允许进程在开始执行时间,一次请求所有需要的资源; => 资源利用率低
    非抢占:如果进程请求不能立即分配所有资源,则释放已占有的资源; 只在能够获得所有需要资源时,才执行分配资源操作
    循环等待:对资源排序,要求进程按顺序请求资源

  • 死锁避免
    在这里插入图片描述

管程与条件变量

  • 管程由四部分组成
    1.管程内部的共享变量 => 需要进程同步的根源
    2.管程内部的条件变量 => 每个条件变量对应一个条件,不满足条件的线程将会阻塞在该条件变量上
    3.管程内部并发执行的进程 => 并发进程
    4.对局部于管程内部的共享数据设置初始值的语句;

进程间通信方式(七种,重要!)

  • 1.信号量
    它是同步原语言,不过也可以看做进程间通信(个人见解:同步主要是两个控制流之间,也就是线程之间的同步,如果理解成进程间的同步,更多的针对的是每个进程只有一个线程的情况;)
    注意由于信号量是多个进程/线程都能访问的,所以它必须定义在内核且是全局变量,程序在用户态访问信号量必须通过系统调用!
    …不多解释,信号量的具体实现方式多种多样,可以参考ucore有助于理解…

  • 2.无名管道(pipe)
    以半双工共管道为例(全双工只是多了反方向,本质没有变)
    系统调用:int pipe(int fd[2]);返回两个文件描述符;这时候pipe两端在一个进程中连接
    在这里插入图片描述
    调用pipe()后通常接着调用fork(),结果如下:
    在这里插入图片描述
    最后一个进程关闭一端,从而管道连接了两个进程:
    在这里插入图片描述
    注意
    1.linux命令行中的 | 就是无名管道
    2.无名管道虽然返回了文件描述符,但是在文件系统(磁盘)中并没有真正的数据,数据都在内存中,这里fd对应的只是vnode,而没有磁盘真正的inode(可参考ucore中要求设计的管道)
    3.无名管道在内核空间中创建,使用无名管道不可避免地需要进程用户空间与内核空间之间的数据拷贝
    4.根据以上的创建过程可知,pipe只能用于父子进程或者有共同祖先的进程(因为这个才能共享文件描述符fd)

  • 3.命名管道(FIFO)
    命名管道就是在文件系统(磁盘)中有真实文件的管道,fifo就是一个文件,对它的访问就是open、read、close等;系统调用mkfilo...
    使用举例
    在这里插入图片描述
    注意
    1.FIFO没有父子进程限制,因为是磁盘文件,可以在任意进程间共享

  • 4.消息队列
    是内核中的一个消息链表,有特定的格式,可以实现消息的随机查询(不一定FIFO)…
    注意
    1.此消息队列不同于常用的消息队列框架(比如rabbitMQ等是基于socket的)
    2.消息队列的实现依赖于内核,不可避免地需要进程用户空间与内核空间之间的数据拷贝

  • 5.共享存储
    共享内存
    本质就是将进程A的一段虚拟地址空间和进程B的一段虚拟地址空间映射到同一段物理内存(通过修改页表即可),A、B的虚拟地址空间是用户空间
    共享内存常用的系统调用:int shmget(key_t key, size_t size,int flag);返回值是该共享存储区域的标志符!
    在这里插入图片描述
    共享文件
    原理与共享内存相同,只是这里是将A、B进程的虚拟地址空间映射到同一个文件,系统调用:
    void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);
    注意
    1.共享内存不依赖于内核空间,不需要内核空间与用户空间的数据复制,是最快的一种IPC;
    2.由于是对同一段物理内存的访问,需要进行进程同步,可使用信号量
    3.mmap不只可以映射文件,也可以完成共享内存

  • 6.socket
    用于网络中不同主机之间的进程通信.建立socket连接的过程如下(下图rio_函数来自caspp,不代表linux是这么做的):
    在这里插入图片描述
    图中各函数的作用参考:第三部分—程序间的交互和通信中的11.4
    注意服务端有监听描述符和已连接描述符之分:
    在这里插入图片描述
    监听描述符:通常创建一次,存在于服务器的整个生命周期;
    已连接描述符:每次接受连接请求时都会创建一次,只存在于一个服务器与客户端服务的过程中 => 个人理解:用户进程监听一个端口其实就对应了一个监听描述符;而每次处理用户请求时都会新建一个连接描述符,用于与客户端通信,也就是收在Java后端中,每个线程其实拿到的是已连接描述符…

  • 7.信号
    发送信号内核通过更新目的进程上下文中的某个状态(具体而言是task_struct中的signal字段),达到发送信号的目标
    接收信号发生在目的进程从内核模式返回到用户模式时!!! => 当内核把进程p从内核模式切换到用户模式时(比如系统调用返回或者上下文切换),检查待处理信号的集合(pending),选择值最小的信号,中断原有程序,控制转移到其信号处理程序
    更多细节,参考:第二部分—在系统上运行程序8.5信号

死锁与活锁(TODO)

区分条件变量与锁(TODO)

锁用于互斥
条件变量多用于同步(一个线程等待另一个线程完成某个事情……)

四、IO/文件系统/网络编程等

bufferedIO/unbufferedIO/direct_io

以下内容主要参考自:buffered, unbuffered, direct IO的区别
1.buffered IO(有用户缓冲区)
buffered IO指的是在内核和用户程序之间设置了一层缓冲区(其实是用户空间有一个缓冲区),用来提高IO读写的效率;
当用户读取数据时,用户缓冲区已经将buffer读满,下次读时,可以直接从buffer中读,无需再经过内核和硬盘;
当用户写数据时,写入用户buffer区,定时将这段buffer的数据flush到硬盘上,减少向内核发送write命令的次数;

读取:硬盘--->内核缓冲区--->用户缓冲区--->用户程序
写回:用户程序--->用户缓冲区--->内核缓冲区--->硬盘

2.unbuffered IO(没有用户缓冲区)
与buffered IO 的区别为没有用户缓冲区,需要注意的是,unbuffered IO并不是没有任何缓冲区,内核缓冲区仍然存在;

读取:硬盘--->内核缓冲区--->用户程序
写回:用户程序--->内核缓冲区--->硬盘

Linux内核中存在页高速缓存(page cache),read时,若数据在页高速缓存中,则直接从页高速缓存中读取,若数据不在页高速缓存中,则内核将数据从磁盘上加载到页高速缓存中,然后再从页高速缓存中读取;write时,Linux采用write-back策略,程序执行写操作直接写到缓存中,并不直接将数据同步到磁盘上,而是将页高速缓存中被写入的页面标记为dirty,并且被加入dirty page链表中,然后由一个进程(回写进程)周期性将dirty page链表中的页写回到磁盘。

3.direct IO(没有缓冲区)
direct Io 才是真正的什么缓冲区都没有,直接与硬盘交互;

读取:硬盘--->用户程序
写回:用户程序--->硬盘

direct io是一种不用内核缓存的io,它可以做到直接将用户空间的内存直接写入磁盘或者将磁盘数据直接读到用户空间的缓冲区,这种策略就是不用内核的缓存而使用用户自己设计的缓存,这一般在数据库系统中用到

page cache(todo)

ZUFS

At the 2018 Linux Storage, Filesystem, and Memory-Management Summit (LSFMM), Boaz Harrosh presented his zero-copy user-mode filesystem (ZUFS,用户态和内核之间的零拷贝、快速传输). It is both a filesystem in its own right and a framework similar to FUSE for implementing filesystems in user space. It is geared toward extremely low latency and high performance, particularly for systems using persistent memory.
更多参考:The ZUFS zero-copy filesystem

常规文件的IO是非阻塞的(待完善)

读常规文件是不会阻塞的,不管读多少字节,read一定会在有限的时间内返回。从终端设备或网络读则不一定,如果从终端输入(stdin)的数据没有换行符,调用read读终端设备就会阻塞,如果网络上没有接收到数据包,调用read从网络读就会阻塞;
TODO:既然有DMA,为什么读常规文件还需要持续占用cpu呢?

五种IO模型(重要)

  • 前言
    (下面的内容主要来自UNP)
    IO操作通常可被分为两个阶段:
    1.等待数据准备好,,准备好后再读取到内核缓冲区(来自设备/网络的数据需要先缓存到内核空间);
    2.将内核缓冲区中的数据复制到用户缓冲区
    下面的例子以数据报socket为例…
    注意:UNP中将recvfrom调用后把数据从内核空间拷贝到用户空间的这段时间,认为进程是阻塞的(不过个人有不同的看法…)

  • 1.阻塞式IO
    在这里插入图片描述
    默认情况下,所有套接字都是阻塞的…
    阶段1:recvfrom阻塞,等待数据可用;
    阶段2:完成数据的复制(不过这个阶段UNP说线程是阻塞的),然后中断返回

  • 2.非阻塞式IO
    在这里插入图片描述
    阶段1:如果数据没有准备好,recvfrom调用会立即返回,而不会阻塞;只有不断调用recvfrom,直到数据准备好,这个过程称为轮询
    阶段2:当某次recvfrom调用时,数据已经准备好了,则开始复制数据(不过这个阶段UNP说线程是阻塞的)…

  • 3.IO多路复用
    在这里插入图片描述
    IO多路复用是通过select/poll等函数实现;可以等待多个描述符就绪
    阶段1:线程调用select后会阻塞,直到数据可用才从中断返回 => 本质上select也是轮询,不过它是在内核态轮询,而非阻塞模型是在用户态轮询;select轮询完都没有准备好的描述符,则阻塞,等待下次被调度时接着轮询,直到有准备好的描述符
    阶段2:select返回后,再调用recvfrom完成数据复制的工作(不过这个阶段UNP说线程是阻塞的)

  • 4.信号驱动式IO
    在这里插入图片描述
    阶段1:线程先调用sigaction建立信号处理程序,然后立即返回并继续执行,这个过程不会阻塞
    阶段2:数据准备好后发送信号通知用户线程,然后再调用recvfrom复制数据(不过这个阶段UNP说线程是阻塞的) => 疑问:是谁发送的信号?(对于网络IO而言或许是数据准备好后传输层进程发送的信号)

  • 5.异步IO
    在这里插入图片描述
    第一阶段:用户线程调用AIO接口后直接返回,不会阻塞
    第二阶段:内核完成数据的复制,然后通过信号通知用户线程这个过程用户线程也不会阻塞 => 全程用户线程几乎没有参与,可以做自己的其他事情,直到内核通知它IO操作全部完成

  • 一些个人观点
    UNP认为,前四种IO模型中,真正的IO操作(recvfrom)都将阻塞进程,而POSIX对同步的定义就是(这里对同步的定义甚至与想象中有所差异):导致请求进程阻塞,直到IO操作完成,所以称前四种为同步的
    个人观点
    1.关于第二阶段recvfrom是否会阻塞,按道理说调用recvfrom后进程进入内核态完成数据复制,仍然是属于原来的进程,个人认为没有阻塞一说(最多只能说在内核态复制数据时,可能因为进程调度而被切换/阻塞,但它并非一直都是阻塞的)
    2.个人对同步的理解:如果认为第二阶段没有阻塞,这里说同步更多地是想表达在第二阶段,用户代码必须等待内核代码完成复制数据,而不能做用户的其他事情
    3.关于异步:由于异步模型中,用户进程始终没有阻塞, 那么第二阶段的数据复制工作是谁完成的呢?也许是有专门的内核进程/线程完成这个工作?

零拷贝(sendfile/splice/tee)

1.sendfile
sendfile在两个文件描述符之间直接传递数据,完全在内核中操作,从而避免了内核缓冲区和用户缓冲区之间的数据拷贝,称为零拷贝

ssize_t sendfile(int out_fd, int in_fd,off_t offset, size_t count);

in_fd必须支持类似mmap函数的文件描述符,即它必须指向真实的文件,不能是socket或管道;
out_fd必须是一个socket
=> sendfile专用于网络传输!!!

2.splice
也是用于在两个文件描述符之间移动数据,同样是零拷贝

ssize_t splice(int fd_in,loff_t* off_in, int fd_out, loff_t off_out, size_t len, unsigned int flags);

fd_in对应源文件,若它是管道,则off_in必须为NULL,否则off_in表示从文件的哪个偏移开始拷贝;
fd_out和off_out同理……;
fd_in和fd_out必须至少有一个是管道

3.tee
tee在两个管道描述符之间拷贝数据,也是零拷贝

ssize_t tee(int fd_in, int fd_out, size_t len, unsigned int flags);

select与accept的区别(todo)

select:查看指定fd_set中socket状态,如果fd_set中有套接字准备就绪(触发(读、写或执行)),则会返回,返回值为触发的套接字个数

accept:经过创建套接字socket()绑定bind()以及listen()之后,将监听socket和客户端socket建立一个全新连接,并返回client的socket信息;

判断是否有客户端发起链接请求, 一般用select(),然后accept()。

应该先select,可以测一下超时。

如果先accept,select就没必要了。

软硬链接的区别

参考:“软链接”和“硬链接”的区别
总结如下:
硬链接链接文件和原始文件共用的磁盘inode 和 文件内容,他们的inode编号是一样的,文件内容也是一样的,删除任意一个,另一个仍然存在(因为inode内部有引用计数);
软链接链接文件和原始文件有各自的磁盘inode,原始文件的磁盘inode就是用于索引原文件内容;而链接文件的磁盘inode本质上索引的是另一个文件(也就是链接文件本身,而非原文件),这个文件(链接文件)存储的是原文件的路径!

五、其他

什么是协程?

1.先理解线程的三种实现方式
2.协程就是线程的用户层实现(优点就是切换开销小,因为不需要陷入内核);
3.目前Java是采用的内核线程实现 => 即一个Java线程会映射内核线程,切换需要陷入内核 => 注意:一个Java线程就是一个线程,陷入内核也还是该线程,这里的"映射内核线程"只是意味着该线程(或者说是所有线程)映射了内核空间,可以陷入内核而已…

同步异步/阻塞非阻塞的理解

  • 阻塞与非阻塞
    阻塞与非阻塞是从单个线程/进程的角度来说的,强调单个线程的状态,描述的是线程/进程主动或者被动地放入阻塞/等待队列…
  • 同步异步
    而同步与异步主要从通信的角度出发,强调通信双方的步调是否一致
    同步(synchronous):同步机制是指发送方发送请求后,需要等待接收到接收方发回的响应后,才接着发送下一个请求。所以,发送方和接收方对请求的处理步调是一致的 => 就是不能各做各的
    异步(asynchronous):发送方发出一个请求后,不等待接收方响应这个请求,就继续发送下个请求;接收方处理完成后通知发送方,二者步调不一致 => 就是各做各的
  • 区别与联系
    在很多场合会被混用,不用过于纠结概念
    1.很多人会把同步和阻塞混淆,是因为很多时候同步操作会以阻塞的形式表现出来:不过需要注意,同步也可以是非阻塞实现的,必须Java中CAS+循环实现同步的这种方式…
    很多人也会把异步和非阻塞混淆,因为异步操作一般不涉及线程的阻塞...

信号与中断的区别

  • 概述
    中断:可分为硬件中断(外部中断)和软件中断(内部中断/异常),常见的软件中断有除零错误、int指令等……
    信号:是在软件层次上对中断/异常机制的一种模拟,但是信号!=中断/异常
    信号的实现方式:信号一共有60个左右,进程控制块中有一个字段signal,要给该进程发送信号时,就将signal对应的bit置为1……
    区别
    1. 中断/异常是直接发送给内核的,不需要区分进程;而信号是发送给进程的
    2. 中断/异常处理程序在内核态执行;而信号处理程序在用户态执行
    3. 某些中断/异常发生时,也会给进程发生信号通知这一事件。比如除法零错误、按下ctrl+c时,当前进程也会收到信号;但是信号!=中断,信号处理程序!=中断处理程序
    ……

进程组、会话、控制终端

  • 概述
    多个进程组构成一个会话,建立会话的进程是会话的领导进程,该进程 ID 为会话的 SID。会话中的每个进程组称为一个作业。会话可以有一个进程组称为会话的「前台作业」,其它进程组为「后台作业」
    一个会话可以有一个控制终端,当控制终端有输入和输出时都会传递给前台进程组,比如Ctrl + Z。会话的意义在于能将多个作业通过一个终端控制,一个前台操作,其它后台运行。
    在这里插入图片描述

中断与信号的区别

中断:可分为硬件中断(外部中断)和软件中断(内部中断/异常),常见的软件中断有除零错误、int指令等……
信号:是在软件层次上对中断/异常机制的一种模拟,但是信号!=中断/异常
信号的实现方式:信号一共有60个左右,进程控制块中有一个字段signal,要给该进程发送信号时,就将signal对应的bit置为1……

区别
1.中断/异常是直接发送给内核的,不需要区分进程;而信号是发送给进程的
2. 中断/异常处理程序在内核态执行;而信号处理程序在用户态执行
3. 某些中断/异常发生时,也会给进程发生信号通知这一事件。比如除法零错误、按下ctrl+c、定时器到时,当前进程也会收到信号;但是信号!=中断,信号处理程序!=中断处理程序

驱动、固件、微码

固件
Firmware,是表示运行在非"控制处理器"(指不直接运行操作系统的处理器,例如外设中的处理器,或者被用于bare metal的主处理器的其中一些核)中的程序.这些程序很多时候使用和操作系统所运行的处理器完全不同的指令集.这些程序以二进制形式存在于Linux内核的源代码树中,生成目标系统的时候,通常拷贝在/lib/firmware目录下.当driver对device进行初始化的时候,通过request_firmware()等接口,在一个用户态helper程序的帮助下,可以把指定的firmware加载到内存中,由驱动传输到指定的设备上.
固件一般指运行在Host内非主CPU上的其他部件中的可执行机器码,其可以是裸程序,也可以是操作系统+程序."非主CPU的其他部件",典型比如以太网卡、显卡、光驱、硬盘.这些设备内,都会有一个或者多个嵌入式CPU核心在运行固件从而发挥作用.这些设备所处理的指令逻辑还是比较复杂的,比如解析SCSI指令,用CPU+固件来完成就具有最高的灵活性,虽然也可以将解析工作固化成纯数字电路译码器,但是其灵活性会大大降低,一旦有bug,就无法解决.

微码
而微码这个词,泛指那些比固件的代码逻辑量级更低的代码,比如典型案例:鼠标/键盘内部其实也有一个小CPU,但是由于其运行的代码逻辑太过简单,而且指令集都是私有的,条数很少,所以其硬件相当简单,但是这并不妨碍其仍然属于CPU,只不过是一种MCU(MicroControl Unit,微控制单元).比如鼠标的MCU就负责接收各种按键的信号,并根据信号从ROM中保存的码表提取编码,并通过驱动USB接口控制器将编码数据传送到Host端的USB控制器,这套流程也需要一个极为简单的程序来处理,这个程序就叫做微码.同理,键盘上的MCU也负责接收按键信号,并通过USB控制器传给Host.

驱动
驱动则通常指运行主CPU上的一些负责控制外设的内核模块,理解起来相对比较容易……

总结
所以,固件的量级比微码更大,微码多运行在MCU这种极度轻量级的CPU上,而固件则运行在稍微重一些的CPU上,OS则运行在更加重量级的CPU比如服务器CPU上.它们一个比一个强大,处理的逻辑也是一个比一个复杂.
你可能不知道,DDR SDRAM,NANDFlash颗粒内部,也有MCU,只不过多数产品其运行逻辑已经被写死在了硬件电路中,用纯电路来实现译码,但是不排除有些留有可编程后门的产品,依然采用了微码的方式来运行,此时就可以用私有指令对微码进行升级.
对于小型机、高端存储系统等,工程师们常挂嘴边的"升级微码",其实是说连固件带微码一起升级,因为厂商每次发布新版本时,基本会将固件和微码一起发布,升级的时候也是使用一些预先规定好的流程,将对应的固件和微码一同升级到对应部件上."升级微码"比"升级固件"显得门槛更高,所以为了彰显格调,工程师们,尤其是大厂工程师就选择了后者.

电源管理:C-state、P-state

CPU 电源状态:C-state(CPU Power states) => 主要是CPU空闲时的电源管理
CPU 性能状态:P-state(CPU Performance states) => CPU忙时(即C0状态下)的电源管理

  • C-state
    C-state 有 C0,C1…Cn 多种模式,但只有 C0 是正常工作模式(active),其他方式都是 idle 状态,只是 idle 的程度不同,C后的数越高,CPU睡眠得越深,CPU的功耗被降低得越多,同时需要更多的时间回到 C0 模式
    更多参考:电源管理 ——P-State 和 C-State
  • P-state
    它通过在C0的情况下通过调整电压和频率达到省电的目的,其中P0是最高的电源和性能的状态,P7是最低的状态。操作系统可以单个core控制P-State。
    更多参考:CPU电源管理-P-State

频率缩放(frequency scaling)

CPU在P-state状态下,会通过调节CPU频率以达到省电的目的。频率调节主要有三种模式:ondemand, userspace, performance
ondemand指的是平时以低速方式运行,当系统负载提高时候自动提高频率。以这种模式运行不会因为降频造成性能降低,同时也能节约电能和降低温度。
performance指满速运行,即使系统负载非常低cpu的频率也为最高。则性能很好,但是电量消耗较快,温度也高一些。
userspace应该是指用户指定居中的频率运行。

超线程(hyperthreading)

实现在一个物理核上,提供两个逻辑核。平时htop看到的core数量都是逻辑核
这里所谓的超线程,就是逻辑核(而不是软件意义上的thread),每个逻辑核上可以运行一个thread

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值