关于操作系统:从零开始写-OS-内核-运行-shell

系列目录序篇筹备工作BIOS 启动到实模式GDT 与保护模式虚拟内存初探加载并进入 kernel显示与打印全局描述符表 GDT中断解决虚拟内存欠缺实现堆和 malloc第一个内核线程多线程运行与切换锁与多线程同步进入用户态过程的实现零碎调用简略的文件系统加载可执行程序键盘驱动运行 shellshell 命令行这是本系列最初一篇了,为这个 OS 加一个用户界面 shell,这算是 Linux 编程中最入门的经典教科书我的项目了,网上也能够找到很多小教程。这里也不多浪费时间,仅展现一下它的外围局部: void print_shell() { printf("bash> ");}while (1) { print_shell(); while (1) { int32 c = read_char(); if (c == '\n') { run_program(); break; } else if (c < 128) { printf(c); }}shell 实质上只是一个壳,正如它的名字,它提供一个和用户交互的命令行界面,不停地期待用户输出字符并反馈打印进去;一旦用户按下了回车键,那么示意须要运行之前输出的命令行,这在 run_program 函数里实现: void run_program() { // Parse cmd and get program and args. // .. // (fork + exec) new prgoram. int32 pid = fork(); if (pid < 0) { printf("fork failed"); } else if (pid > 0) { // parent int32 status; wait(pid, &status); } else { // child int32 ret = exec(program, args_index, (char**)args); exit(ret); }}这里首先 parse 用户方才敲回车之前输出的命令行字符串,解析出可执行程序名,以及参数。而后就是经典的 fork + exec 组合,运行这个程序。程序名和参数都会被传递到 exec 零碎调用的处理函数 process_exec,那里会从磁盘上读取该用户可执行文件并执行。这里命令行输出的程序名都很简略,也没有什么门路的概念,因为咱们应用的 naive_fs 只有一层构造,所有文件全在顶层,所以间接用文件名就能够了。 ...

July 26, 2021 · 1 min · jiezi

关于操作系统:从零开始写-OS-内核-键盘驱动

系列目录序篇筹备工作BIOS 启动到实模式GDT 与保护模式虚拟内存初探加载并进入 kernel显示与打印全局描述符表 GDT中断解决虚拟内存欠缺实现堆和 malloc第一个内核线程多线程运行与切换锁与多线程同步进入用户态过程的实现零碎调用简略的文件系统加载可执行程序键盘驱动运行 shell键盘输入这个 kernel 系列我的项目到这里曾经实现了所有基本功能的搭建了,最初两篇算是拓展欠缺,咱们将会退出键盘性能,并在此基础上实现一个 shell 命令行界面。 键盘码键盘的具体原理和实现比拟简短无聊,这里也不想浪费时间解释,感兴趣的话你能够在网上找材料钻研。本篇会尽可能地简单化解决,把很多底层细节省略掉,只关注外围实现原理。 一般来说键盘上你按一个键,而后松开,它会产生两个电信号: 按下键产生的那个信号叫通码(make code),就是接通的意思;松开键产生的那个信号叫断码(break code),就是断开的意思;通码和断码都称为扫描码(scan code),对于键盘来说是厚此薄彼的,就是发送一个信号给主机而已,操作系统接管到这一系列信号之后,则须要将它们翻译成对应的输入字符。一个键有通、断两个码是必要的,比方在用户界面上你能够决定是按下键就打印出字符,还是肯定要按下并松开才打印字符,这在用户感触上是不一样的;再例如某些组合键,Shift + a,零碎间断接管到 Shfit 的通码和 a 的通码,才会翻译成一个 A 的通码,两头不能够有 Shfit 的断码,否则示意 Shift 曾经松开。 中断触发键盘的信号是通过中断来触发的,中断号 33,因而咱们首先为它注册中断 handler: register_interrupt_handler(IRQ1_INT_NUM, &keyboard_interrupt_handler);在 keyboard_interrupt_handler 函数里,会从端口 0x60 读取输出的 scan code,而后将它退出到缓冲区暂存。这里咱们用到了一个环形缓冲区(ring buffer),它是一个容量无限的队列,键盘中断 handler 一直将新输出的 scan code 退出到这个缓冲区尾部,而消费者则从缓冲区头部一直地读取生产 scan code 并翻译成字符。 生产阻塞期待scan code 缓冲队列消费者是函数 read_keyboard_char_impl,它的外围逻辑在函数 process_scancode,它的性能是将读入的 scan code 翻译为字符。不过它的实现细节不用深究,非常干燥简短,就是对着 scan code 码表翻译而已。 int32 read_keyboard_char_impl() { if (queue.size == 0) { return -1; } int32 augchar = process_scancode((int)dequeue()); while (!(KH_HASDATA(augchar) && KH_ISMAKE(augchar))) { if (queue.size == 0) { return -1; } augchar = process_scancode((int)dequeue()); } return KH_GETCHAR(augchar);}如果缓冲区是空的,或者以后的 scan code 不足以翻译成一个无效的字符(例如只读到一个 Shfit 的通码),那么它不会返回无效字符。留神它的退出判断条件: ...

July 26, 2021 · 1 min · jiezi

关于操作系统:进程管理01死锁

死锁是过程僵持的一种状态。 是因为过程推动不当,导致多个过程竞争无限的资源;在无外力的作用下,各个过程永远无奈调配到所须要的资源,进而导致过程无限期地期待。 如下图: 过程 p1 取得文件 F 占用权,而后申请磁带机 T 占用权;但因为磁带机 T 曾经调配给过程 p2,所以 p1 只能阻塞并期待过程 p2 开释磁带机 T。 此时过程 p2 申请文件 F 的占用权,可是文件 F 曾经调配给过程 p1,而过程 p1 又因为申请磁带机 T 而阻塞;因而,过程 p2 无奈取得文件 F 的占用权而阻塞。 这样过程 p1 和 p2 都同时阻塞,都无奈继续执行上来了,而这种无奈继续执行上来导致僵持的状态就是死锁。

July 25, 2021 · 1 min · jiezi

关于操作系统:从零开始写-OS-内核-加载可执行程序

系列目录序篇筹备工作BIOS 启动到实模式GDT 与保护模式虚拟内存初探加载并进入 kernel显示与打印全局描述符表 GDT中断解决虚拟内存欠缺实现堆和 malloc第一个内核线程多线程运行与切换锁与多线程同步进入用户态过程的实现零碎调用简略的文件系统加载可执行程序键盘驱动运行 shellexec 零碎调用有了后面几篇对于零碎调用和文件系统的铺垫,本篇来实现 exec 零碎调用,其实曾经是万事俱备了。exec 的应用想必你应该相熟,它在过程里调用后会读取给定的可执行文件,而后用这个文件里的程序笼罩以后 process 运行,这实际上就是实现了从磁盘上启动运行一个新程序的过程。 筹备用户程序首先咱们须要筹备几个用户程序,并将它们写入上一篇中咱们定制的那个 naive_fs 磁盘镜像中去。我在我的项目里创立了一个 user 目录,并在外面也加了 user/src 目录寄存用户程序的源文件,以及 user/prog 用以寄存编译链接后的可执行二进制。例如咱们能够简略地写一个用户程序: int main(int argc, char** argv) { while (1) {}}它非常简单只是一个死循环。当然你也能够写一个打印性能的程序,不过这里须要先实现打印,留神这是用户态的打印,你必须在让 kernel 提供一个打印性能的零碎调用,比方叫 print,供用户调用。我将这个性能封装在了 src/common/stdio.c 的 printf 函数里,它底层会应用 print 零碎调用。这样相似于 C 规范库,它会被 link 到用户程序二进制里。 例如我写了一个用户程序 hello,外面用到了打印性能: #include "common/common.h"#include "common/stdio.h"#include "syscall/syscall.h"int main(uint32 argc, char* argv[]) { printf("start user app: hello\n"); printf("argc = %d\n", argc); for (uint32 i = 0; i < argc; i++) { printf("argv[%d] = %s\n", i, argv[i]); } return 0;}编译链接生成用户二进制程序后,就能够应用前一篇提到的 disk_image_writer 函数将它们写入磁盘镜像。 ...

July 25, 2021 · 1 min · jiezi

关于操作系统:从零开始写-OS-内核-简单的文件系统

系列目录序篇筹备工作BIOS 启动到实模式GDT 与保护模式虚拟内存初探加载并进入 kernel显示与打印全局描述符表 GDT中断解决虚拟内存欠缺实现堆和 malloc第一个内核线程多线程运行与切换锁与多线程同步进入用户态过程的实现零碎调用简略的文件系统加载可执行程序键盘驱动运行 shell筹备后面几篇中咱们曾经建设起了 process 和零碎调用的框架,并且曾经实现了第一个 fork 零碎调用。到目前为止,所有的 process 和它们的 threads 都是咱们在 kernel 里手动创立,thread 的工作函数也是提前准备好的固定函数,这只是纯正给测试用的。一个真正的 OS 当然须要有能力加载用户提供的程序到 process 中运行,这会用到咱们要实现的第二个零碎调用 exec。 然而在此之前,还有一项筹备工作要做。既然要加载用户程序,那么当然须要从磁盘加载。目前咱们的 kernel 尚不具备和磁盘交互的能力,也没有VFS 中每一个节点的文件或者目录都是形象的,它们都要任何文件系统,本篇就来实现一个非常简单的文件系统。 文件系统文件系统(file system)这个词往往带有二义性,在不同的语境里有不同的含意,所以初学者往往混同。例如咱们常常听到的 windows 零碎的 FAT,NTFS 文件系统,Linux 零碎的 EXT 文件系统,有时候你又会听到 Linux 的虚构文件系统 VFS (Virtual File System)等。 计算机世界里有句话,任何技术问题都能够通过加一个中间层来解决,Linux 的 file system 架构正是完满地体现了这种哲学。你听到的下面的各种术语,都只是分属于整个大 file system 概念下的不同的分层中。 上面咱们别离来看这三层的具体职责。 Virtual File System从顶向下,顶层的 Virtual File System 是 Linux 内核构建进去的一个形象的文件系统,它实际上能够大抵地对应咱们平时看到的零碎中的文件和目录等: bash> ls -l /drwxr-xr-x 2 root root 4096 Jan 13 2019 bindrwxr-xr-x 4 root root 4096 Jan 11 2019 bootdrwxr-xr-x 3 root root 4096 Feb 3 2020 data/usr/bin/cat/home/foo/hello.txt这一层最靠近咱们用户心理概念上的文件系统,但它其实是形象的,因为你并不知道这些文件底下的设施和存储格局,作为用户也并不需要关怀,VFS 屏蔽了这些底层的细节,所以这一层叫 Virtual 文件系统。VFS 从逻辑上看是一个树状构造,顶端是根目录 /,每个节点可能是目录(灰色)或者一般文件(绿色)。 ...

July 25, 2021 · 3 min · jiezi

关于操作系统:从零开始写-OS-内核-系统调用

系列目录序篇筹备工作BIOS 启动到实模式GDT 与保护模式虚拟内存初探加载并进入 kernel显示与打印全局描述符表 GDT中断解决虚拟内存欠缺实现堆和 malloc第一个内核线程多线程运行与切换锁与多线程同步进入用户态过程的实现零碎调用简略的文件系统加载可执行程序键盘驱动运行 shell零碎调用接上一篇过程的实现,本篇将开始真正地创立 process,应用到的正是咱们相熟的 fork 零碎调用,所以首先须要将零碎调用(system call)的框架搭建进去。 system call 的概念不用赘述,它是 kernel 为 user 提供的对外性能接口,是 user 态被动申请调用 kernel 性能的次要形式。既然是从 user 到 kernel 态,那么就须要通过中断的形式触发。仿照 Linux 32-bit 零碎的经典形式,咱们也将会应用 int 0x80 的软中断进入 syscall。 因为 syscall 是给用户应用的,所以它的整个实现包含了两个局部: user 局部:对立的函数接口,底层是通过 int 0x80 触发中断;kernel 局部:相似失常的中断解决;user 接口首先来看 user 局部的实现,留神这部分的代码是编译链接入 user 程序,而不是 kernel 的,它会以相似规范库的模式打包,在前面咱们编写 user 程序时会 link 进去。 本节的代码次要是以下几个文件,依照从顶往下的调用关系: syscall.h 和 syscall.c,这里是用户层接口;syscall_trigger.S,这是中断触发和传参的实现;先看 syscall.c 中的用户层接口,这是用户间接调用的 syscall 函数,就是相似咱们平时 Linux 里用到的: int32 fork();int32 exec(char* path, uint32 argc, char* argv[]);它们的底层调用了由 syscall_trigger.S 提供的 trigger 函数,它们是真正触发 syscall 中断和传递参数的中央。 ...

July 22, 2021 · 5 min · jiezi

关于操作系统:从零开始写-OS-内核-进程的实现

系列目录序篇筹备工作BIOS 启动到实模式GDT 与保护模式虚拟内存初探加载并进入 kernel显示与打印全局描述符表 GDT中断解决虚拟内存欠缺实现堆和 malloc第一个内核线程多线程运行与切换锁与多线程同步进入用户态过程的实现零碎调用简略的文件系统加载可执行程序键盘驱动运行 shell过程 Process在后面几篇中,咱们搭建起了 thread 运行和调度的框架。本篇开始咱们将在 thread 之上,实现过程 process 的治理。 对于线程 thread 和 过程 process 的概念和区别应该是陈词滥调了,这里也不想赘述这些八股文。对于 scroll 这样一个小我的项目的实现来讲,thread 是重点,是骨架,因为 thread 才是工作运行的根本单位;而 process 只是更下层的对一个或多个 threads 的封装,它更多地是负责资源的治理,例如 Linux 零碎中每个 process 治理的内容包含: 虚拟内存;文件描述符;信号机制;......咱们这个我的项目比较简单,不会波及简单文件系统和信号等内容,所以 process 的最主要职责就是对内存的治理,本篇首先定义 process 的构造,而后次要围绕它的两个性能开展: user stack 治理;page 治理;在当前的几个篇章中,将会进一步展现 OS 如何加载并运行一个用户可执行程序,这同时将随同着零碎调用 fork / exec 等性能的实现,这些都是以 process 为对象进行的操作。 process 构造定义 process_struct,即 Linux 里所谓的 pcb (process control block): struct process_struct { uint32 id; char name[32]; enum process_status status; // map { tid -> threads } hash_table_t threads; // allocate user space stack for threads bitmap_t user_thread_stack_indexes; // exit code int32 exit_code; // page directory page_directory_t page_dir;};typedef struct process_struct pcb_t;这里只列出了最重要的一些字段,正文应该写的很分明。对于目前而言,这样一个简略的构造足以满足需要了。 ...

July 22, 2021 · 4 min · jiezi

关于操作系统:从零开始写-OS-内核-进入用户态

系列目录序篇筹备工作BIOS 启动到实模式GDT 与保护模式虚拟内存初探加载并进入 kernel显示与打印全局描述符表 GDT中断解决虚拟内存欠缺实现堆和 malloc第一个内核线程多线程运行与切换锁与多线程同步进入用户态过程的实现一个简略的文件系统加载可执行程序零碎调用的实现键盘驱动运行 shell用户态线程在后面几篇中,咱们曾经启动了 kernel 线程并实现了 multi-threads 调度运行。接下来咱们须要启动用户线程,毕竟这个 OS 是提供给用户应用的,未来的大多数 thread 也将是用户态的。 这里须要清晰一下 user 和 kernel 的 thread / stack 之间的关系,可能有的同学对此不足一个直观的认知。 每一个 user thread 都会有 2 个 stack,别离是 user 空间的 stack 和 kernel 空间的 stack;失常状况下的 user thread,运行在 user stack 上;当产生中断时(包含 interrupt / exception / soft int),执行流跳转到该 thread 的 kernel stack 开始执行中断 handler,执行结束后再返回到 user stack 复原执行; 从 kernel thread 启动须要明确一点,user thread 不是凭空出现的,实质上它依然须要从 kernel thread 开始运行,而后跳转到用户的 code + stack 上运行。所以这里首先回顾一下 kernel thread 启动的过程,在第一个内核线程一篇中具体解说过。这里最外围的工作是对 kernel stack 的初始化,咱们构建了如下图所示的一个 stack: ...

July 20, 2021 · 5 min · jiezi

关于操作系统:进程管理05进程的调度算法

过程调度,就是绪状态的过程取得 CPU 的使用权,过程由就绪状态转变成运行状态。 过程调度能够分为: 抢占式 零碎会依据过程的优先级高下来进行调度,过程之间能够插队非抢占式 零碎依照先来先服务的形式来调度,过程间不能插队 过程调度算法有很多,比拟罕用的调度算法如下: 1、先来先服务 first come first serve (FCFS) 先来的过程先被调度,这种算法很偏心,然而如果执行的是一个长作业,那么前面的短作业将会被长时间搁置,造成短作业饥饿。 2、短作业优先 shortest job first (SJF) 为了防止短作业饥饿的状况,就优先把短作业都执行完,而后再执行长作业。这样又会引发另一个问题,那就是如果短作业十分多,那么容易导致长作业将不会被执行,又会造成长作业饥饿。 3、工夫片轮转 round serve (RS) 工夫片轮转就是轮询,定义一个工夫片的长度,而后均匀给每个过程调配工夫片,一旦工夫片用完,此时作业未执行完,作业就会从运行状态转变成就绪状态,期待被从新调度。如果作业比拟多,那这样容易造成长作业须要轮转良久能力执行完。 4、多级反馈队列 该算法设置了不同的队列,能够分类为高、中、低优先级队列,并且优先级越高,调配的工夫片就越短,反之优先级越低,调配工夫片越长。首先,先进来的作业会进入高优先级;如果没有被执行完,就会压入中优先级队列;如果还没执行完再压入低优先级队列。只有上一个队列的过程被执行完,能力执行以后队列的过程。 这种算法还是无奈无效防止长作业饥饿的状况,因为只有高优先级队列没有过程,才会执行下一级别的队列。如果上一级的队列始终有过程,那么下一级别的队列的过程将会饥饿。 5、高响应比优先 这种算法是在短作业优先调度算法的根底上,加上一个随着工夫累计而叠加的权重机制。 零碎会依据优先级来决定过程执行的先后,但同时为了确保那些等待时间长的过程也能被执行,那么就会联合过程期待的工夫来更新过程的权重。其中等待时间越长,那么权重越高。 这种算法既能够优先实现短作业,又能确保长作业不至于长期饥饿,是一个折中的算法。

July 18, 2021 · 1 min · jiezi

关于操作系统:深入理解计算机系统异常

 古代操作系统通过使控制流产生渐变来对某些意外状况(磁盘读写数据准备就绪、硬件定时器产生信号等)做出反馈。一般而言,咱们把这些渐变命名为异样控制流(Exceptional Contral Flow ECF)。异样控制流产生在计算机系统的各个档次。比方,在硬件层,硬件检测到工夫会触发管制忽然转移到异样处理程序。在操作系统层,内核通过上下文切换将管制从一个用户过程转移到另一个用户过程。在应用层,一个过程能够发送信号到另一个过程,而接受者会将管制转移到一个信号处理程序。一个程序能够通过回避通常的栈规定,并执行到其余函数中任意地位的非本地跳转谬误做出反馈。 为什么须要了解ECF? 有助于了解重要的零碎概念有助于了解应用程序是如何与操作系统交互有助于了解并发有助于了解软件异样如何工作(如C++/JAVA try-cache-throw软件异样机制)异样 异样是异样控制流的一种模式,它一部分由硬件实现,一部分由操作系统实现。 异样就是控制流的一种渐变,用来响应处理器状态中的某些变动。上图中,当处理器状态产生一个重要的变动时,处理器正在执行某个以后指令Icur。在处理器中,状态被编码为不同的位和信号。状态的变动称为事件。事件可能和以后执行的执行间接相干,比方虚拟内存缺页、算术溢出、除以零,也可能和以后指令没有关系,比方零碎定时器产生信号、I/O申请实现等。 在任何状况下,当处理器检测到有事件产生时,它会通过一张叫做异样表(exception table)的跳转表,进行一个间接过程调用。到一个专门波及用来解决这类事件的操作系统子程序(异样处理程序(exception handler))。当异样处理程序实现解决后,依据引起异样的事件类型,会产生以下状况: 从新执行Icur(如产生缺页中断)继续执行I_next(如收到I/O设施信号)终止程序(如收到kill信号)异样解决 零碎中可能的每种类型的异样都调配了一个惟一的非负整数的异样号(exception number)。其中一些号码有处理器的设计者调配,其余号码由操作系统内核(操作系统常驻内存的局部)的设计者调配。前者的示例包含除以零、缺页、内存拜访违例(如segment fault)、断点、算术运算溢出等,后者包含零碎调用和来自内部I/O设施的信号。在系统启动时(重启或加电时),操作系统调配和初始化一张称为异样的跳转表。每个条目k蕴含了异样k的处理程序的跳转地址。异样表的起始地址放在一个叫做异样表基址地址寄存器的特俗cpu寄存器内。 异样相似于过程调用,但仍旧有重要的不同之处: 过程调用时,在跳转到处理程序之前,处理器会将返回地址压入栈中。然而对于不同的异样类型,返回地址可能时以后指令,也可能时下一条指令处理器也会把一些额定的处理器状态压入栈里,在处理程序返回时,从新开始执行被中断的程序须要这些状态如果管制从用户程序转移到内核,那么所有这些我的项目都会压到内核栈中异样处理程序运行在内核模式下,意味着异样处理程序对所有的系统资源都有齐全的拜访权限(问:用户指定的异样处理程序呢?) 当异样处理程序实现后,它通过执行一条非凡的“从中断返回”指令,可选地返回被中断的程序,该指令将适当的状态弹回处理器的管制和数据寄存器中。如果异常中断的是一个用户程序,就将状态复原为用户模式,而后将管制返回给被中断的程序。 异样的类别异样分为4类:中断(interrupt)、陷阱(trap)、故障(fault)、终止(abort): 中断:异步产生,是来自处理器内部的I/O设施的信号的后果(如硬盘数据读取实现)。个别这种信号是内部硬件设施向处理器上的一个引脚发信号,并将异样号(标识了引起中断的设施)放到系统总线,来触发中断。以后指令实现后,处理器留神到中断引脚电压变高,就从系统总线读取异样号,并调用适当的异样处理程序。异样解决实现后,执行下一条指令I_next。陷阱和零碎调用:是无意的异样,是执行一条指令的后果(如执行malloc、读、写文件、fork、execve、exit等),处理器提供一条非凡的“syscall n”(n是零碎调用的编号,操作系统有对应的零碎调用表,表中条目i标识零碎调用i的处理程序地址)来处理器这些零碎调用。中断处理程序执行实现后,将程序切换为用户态,执行下一条指令I_next。运行在用户模式的一般函数只能拜访与调用函数雷同的栈,然而零碎调用运行在内核模式,因而容许一行特权指令,并拜访定义在内核中的栈。故障:由谬误引起,通常可能被故障处理程序修改。当故障产生,处理器将管制转移给故障处理程序。如果故障处理程序可能修改这个谬误,就见管制返回到因而故障的指令,并从新执行它。否处理程序返回到内核中的abort历程,abort会终止引起故障的应用程序。常见的故障如:缺页。终止:不可复原的致命谬误造成后果,通常是一些硬件谬误,如比方DRAM/SRAM为被损坏时产生的奇偶谬误。终止处理程序从不将管制返回给应用程序,而是间接返回到内核的abort历程。linux零碎调用函数先将零碎调用好写入寄存器%rax,而后将参数(如mallo的字节数量)写入寄存器%rdi等,而后调用“syscall”指令来调用零碎调用。过程 过程的经典定义就是一个执行中的程序的实例。零碎中的每个程序都运行在某个过程的上下文中。上下文由程序正确运行所需的状态组成的。这个状态包含寄存在内存中的程序的代码和数据,它的栈、通用目标寄存器的内容、程序计数器、环境变量、曾经关上文件描述符的汇合等。 逻辑控制流 过程是轮流应用处理器的。每个过程执行它的流的一部分,而后被抢占,而后轮到其余过程。对于一个运行在这些过程之一的上下文的程序,它看上去就像是在独占地应用处理器。 并发流 一个逻辑流的执行工夫上与另一个流重叠,称为并发流。这个两个流被称为并发地运行。多个流并发地执行的个别景象被称为并发(concurrency)。一个过程和其余过程轮流地运行的概念称为多任务(multitasking)。一个过程执行它的控制流的一部分的每一个时间段叫做工夫片。 公有地址空间 过程也为每个程序提供了一种假象:如同它独占地应用零碎地址空间。过程为每个程序提供它本人的公有地址空间。一般而言,和这个空间(也就是咱们所说的虚拟地址空间)中某个地址关联的那个内存字节是不能被其余过程读写的。 只管和每个公有地址空间相关联的内存的内容个别是不同的,然而每个这样的空间都有雷同的通用构造。地址空间境地是保留给用户程序的,包含通常的代码、数据、堆和栈段。代码段总是从0x400000开始。地址空间顶部保留给内核(操作系统常驻内存的局部)。地址空间的这个局部蕴含内核在带白继承执行指令时(比方当应用程序执行零碎调用时)应用的代码、数据和栈。 用户模式和内核模式 为了限度一个利用能够执行的指令以及它能够拜访的地址空间范畴,处理器应用某个管制寄存器中的一个模式位来形容过程以后享有的权限:模式位为1标识过程运行在内核模式中,能够执行指令集中的任何指令,并拜访零碎中的任何内存地位。如果没有设置模式位,则标识处于用户模式,不容许执行特权指令(如进行处理器、扭转位模式、发动I/O操作、援用地址空间中内核区的代码和数据)。用户程序必须通过零碎调用拜访内核代码和数据。 过程从用户模式变为内核模式的惟一办法是通过诸如中断、故障或者陷入零碎调用这样的异样。当异样产生,管制传递到异样处理程序,处理器将模式从用户模式变为内核模式。当异样处理程序返回到利用程序代码时,处理器就将模式从内核模式改为用户模式。 linux中的/proc文件系统容许用户模式过程拜访内核构造的内容。/proc文件系统将许多内核数据结构的内容输入为一个用户程序能够读的文本文件的层次结构。 /proc/cpuinfo/proc/$pid/maps等思考: /proc是否存储到磁盘中?如果不是,那它是怎么实现的?实现一个程序,仿照/proc,将以后程序应用过程id、内存应用状况写入到某个文件中。上下文切换 内核为每个过程维持一个上下文(context)。上下文就是内核重新启动一个被抢占的过程所需的状态(通用目标寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈、内核数据结构),比方形容地址空间的页表、蕴含无关以后过程信息的过程表、已关上的文件的描述符等。 零碎调用可能导致上下文切换,如I/O读写。中断也可能引起上下文切换。比方,所有操作系统都有周期性定时器中断的机制,通常为1ms或者10ms。每次产生定时器中断。内核就断定以后过程曾经运行了足够长的工夫,并切换到一个新的过程。 零碎调用错误处理 当unix零碎级函数产生谬误时,它们通常会返回-1,并设置全局变量errno来标识出错。程序用应该总是查看谬误。 if ((pid = fork()) < 0){ // strerror 返回一个文本串,形容了和某个error值相关联的谬误。 fprintf(stderr, "fork error: %s\n", strerror(errno)); exit(0)}过程管制unix零碎提供了大量从C程序操作过程的零碎调用。 pid_t getpid<void>;pid_t getppid<void>;pid_t fork(void);void exit(int status); 新创建的子过程简直但不齐全和父过程雷同。子过程失去与父过程用户及虚拟地址空间雷同的(且独立的)一份正本,包含代码和数据段、堆、共享库以及用户栈。子过程还取得与父过程任何关上文件描述符雷同的正本,象征这子过程能够读写父过程关上的任何文件。后续父子两个过程所做的任何扭转都是独立的,都有本人的公有地址空间,不会反映在另一个过程的内存中。 ...

July 17, 2021 · 3 min · jiezi

关于操作系统:从零开始写-OS-内核-锁与多线程同步

系列目录序篇筹备工作BIOS 启动到实模式GDT 与保护模式虚拟内存初探加载并进入 kernel显示与打印全局描述符表 GDT中断解决虚拟内存欠缺实现堆和 malloc第一个内核线程多线程运行与切换锁与多线程同步进入用户态过程的实现一个简略的文件系统加载可执行程序零碎调用的实现键盘驱动运行 shell多线程竞争上一篇 咱们终于运行起了多线程,并初步建设起了任务调度零碎 scheduler,使这个 kernel 终于开始显现出了一个操作系统应有的风貌。在 multi-threads 运行的根底上,接下来咱们须要进入用户态运行 threads,并且建设过程 process 的概念,加载用户可执行程序。 然而在此之前,有一个重要且危险的问题曾经随同着 multi-threads 的运行而来临,这就是线程的竞争和同步问题。置信你应该有用户态下多线程相干的编程教训,了解 threads 之间竞争同步的问题和起因,以及 lock 的概念和应用。本篇会从 kernel 的视角来扫视和探讨 lock,以及代码实现。须要阐明的是,lock 是一个宏大简单的课题,且在 kernel 和在用户态下的 lock 的应用形式和实现会有很多的不同(当然也有大部分的共通之处),本篇只是我集体浅显的了解和实现,欢送探讨和斧正。 锁 Lockthreads 之间竞争导致的数据问题,我想不用在这里多解释。通过前两篇的 thread 启动运行后,咱们应该能分明地意识到,interrupt 是随时并且在任何指令处都可能会产生的,任何非原子的操作都会导致 threads 之间的数据竞争(race)。 对于咱们当初这个 kernel,其实曾经有很多中央须要加 lock,来爱护对公共数据结构的拜访,例如: page fault 处理函数,调配 physical frame 的 bitmap,显然须要爱护;kheap,所有 threads 都在外面挖内存;scheduler 里的各种工作队列,如 ready_tasks;......在大多数反对多线程的编程语言里,都会有 lock 相干的概念和工具,而作为一个一穷二白的 kernel 我的项目,咱们须要本人实现之。 lock 是一门简单的课题,在平安第一的根底上,设计实现的好坏以及应用形式,会极大地影响零碎的性能。坏的 lock 设计和应用,可能会导致 threads 的不合理调度和 CPU 工夫的大量节约,升高零碎吞吐性能。 接下里咱们从 lock 的底层原理登程,探讨几种常见的 lock 的分类和实现形式,以及它们的应用场景。 ...

July 16, 2021 · 4 min · jiezi

关于操作系统:操作系统实战2启动初始化

一、参考操作系统 学习系列目录——更新ing 设置工作模式与环境(上):建设计算机 二、装置虚拟机(套娃形式)主机是MacOS, 先装置了 Ubuntu虚拟机,然而最终无奈胜利启动 步骤形容ubuntu装置 virtualbox建设虚构电脑生产虚构硬盘装置grub转换虚构硬盘格局装置虚构硬盘启动三、MacOS通过虚拟机运行HelloOS步骤形容VirtualBox装置虚拟机在ubuntu生产虚构硬盘,格式化虚构硬盘,装置GRUB同上将ubuntu创立好的hd.img传送到MacOS在MacOS上转换虚构硬盘格局,装置虚构硬盘同上启动留神: (1) grub.cfg中的root='hd0'如果写错了,启动后,会间接进入到grub命令行,看不到 HelloOS, 然而能够通过ls列出所有的grub分区,抉择正确的root目录,从新生成dvi文件,重启虚拟机

July 13, 2021 · 1 min · jiezi

关于操作系统:进程管理04进程和线程的通讯方式

过程和线程的通信形式都是一样的,一共能够分成两种 1、共享存储 一个过程(线程)将信息存储到一个所有过程(线程)都能够拜访的存储区,那另一个过程(线程)就能够从这个存储区获取信息内容。 2、网络通讯 过程(线程)之间能够通过网络通讯来实现信息替换的目标。

July 11, 2021 · 1 min · jiezi

关于操作系统:操作系统实战1最简单的内核

一、参考操作系统 学习系列目录——更新ing 几行汇编几行C:实现一个最简略的内核

July 7, 2021 · 1 min · jiezi

关于操作系统:操作系统-学习系列目录更新ing

一、操作系统实战学习操作系统实战(1)——最简略的内核

July 7, 2021 · 1 min · jiezi

关于操作系统:进程管理03进程和线程的同步方式

过程和线程的同步形式: 过程和线程的同步形式基本一致,次要就是锁和信号量 1、锁 锁就是利用资源的独占性和互斥性来确保过程(线程)同步的。 简略来说,一个过程(线程)取得了一个资源的锁,相当于这个过程(线程)取得了该资源的使用权,并且回绝被其余过程(线程)占用。不过其余过程(线程)能够获取,然而会被阻塞了,直到资源的锁被被开释,那么能力取得资源的锁。 2、信号量 信号量机制,和锁机制相似。信号量会对资源的数量进行记录,每当过程(线程)申请肯定数量的资源,就会更新闲暇资源的数量;一旦某个过程(线程)申请的资源数量超过闲暇资源数量,那么就会收回资源有余的信号来提醒过程(线程)无奈取得足够资源数量并阻塞,直到有足够数量资源。

July 4, 2021 · 1 min · jiezi

关于操作系统:从零开始写-OS-内核-多线程切换

系列目录序篇筹备工作BIOS 启动到实模式GDT 与保护模式虚拟内存初探加载并进入 kernel显示与打印全局描述符表 GDT中断解决虚拟内存欠缺实现堆和 malloc第一个内核线程多线程切换锁与多线程同步过程的实现进入用户态一个简略的文件系统加载可执行程序零碎调用的实现键盘驱动运行 shell筹备接上一篇,咱们启动了第一个 thread,如同只是让一个一般的函数在一个全新的 stack 上运行起来而已。作为一个真正的操作系统,须要能运行调度多个 threads。创立多个 threads 的过程很简略,问题是如何让它们交替切换运行起来。回到上一篇提到的对于 thread 的两个外围形成因素: 代码stack本篇将进入多线程的世界。在这里会展现,两个 thread 如何实现 stack 的转换,并且在 stack 转换的同时,代码的执行流也主动产生了切换。并且基于 multi-threads 的切换能力,咱们将初步构建出一个调度器 scheduler。 线程切换首先回顾上一篇,thread 启动后的 stack 情况,程序运行在图中浅蓝色局部的 stack 区域内: 如果没有外界触发,程序将始终在这里运行上来,不可能产生 thread 切换,即便在别的中央另外有一个 thread,领有它本人独立的 stack;因而这里须要一个触发者,这就是时钟 interrupt,在中断解决一篇的最初,咱们尝试关上了时钟 interrupt,那里的 handler 只是简略的打印函数。当初咱们须要在外面实现 threads 切换。 构想程序正在上图的 stack 运行,这时产生了时钟 interrupt,CPU 将主动进入中断解决,回顾一下中断解决一篇的内容,此时 stack 会变成这样: 通过 CPU 和 kernel 的一系列压栈操作,程序执行流程大略是这样: isr32 (32 是时钟 interrupt 中断号) --> isr_common_stub --> isr_handler --> timer_callback最终来到 timer_callback 函数: ...

June 29, 2021 · 2 min · jiezi

关于操作系统:从零开始写-OS-内核-第一个-kernel-线程

系列目录序篇筹备工作BIOS 启动到实模式GDT 与保护模式虚拟内存初探加载并进入 kernel显示与打印全局描述符表 GDT中断解决虚拟内存欠缺实现堆和 malloc第一个 kernel 线程多线程运行与切换锁与多线程同步过程的实现进入用户态一个简略的文件系统加载可执行程序零碎调用的实现键盘驱动运行 shell筹备这个我的项目系列到这里差不多到一半了,前半部分的 segment,虚拟内存,中断等,其实能够看作只是筹备工作。是的,咱们花了大量工夫去筹备这些基石工作,以至于到当初,整个所谓的 kernel 如同依然处于一种“静止“状态,可能曾经让你感觉困倦了。从本篇开始,这个 kernel 将会真正地”动“起来,开始搭建一个操作系统应有的外围能力,那就是工作治理。 从用户的角度来讲,操作系统的实质性能就是为他们运行工作,否则就难以称之为操作系统了。这是一个简单的工程,万事开头难,所以作为开始阶段的本篇,会尽可能地简略,从运行一个单线程开始。在前面的篇章中,会逐步进入多线程切换与治理,同步与竞争等问题,以及最终来到更下层的过程治理。 线程thread 和 process 的相干概念应该不须要多解释了,都是陈词滥调。在接下来的行文和代码中,我会将 task 等同于 thread,两者混用,都示意线程;而 process 则是过程。 操作系统调度的对象是 thread,也是接下来须要探讨和实现的外围概念。 兴许 thread 听下来很形象,从实质上来说,它能够归结为以下两个外围因素: 代码 + stack代码管制它工夫维度上的流转,stack 则是它空间维度的依靠,这两者形成了 thread 运行的外围。 所以每个 thread 都有它本人的 stack,例如运行在 kernel 态的一堆 threads,大略是这样的格局: 每个 thread 运行在它本人的 stack 上,而操作系统则负责调度这些 threads 的启停。从实质上说,自从咱们进入 kernel 的 main 函数运行到当初,也能够归为一个 thread,它是一个疏导。再往后,操作系统将创立更多的 threads,并且 CPU 将会在操作系统的管制下,在这些 threads 之间来回跳转切换,其实质就是在这些 threads 各自所属代码(指令)和 stack 上进行跳转切换。 创立 thread本篇代码次要在 src/task/thread.c,仅供参考。 首先建设 thread 构造 task_struct,或者叫 tcb_t,即 task control block: ...

June 28, 2021 · 3 min · jiezi

关于操作系统:进程管理02进程和线程的状态切换

过程的状态切换: 过程次要有五种状态,别离是新建、就绪、运行、阻塞、销毁。如下图: 严格来说,过程还有挂起的状态,次要为了解决内存资源有余的问题。如果终端用户申请、父过程申请、负荷调节都须要应用挂起状态。如下图: 在就绪和阻塞两个状态中,再辨别静止就绪和流动就绪,以及静止阻塞和流动阻塞;静止与活动状态之间通过激活和挂起来互相切换,同时运行状态能够通过挂起来切换成静止就绪状态。 线程的状态切换: 线程是过程的宏观体现,因而线程的状态根本与过程状态统一,切换的条件也是基本一致。如下图:

June 27, 2021 · 1 min · jiezi

关于操作系统:从零开始写-OS-内核-实现堆和-malloc

系列目录序篇筹备工作BIOS 启动到实模式GDT 与保护模式虚拟内存初探加载并进入 kernel显示与打印全局描述符表 GDT中断解决虚拟内存欠缺实现堆和 malloc创立第一个内核线程多线程运行与切换锁与多线程同步过程的实现进入用户态一个简略的文件系统加载可执行程序零碎调用的实现键盘驱动运行 shell黑盒 malloc到目前为止,所有的内存都是动态调配的,这显然不是一种高效的做法,不可能满足后续 kernel 开发的需要,动静分配内存也就是 malloc 是咱们必须要实现的性能。 想必你在以前 C 编程里用过 malloc,晓得它是用来调配一块指定大小的内存返回给你,用完还得手动 free;如果你对它还感到神秘,不晓得它外部到底做了什么事件的,或者说连它调配进去的内存大略在什么中央都不晓得的,甚至连 malloc 这个单词是什么的缩写都不晓得的,那须要检查一下了。 哈别缓和,这个我的项目的意义就在于带你扫视这些底层常识和原理。malloc 确实是咱们平时始终用,但可能从未真正详察过的货色,这一篇将解读并实现一个简略的 malloc 库函数。 堆 heapmalloc 调配的内存是在 堆 heap 上的,这里的 heap 不是数据结构里讲的大顶堆小顶堆那种,它只是一块单纯的内存区域而已,例如在用户空间的 heap 位于 stack 和程序加载区域之间: 而在 kernel 空间要实现 malloc,同样须要在一大块 heap 上进行操作。回到咱们的 kernel 空间,它目前的状态是这样: 前三个 4MB 曾经被占用,从 0xC0C00000 开始是自由空间,咱们无妨就将 kernel 的 heap 空间从这里开始划定,到某处完结,即图中粉红色区域,当前这里就是咱们挖内存的高兴星球。 heap 上的 mallocheap 是一个大池子,malloc 做的事件就是在 heap 里圈地盘挖内存,例如你须要 32 bytes 内存,它就在 heap 上找一段还没有被应用的,长度为 32 bytes 的区域给你,就是这样而已。 ...

June 24, 2021 · 2 min · jiezi

关于操作系统:掌握鸿蒙轻内核静态内存的使用从源码分析开始

摘要:动态内存本质上是一个动态数组,动态内存池内的块大小在初始化时设定,初始化后块大小不可变更。动态内存池由一个管制块和若干雷同大小的内存块形成。管制块位于内存池头部,用于内存块治理。内存块的申请和开释以块大小为粒度。本文分享自华为云社区《鸿蒙轻内核M核源码剖析系列八 动态内存Static Memory》,原文作者:zhushy。 内存治理模块管理系统的内存资源,它是操作系统的外围模块之一,次要包含内存的初始化、调配以及开释。 在零碎运行过程中,内存治理模块通过对内存的申请/开释来治理用户和OS对内存的应用,使内存的利用率和应用效率达到最优,同时最大限度地解决零碎的内存碎片问题。 鸿蒙轻内核的内存治理分为动态内存治理和动态内存治理,提供内存初始化、调配、开释等性能。 动态内存:在动态内存池中调配用户指定大小的内存块。长处:按需分配。毛病:内存池中可能呈现碎片。动态内存:在动态内存池中调配用户初始化时预设(固定)大小的内存块。长处:调配和开释效率高,动态内存池中无碎片。毛病:只能申请到初始化预设大小的内存块,不能按需申请。本文次要剖析鸿蒙轻内核动态内存(Memory Box),后续系列会持续剖析动态内存。动态内存本质上是一个动态数组,动态内存池内的块大小在初始化时设定,初始化后块大小不可变更。动态内存池由一个管制块和若干雷同大小的内存块形成。管制块位于内存池头部,用于内存块治理。内存块的申请和开释以块大小为粒度。 本文通过剖析动态内存模块的源码,帮忙读者把握动态内存的应用。本文中所波及的源码,以OpenHarmony LiteOS-M内核为例,均能够在开源站点https://gitee.com/openharmony... 获取。 接下来,咱们看下动态内存的构造体,动态内存初始化,动态内存罕用操作的源代码。 1、动态内存构造体定义和罕用宏定义1.1 动态内存构造体定义动态内存构造体在文件kernel\include\los_membox.h中定义。源代码如下,⑴处定义的是动态内存节点LOS_MEMBOX_NODE构造体,⑵处定义的动态内存的构造体池信息结构体为LOS_MEMBOX_INFO,,构造体成员的解释见正文局部。 ⑴ typedef struct tagMEMBOX_NODE { struct tagMEMBOX_NODE *pstNext; /**< 动态内存池中闲暇节点指针,指向下一个闲暇节点 */ } LOS_MEMBOX_NODE;⑵ typedef struct LOS_MEMBOX_INFO { UINT32 uwBlkSize; /**< 动态内存池中闲暇节点指针,指向下一个闲暇节点 */ UINT32 uwBlkNum; /**< 动态内存池的内存块总数量 */ UINT32 uwBlkCnt; /**< 动态内存池的已调配的内存块总数量 */ #if (LOSCFG_PLATFORM_EXC == 1) struct LOS_MEMBOX_INFO *nextMemBox; /**< 指向下一个动态内存池 */ #endif LOS_MEMBOX_NODE stFreeList; /**< 动态内存池的闲暇内存块单向链表 */ } LOS_MEMBOX_INFO;对动态内存应用如下示意图进行阐明,对一块动态内存区域,头部是LOS_MEMBOX_INFO信息,接着是各个内存块,每块内存块大小是uwBlkSize,蕴含内存块节点LOS_MEMBOX_NODE和内存块数据区。闲暇内存块节点指向下一块闲暇内存块节点。 1.2 动态内存罕用宏定义动态内存头文件中还提供了一些重要的宏定义。⑴处的LOS_MEMBOX_ALIGNED(memAddr)用于对齐内存地址,⑵处OS_MEMBOX_NEXT(addr, blkSize)依据以后节点内存地址addr和内存块大小blkSize获取下一个内存块的内存地址。⑶处OS_MEMBOX_NODE_HEAD_SIZE示意内存块中节拍板大小,每个内存块蕴含内存节点LOS_MEMBOX_NODE和寄存业务的数据区。⑷处示意动态内存的总大小,蕴含内存池信息结构体占用的大小,和各个内存块占用的大小。 ⑴ #define LOS_MEMBOX_ALIGNED(memAddr) (((UINTPTR)(memAddr) + sizeof(UINTPTR) - 1) & (~(sizeof(UINTPTR) - 1)))⑵ #define OS_MEMBOX_NEXT(addr, blkSize) (LOS_MEMBOX_NODE *)(VOID *)((UINT8 *)(addr) + (blkSize))⑶ #define OS_MEMBOX_NODE_HEAD_SIZE sizeof(LOS_MEMBOX_NODE)⑷ #define LOS_MEMBOX_SIZE(blkSize, blkNum) \ (sizeof(LOS_MEMBOX_INFO) + (LOS_MEMBOX_ALIGNED((blkSize) + OS_MEMBOX_NODE_HEAD_SIZE) * (blkNum)))在文件kernel\src\mm\los_membox.c中也定义了一些宏和内联函数。⑴处定义OS_MEMBOX_MAGIC魔术字,这个32位的魔术字的后8位保护工作编号信息,工作编号位由⑵处的宏定义。⑶处宏定义工作编号的最大值,⑷处的宏从魔术字中提取工作编号信息。 ...

June 21, 2021 · 3 min · jiezi

关于操作系统:进程管理01进程和线程

过程: 过程是零碎资源分配和独立运行的根本单位。 过程的创立、调度、运行和销毁都是由操作系统管制、操作系统会负责对过程进行状态的切换,而这便是对过程的治理。 过程会创立过程,前者称为父过程,后者便是子过程。而子过程也能够创立过程,这样就会造成一颗过程树。如下图: 过程创立之前,会创立过程管制块(PCB),而过程管制块负责存储过程的根本信息(例如:过程的 ID、以后状态以及与其它过程和资源的关系),是过程的惟一标识,与过程同生死。 线程: 线程是系统调度的根本单位,在同一个过程中,能够创立多条线程,而所有的线程能够共享过程调配的资源。 过程与线程的区别: 过程能够说就是一个程序,是一种宏观上的体现;而线程是过程在运行的过程的一个小步骤,是一种宏观上的体现。而过程最初运行的后果便是这些线程执行汇总而成的后果。

June 20, 2021 · 1 min · jiezi

关于操作系统:从零开始写-OS-内核-虚拟内存完善

系列目录序篇筹备工作BIOS 启动到实模式GDT 与保护模式虚拟内存初探加载并进入 kernel显示与打印全局描述符表 GDT中断解决虚拟内存欠缺实现堆和 malloc创立第一个内核线程多线程运行与切换锁与多线程同步过程的实现进入用户态一个简略的文件系统加载可执行程序零碎调用的实现键盘驱动运行 shell开拓虚拟空间在虚拟内存初探一篇中曾经在 loader 阶段初步为 kernel 建设了虚拟内存的框架,包含 page directory,page table 等。在那篇里,咱们在 0xC0000000 以上的 kernel 空间曾经开拓了它前三个 4MB,并且手工指定了它们的性能: 0xC0000000 ~ 0xC0400000:映射初始低 1MB 内存;0xC0400000 ~ 0xC0800000:页表;0xC0800000 ~ 0xC0C00000:kernel 加载; 在这一阶段,所有的内存都是咱们手动布局好的,virtual-to-physical 的映射也是手动调配的,这当然不是长久之计。后续的 virutal 内存将会以一种更灵便的形式动态分配,所映射的 physical 内存也不再是提前调配,而是按需取用,这就须要进行缺页异样(page fault)的解决。 缺页异样page fault 的概念这里不做赘述,咱们在上一篇中断解决的最初尝试了触发一个 page fault,然而它的处理函数只是一个 demo,没有做真正解决 page fault 的问题,当初咱们就来解决它。 page fault 解决的外围问题有两个: 确定产生 page fault 的 virtual 地址,以及异样的类型;调配物理 frame,并建设映射;page fault 详情第一个问题比较简单,咱们间接看代码: void page_fault_handler(isr_params_t params) { // The faulting address is stored in the CR2 register uint32 faulting_address; asm volatile("mov %%cr2, %0" : "=r" (faulting_address)); // The error code gives us details of what happened. // page not present? int present = params.err_code & 0x1; // write operation? int rw = params.err_code & 0x2; // processor was in user-mode? int user_mode = params.err_code & 0x4; // overwritten CPU-reserved bits of page entry? int reserved = params.err_code & 0x8; // caused by an instruction fetch? int id = params.err_code & 0x10; // ...}产生 page fault 的地址,贮存在了 cr2 寄存器中;page fault 的类型以及其它信息,贮存在了 error code 中;还记得 error code 吗? 上一篇中断解决中提到过,对于某些 exception 产生时,CPU 会主动 压入 error code 到 stack 中,记录 exception 的一些信息,page fault 就是这样一种: ...

June 19, 2021 · 3 min · jiezi

关于操作系统:从零开始写-OS-内核-中断处理

系列目录序篇筹备工作BIOS 启动到实模式GDT 与保护模式虚拟内存初探加载并进入 kernel显示与打印全局描述符表 GDT中断解决虚拟内存欠缺实现堆和 malloc创立第一个内核线程多线程运行与切换锁与多线程同步过程的实现进入用户态一个简略的文件系统加载可执行程序零碎调用的实现键盘驱动运行 shell中断中断在 CPU 中扮演着十分重要的角色,对硬件的响应,工作的切换,异样的解决都离不开中断。它既是驱动所有的源能源,又是给咱们带来各种苦楚的万恶之源。 中断相干的问题将会贯通整个 kernel 的开发,它的难点就在于它对代码执行流的打乱,以及它的不可预知性。本篇只是初步搭建起中断解决的框架,前面它会始终如影随形,同时也是考验 kernel 设计实现的试金石。 概念筹备常常有一些对于中断、异样,硬中断,软中断等文字上的概念混同,而且中英文在这些术语的应用也有些不对立。为了了解上的对立,咱们在前面的术语应用上做一个申明: 中断,这个词用作总体概念,即它包含各种类型的中断和异样;而后在中断这个总概念下,做如下分类: 异样(exception):外部中断,它是 CPU 外部执行时遇到的谬误,在英文表述上它有 exception,fault,trap 等几类,咱们个别都统称为 exception;此类问题个别是不可屏蔽,必须被解决的;硬中断(interrupt):内部中断,个别就是其它硬件设施发来的,例如时钟,硬盘,键盘,网卡等,它们能够被屏蔽;软中断(soft int):严格来说这不是中断,因为这是由 int 指令被动触发的,最罕用的就是零碎调用,这是用户被动申请进入 kernel 态的形式;它的解决机制和其它中断是一样的,所以也归在中断里; 前面咱们就用英文单词 exception 来指第一类,即 CPU 外部异样和谬误,这一点没有歧义;而 interrupt 这个单词用来专指第二类,即硬中断,这也是 Intel 文档上的原始用法;至于第三类,能够先疏忽,因为目前咱们还不须要探讨它; 至于中断这个中文词,咱们用来指代包含上述的所有类型,是一个大概念,留神咱们不将它与单词 interrupt 等同。 留神这纯正是我的集体用法和规定,只是为了不便前面术语的表述和了解上的对立。 中断表述符表之所以中断这个词会引起歧义,我想可能是因为所有以上这些货色的处理函数都放在了中断描述符表 IDT(Interrupt Descriptor Table)里治理,导致如同中断超出了 interrupt 这个词自身的领域,把 exception 也给囊括了进来。这也是为什么我想用中文词中断来示意总体概念,而用英文的 interrupt 和 exception 示意它上面的两个子概念。 IDT 表项回到中断描述符表 IDT,它的次要作用就是定义了各种中断 handler 函数,它的每个 entry 的构造定义如下: struct idt_entry_struct { // the lower 16 bits of the handler address uint16 handler_addr_low; // kernel segment selector uint16 sel; // this must always be zero uint8 always0; // attribute flags uint8 attrs; // The upper 16 bits of the handler address uint16 handler_addr_high;} __attribute__((packed));typedef struct idt_entry_struct idt_entry_t;代码链接在 src/interrupt/interrupt.h,对于 IDT 的文档能够参考这里。 ...

June 18, 2021 · 4 min · jiezi

关于操作系统:操作系统1操作系统概述

1、What & WhyWhat(什么是操作系统)?操作系统次要是负责以下3个工作: 治理计算机硬件和软件资源的计算机程序治理配置内存,决定资源供需程序、管制输入输出设施等提供让用户和零碎交互的界面操作系统的品种多种多样,不局限于计算机,从手机到超级计算机,操作系统可简略可简单。不同的设施上,操作系统可向用户出现多种操作伎俩(触屏、鼠标)。常见的操作系统有:linux、windows、MacOs、Android、IOS等 Why(为什么要有操作系统)?人力不能够间接操作计算机硬件设施品种繁多简单,须要对立界面操作系统的繁难性,使得更多人可能应用计算机2、操作系统的基本功能1、操作系统对立治理着计算机资源;2、操作系统实现了对计算机资源的形象; 用户无需面向接口硬件编程;比方IO设施管理软件,向用户提供了读写接口的性能;文件管理软件提供了操作文件接口的性能;3、操作系统提供了用户与计算机之间的接口; 比方图形窗口模式、命令模式、零碎调用模式3、操作系统的相干概念1、并发性2、虚拟性3、虚拟性4、异步性 并发性并行:两个或多个事件能够在同一时刻产生,比方多核处理器同时解决两个不同的程序;并发:两个或者多个事件能够在同一时间距离产生 并行和并发的事实例子: 并行和并发在计算机中的例子: 共享性共享性体现为操作系统中的资源能够供多个并发程序独特应用;这种独特应用的形式称为资源共享 共享性--互斥共享当资源被程序A占用的时候,其余程序想用的话只能期待;只有程序A应用完,其余程序才能够应用该资源;比方当打印机被程序A应用了,程序B想打印的时候只能期待A用完后能力应用。 共享性--同时拜访某种资源在一段时间内并发地被多个程序拜访;这种“同时”是宏观的,从宏观去看该资源能够被同时拜访; 比方程序A和B都想往磁盘写数据,但因为磁盘的悬臂只有一个,那么程序A写入的时候程序B是不能写的。然而因为写数据比拟快,如果在一段时间内去察看它,咱们能够认为它是同时拜访的。 虚拟性虚拟性体现为把一个物理实体转变为若干个逻辑实体物理实体是实在存在的,逻辑实体是虚构的虚构的技术次要有时分复用技术和空分复用技术虚拟性--时分复用技术资源在工夫上进行复用,不同程序并发应用多道程序分时应用计算机的硬件资源,达到进步资源的利用率的成果比方在时间轴上,某单核cpu顺次交替执行,交替应用cpu资源,这就是时分复用的一种。 时分复用技术分为:虚构处理器技术、虚构设施技术 虚构处理器技术: 借助多道程序设计技术为每个程序建设过程,多个过程分时复用处理器。 虚构设施技术:物理设施虚构为多个逻辑设备,每个程序占用一个逻辑设备,多个程序通过逻辑设备并发拜访。 虚拟性--空分复用技术空分复用技术用来实现虚构磁盘、虚拟内存。能够进步资源的利用率,晋升编程效率。 虚构磁盘技术:把一个物理磁盘虚构为多个逻辑磁盘,比方硬盘虚构为C盘、D盘。 虚拟内存:从逻辑上扩充了程序的存储容量,这样程序能够应用比理论内存更大的容量;能够大大的晋升编程效率。 异步性异步性使得在多道程序的环境下容许多个过程并发执行;过程在应用资源时可能须要期待或者放弃;过程的执行并不是零打碎敲的,而是以走走停停的模式推动; 如果过程在运行到某个时刻的时候须要用到某个资源,那么如果这个资源被占用,可能过程就须要进行或者期待,当资源被开释的时候再获取这个资源继续执行。

June 17, 2021 · 1 min · jiezi

关于操作系统:从零开始写-OS-内核-全局描述符表-GDT

系列目录序篇筹备工作BIOS 启动到实模式GDT 与保护模式虚拟内存初探加载并进入 kernel显示与打印全局描述符表 GDT中断解决虚拟内存欠缺实现堆和 malloc创立第一个内核线程多线程运行与切换锁与多线程同步过程的实现进入用户态一个简略的文件系统加载可执行程序零碎调用的实现键盘驱动运行 shell扩大并重载 GDT本篇咱们将在 kernel 中从新定义并扩大全局描述符表 GDT,并再次加载它。本篇的内容也会比较简单,更多的是对 x86 相干手册文档的查阅和相熟。 GDT 在 loader 阶段咱们曾经初步定义并加载过一次,在那里咱们只定义了 kernel 的 code 和 data 段,因为到目前为止,以及在前面相当长的一段时间里,咱们始终处于 kernel 空间中,以 CPU 特权级 0 进行运行。然而作为一个 OS,最终是要运行并治理用户程序的,因而 GDT 中还须要退出用户态的 code 和 data 段。 另外咱们也心愿对后面的 GDT 重新整理一下,毕竟在汇编下比拟凌乱,很多数据结构治理起来不清晰。 GDT 代码GDT 以及 segment 相干的常识,是 x86 体系架构的历史遗留产物,十分令人讨厌。然而 Intel 为了历史兼容,又不得不始终保留这些历史包袱。咱们也不用花太多心理和脑筋在这下面,只有依照文档标准,把该填都填了,该写的都写了,微微带过就能够了。它并不是咱们我的项目的外围局部。 按常规,先给出代码链接,次要源文件是 src/mem/gdt.c。 对于 GDT 的文档,你能够参考这里。 首先咱们须要定义 GDT entry 的数据结构: struct gdt_entry { uint16 limit_low; uint16 base_low; uint8 base_middle; uint8 access; uint8 attributes; uint8 base_high;} __attribute__((packed));typedef struct gdt_entry gdt_entry_t;它对应的是这样一个 64 bit 的构造: ...

June 17, 2021 · 2 min · jiezi

关于操作系统:从零开始写-OS-内核-显示与打印

系列目录序篇筹备工作BIOS 启动到实模式GDT 与保护模式虚拟内存初探加载并进入 kernel显示与打印GDT 和 IDT,中断解决虚拟内存欠缺实现堆和 malloc创立第一个内核线程多线程运行与切换锁与多线程同步过程的实现进入用户态一个简略的文件系统加载可执行程序零碎调用的实现键盘驱动运行 shellkernel 的世界接上一篇 加载并进入 kernel,咱们终于来到了kernel 的大门,本篇开始将正式开展 kernel 阶段的工作。有一个好消息是咱们终于能够开始以 C 语言为主的编程,仿佛能够辞别汇编的汪洋大海了,不过汇编依然会在前面用到,它们都是小规模地呈现,但都处于非常重要的要害节点上。 总的来说,kernel 的次要工作将包含以下几个局部: 建设欠缺的内存管理机制,这次要包含了 virtual memory,以及 heap / kmalloc 的实现;建设多任务管理系统,即 thread / process 的运行和治理;实现简略的硬件驱动,次要是 disk 和 keyboard;实现用户态程序的加载和运行,提供零碎调用(system call);不过在开始之前,咱们须要做一些后期筹备工作,其中很重要的一项就是屏幕显示,毕竟总得能有些看得见摸得着的货色,能力让咱们能继续取得一些正反馈,而且其中 print 相干的函数也是对前面的开发调试至关重要。所以本篇的次要内容就是对屏幕显示的管制,以及打印 string 等性能的开发,相对而言没什么难度,轻松愉快。 VGA 显示按常规,首先给出本篇的代码,次要在 src/monitor/ 目录下。 咱们用到的是 VGA text mode,一种古老的显示模式,它的原理简略来说就是用 32KB 内存来管制一个 25 行 * 80 列 的屏幕终端。这 32KB 内存被映射到了哪里呢? 答案是低 1MB 内存的 0xB800 ~ 0xBFFF 这一段,咱们能够通过拜访并批改这一段内存的值来管制屏幕显示。 当然咱们曾经关上 paging 并进入了 kernel,低 1MB 的内存曾经被映射到了 0xC0000000 以上,所以咱们能够应用 0xC000B800 ~ 0xC000BFFF 来拜访,即图中深蓝色局部。 ...

June 16, 2021 · 2 min · jiezi

关于操作系统:Ubuntu-2004-都有哪些新特性

Ubuntu 19.10 生命周期将在往年 7 月完结,之后一段时间内最大的版本则是 Ubuntu 20.04 LTS,其重大更新和改良将在 2030 年前终止。20.04 是 Ubuntu 的第 8 个 LTS 版本,于2020 年 4 月 23 日公布。一、 回顾Ubuntu 20.04 LTSUbuntu 19.10 生命周期将在往年 7 月完结,之后一段时间内最大的版本则是 Ubuntu 20.04 LTS,其重大更新和改良将在 2030 年前终止。 20.04 是 Ubuntu 的第 8 个 LTS 版本,打算于明年 4 月 23 日公布。Canonical 会为 10 份月公布的版本制订 25 周时间表和 4 月公布的版本制订 27 周时间表来领导 LTS 的倒退,Ubuntu 20.04 的开发生命周期遵循的正是 27 周的公布时间表。 如 Ubuntu wiki 所示:https://wiki.ubuntu.com/FocalFossa/ReleaseSchedule 从这个表中咱们能够看到几个要害节点: 2 月 27 日:个性解冻Ubuntu 团队进行引入新个性、程序包和 API,他们开始专一于修复开发版本中的 bug。3 月 19 日:解冻用户界面此时,用户界面中不会产生其它更新。随后,文档解决也将开始,包含最新的屏幕截图。3 月 26 日:解冻文档字符串实现所有个性和用户界面更新之后,文档中将进行创立和批改字符串。如果在稍后阶段须要更改字符串,则在取得团队批准的状况下才能够增加。4 月 2 日:测试版解冻开发人员在 Ubuntu 正式公布之前体验 Ubuntu 测试版本并提出 bug 或谬误修复倡议。4 月 9 日:内核解冻内核解冻是内核更新的最初期限。4 月 16 日:最终解冻最终解冻是最终版本的倒数第二个阶段,团队确认所有修复。4 月 23 日:最终稳固版本正式推出最终稳固版本。二、Ubuntu 20.04新个性首先是主题变动,Yaru 会有一个从亮到暗的主题变动可选,该主题下的复选框、单选按钮以及滑块和进度条等,都不再呈蓝色或绿色,而以紫色取代之。从绿色切换到紫色,整体上缩小了纷杂的色调,同时也不毁坏 Ubuntu 自身的谐和。 ...

June 13, 2021 · 1 min · jiezi

关于操作系统:从零开始写-OS-内核-加载并进入-kernel

系列目录序篇筹备工作BIOS 启动到实模式GDT 与保护模式虚拟内存初探加载并进入 kernel显示与打印GDT 和 IDT,中断解决虚拟内存欠缺实现堆和 malloc创立第一个内核线程多线程运行与切换锁与多线程同步过程的实现进入用户态一个简略的文件系统加载可执行程序零碎调用的实现键盘驱动运行 shellkernel 磁盘镜像接上一篇 虚拟内存初探,本篇将正式加载并启动 kernel,也就是图中绿色的局部: 当然 kernel 镜像要从磁盘上读取加载,所以这里回顾一张老图,是 disk 和 memory(物理内存)的数据对应关系: 顺便提一下,上图中斜线暗影打问号的局部,就是上一章讲的 kernel page tables,即第一张图的橙色局部,共 256 张占地 1MB。 编写 kernel回到 kernel ,即图中绿色局部,它当初实际上还不存在,所以首先咱们须要实现、编译一个简略的 demo 性质的 kernel。如果对 kernel 是什么还没有概念的同学,可能会问:到底 kernel 长什么样? 答案非常简单:kernel 和你平时用 C 语言写的可执行程序简直没有任何区别,也是从一个 main 函数开始。 上面咱们就实现咱们的第一个 kernel: void main() { while (1) {}}就是这样简略,除了一个 while 循环,没有任何其它货色,但它足以用作咱们这里的 demo。 编译 kernel这里有很多编译参数,例如以 32 位编码,禁用 C 规范库等(这是咱们本人定制的 OS,和 C 规范库不可能兼容)。 gcc -m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector -no-pie -fno-pic -c main.c -o main.o链接 kernel:ld -m elf_i386 -Tlink.ld -o kernel main.o这里会用到一个 link 配置文件 link.ld: ...

June 12, 2021 · 2 min · jiezi

关于操作系统:从零开始写-OS-内核-虚拟内存初探

系列目录序篇筹备工作BIOS 启动到实模式GDT 与保护模式虚拟内存初探加载并进入 kernel显示与打印GDT 和 IDT,中断解决虚拟内存欠缺实现堆和 malloc创立第一个内核线程多线程运行与切换锁与多线程同步过程的实现进入用户态一个简略的文件系统加载可执行程序零碎调用的实现键盘驱动运行 shellkernel 虚拟内存概览接上一篇 GDT 与保护模式,这一篇将是 loader 的重点。首先咱们须要建设 kernel 空间的虚拟内存。如果你对虚拟内存的原理还不相熟,请务必先自学,这里能够提供一个文档供参考。 到目前为止咱们始终在物理内存上操作,确切地说是在 1MB 的低地址空间内操作,这所有都很简略间接。然而接下来 loader 行将为加载 kernel 做筹备,咱们须要在更广大的 4GB 虚拟内存空间上布局数据和代码。 仿照 Linux 零碎,咱们将应用 3GB 以上的高地址空间作为内核空间来发展后续所有工作。例如最根本的,目前的物理低地址 1MB 会被映射到 virtual 地址 0 ~ 1MB 以及 3GB 以上空间 0xC0000000 ~ (0xC0000000 + 1MB) 处: 进入 kernel 当前,对低 1MB 空间的拜访将会应用 0xC0000000 ~ (0xC0000000 + 1MB) 虚拟地址,这里次要包含以后应用的 stack,以及显示器对应的内存映射: 所以 video 内存基地址将从 virtual 地址 0xC00B8000 开始,不过目前不用深究,后续将会在显示与打印一篇中详解。 除了最根本的低 1MB 内存空间,loader 还须要进一步在 0xC0000000 以上的 virtual 空间中开疆拓土,这次要包含两局部: ...

June 11, 2021 · 4 min · jiezi

关于操作系统:从零开始写-OS-内核-GDT-与保护模式

系列目录序篇筹备工作BIOS 启动到实模式GDT 与保护模式加载并进入 Kernel显示与打印GDT 和 IDT,中断解决关上虚拟内存实现堆和 malloc创立第一个内核线程多线程运行与切换锁与多线程同步过程的实现进入用户态一个简略的文件系统加载可执行程序零碎调用的实现键盘驱动运行 shell从 mbr 到 loader接上一篇 BIOS 启动到实模式,这篇开始 loader 的编写。首先回顾一下那张磁盘镜像和内存分布图: 目前只须要关注 1MB 一下的内存散布,次要是黄色 mbr 和蓝色 loader 局部。上一篇中曾经将 mbr 加载到内存,并且程序流通过 mbr 最初一条指令 jmp LOADER_BASE_ADDR (0x8000) 曾经执行到了 loader 的入口处,接下来就须要将 loader 实现。 loader 的工作总的来说, loader 的工作次要有以下几项: 建设 GDT(Global Descriptor Table),初始化内核代码和数据段寄存器(segment registers),率领 CPU 进入保护模式(protection mode);建设 kernel 页目录(page directory)和页表(page tables),关上虚拟内存(virtual memory),进入 paging 模式;加载 kernel 镜像到内存,而后进入到 kernel 代码执行,至此零碎的控制权转交到了 kernel ;能够看到 loader 的工作是比拟多的,并且曾经波及到了x86 体系架构中的一些外围局部,因而为了读懂并实现 loader,你必须做好以下的常识筹备: GDT,段内存寻址,段寄存器,保护模式;虚拟内存,页目录,页表;elf 文件格式,因为 kernel 会被编译链接成该格局的文件;loader 实现依然和之前一样,先给出我的我的项目代码链接 src/boot/loader.S,供你参考。 ...

June 10, 2021 · 2 min · jiezi

关于操作系统:聊聊磁盘-IO

常见的磁盘类型按存储原理的不同,能够把磁盘分为这么几种 HDD 盘:没啥说的,就是平时最常见的机械盘。SSD 盘:用电信号来记录存储数据,而不是磁片。显然进行 I/O 时,这要比机械盘的物理寻址形式快的多。HHD 盘:HDD + SSD 的组合模式。掂量磁盘性能的指标有哪些?有时咱们发现运行在 Linux 服务器上的某个利用响应很慢,你也不晓得是什么起因,然而就是想看看磁盘 I/O 是不是有问题。除了 I/O 还有没有其余的指标参考呢? 答案是 有! 吞吐量: 指的是磁盘每秒解决 I/O 申请总的数据大小。用 TPS 示意,例如 32M/s。IOPS: 每秒解决 I/O 申请的次数。个别咱们在说 IOPS 的时候说的就是磁盘的随机读写性能,这个指标在购买云服务器的时候,厂商会明确的通知你该数据,用来辨别不同云存属性的性能。使用率: 磁盘解决 I/O 的工夫百分比。过高的使用率(80%+)意味着磁盘的 I/O 存在性能瓶颈。响应工夫: 通过零碎调用内核收回 I/O 申请,到内核收到 I/O 响应破费的工夫,包含 I/O 申请处于 I/O 队列中期待的工夫。在查找磁盘瓶颈时,这些指标是咱们须要特地关注的。那么,这些指标怎么看?上面我用一个案例说下。 环境筹备 4G , 4c 虚拟机两台,装置了 docker 环境。 S: 10.10.3.56 C: 10.10.3.55 S:# 下载 redis 官网镜像[root@a ~]# docker pull redis# 这里我本地 /data 目录下有一个自定义的 redis.conf 配置文件,启动时mount 到了容器的 /data 目录[root@a ~]# docker run -itd --name redis -p 6379:6379 -v /data:/data docker.io/redis redis-server /data/redis.conf在另一台虚拟机写脚本程序向 redis 插入数据。 ...

June 7, 2021 · 7 min · jiezi

关于操作系统:从零开始写-OS-内核-BIOS-开始启动到实模式

系列目录序篇筹备工作BIOS 开始启动到实模式进入保护模式加载并进入内核显示与打印GDT 和 IDT,中断解决关上虚拟内存实现堆和 malloc创立第一个内核线程多线程运行与切换锁与多线程同步过程的实现进入用户态一个简略的文件系统加载可执行程序零碎调用的实现键盘驱动运行 shell实现 Boot Loader从这篇开始咱们将进入 boot loader 的编写。网上有一些相似的教程可能跳过了这个阶段,间接为你筹备好了 boot loader,从而你能够间接开始 kernel 的编写,例如之前举荐的 JamesM's kernel development tutorials 就是这样的。不过我还是强烈建议将 boot loader 也本人实现了,尤其是对初学者,起因如下: 它并不艰难,相比于前面的 kernel;有助于你疾速进步汇编能力,这在前面的 C 语言 kernel 编写、调试中依然很重要;boot 裸机运行阶段的的编程有助于你建设起对磁盘、内存、指令和 data 之间的加载、映射关系的正确认识,为前面的内核、可执行程序的加载,以及虚拟内存的建设做好筹备,尤其是如果你感觉对这一块比拟含糊的话;boot 阶段其实会初步搭建起 segment 以及虚拟内存的框架,为后续 kernel 编写打下基础;开机进入 BIOS这是一个经典的问题,就是计算机主板开机上电后到启动,产生了什么? 首先咱们须要晓得开机后 CPU 和内存所处的状态,开机后 CPU 初始模式是实模式,地址宽度为 20 位,即最大地址空间 1MB。这 1MB 空间的划分是固定的,每一块都有规定的用处的,被映射到不同的设施上: BIOS 的工作咱们来看一下开机后产生的事件: 开机后 CPU 的指令寄存器 ip 被强置为地址 0xFFFF0,这一地址被映射到 BIOS 固件上的代码,这就是计算机开机后的第一条指令的地址;CPU 开始执行 BIOS 上的代码,这一部分次要是硬件输入输出设施相干的查看,以及建设一个最后的中断向量表,目前不用深究;BIOS 代码最初阶段的工作,就是查看启动盘上的 mbr 分区,所谓 mbr 分区就是磁盘上的第一个 512B 内容;BIOS 会对这 512B 做一个查看:它的最初2个字节必须是两个 magic number:0x55 和 0xaa,否则它就不是一个非法的启动盘;查看通过后,BIOS 将这 512B 加载到内存 0x7C00 处,到 0x7E00 为止,而后指令跳转到 0x7C00 开始执行;将下面那张表格画成图,去掉烦扰项,只留下咱们关怀的局部: ...

June 7, 2021 · 2 min · jiezi

关于操作系统:Clickhouse-系列-番外-零拷贝

本文将向读者具体阐明第三章中提到的无序存储时,每次读取须要读取 4k 的底层细节。第三章的附录已将向读者阐明了 “这个起因是因为操作系统在读取磁盘时,根据数据局部性原理,会依照页为单位读取,每页的大小默认是 4k。“本番外将向读者由此深刻到一个计算机领域罕用的一个优化——零拷贝技术。 在 linux 零碎中,提供了 3 套 API 供给用执行文件操作: 零碎调用规范 I/Ommap第一种零碎调用,是操作系统对外间接提供的文件 API,提供对文件的字节读写操作。操作系统在其外部实现了页缓存机制,对利用端通明,操作系统依据拜访页状况自行调整缓冲区大小。 第二种规范 I/O,也就是赫赫有名的 <stdio.h>。其通过流的形式实现对文件的操作。开发 stdio 的起因是因为零碎调用的页缓存太大(16K~128K), 而一些简略的利用,并不需要这么大的缓存,同时也因为调用零碎调用波及到 CPU 由用户模型向内核模式的切换,工夫耗费比拟大,因而开发了规范 IO,能够看成是对内核的缓冲。 第三种 mmap,就是所谓的零拷贝了。第二章规范 IO 适宜简略的程序应用,然而对于数据库这样的对性能要求高的程序就会有个出名的毛病。规范 IO 实质是对第一种形式的缓冲,是通过在用户空间复制一份数据实现的,规范 IO 会将第一种零碎调用 read() 生成的数据复制到用户空间一份,后续操作都在用户空间上进行操作,期待适合机会写会内核。此时,数据就产生了两次拷贝:即内核零碎调用 read() 将数据复制到内核空间的内存上,和规范 io 将数据复制到用户空间。这也势必带来了性能损耗,幸好内核提供了 mmap,反对利用将文件地址间接映射到以后过程的内存空间,这样利用就能够间接操作内存,由内核负责将内容同步到磁盘。 大部分数据库都应用 mmap 实现零拷贝,防止规范 IO 呈现的两次拷贝的状况。不过 mmap 将文件内容映射到内存,而操作系统的内存治理单元(MMU)治理内存最小单位是页,因而 mmap 必须依照页的整数倍组织映射大小。这就是第三章计算中呈现 4K 的起因。 应用 mmap 还有一个益处就是,除了多数的一些缺页异样,对 mmap 的读写都在用户空间进行。不会产生零碎调用。此外,操作 mmap 还能够通过 madvise() 零碎调用按需管制内核是否应用预读机制从而管制页缓存的大小。 mmap 是古代应用程序利用十分广的一项技术,kafka 的 commitlog、postgresql 的存储引擎…… 这些出名数据库都在大量利用零拷贝技术。但仍然像我之前强调的那样,应用 mmap 也不是没有毛病,次要毛病是必须依照页的整数倍来组织大小,容易呈现空间节约。因而在解决大文件或者文件大小正好是 pagesize 的整数倍时,应用 mmap 会取得很大的性能晋升。 ...

June 6, 2021 · 1 min · jiezi

关于操作系统:从零开始写-OS-内核-准备工作

系列目录筹备工作从 BIOS 开始启动到实模式进入保护模式加载并进入内核显示与打印GDT 和 IDT,中断解决关上虚拟内存实现堆和 malloc创立第一个内核线程多线程运行与切换锁与多线程同步过程的实现进入用户态一个简略的文件系统加载可执行程序零碎调用的实现键盘驱动运行 shell装置 BochsBochs 是一个硬件模拟器,咱们写的内核将运行在下面,后续所有的开发调试工作也将在这下面进行,所以第一步就是装置它。我的开发环境是 Linux Mint 零碎,你用 Ubuntu 也一样,能够间接装置: sudo apt install bochs要是这种办法不行的话,能够间接去官网下载源码包编译装置,这种办法我也试过是 ok 的。不过在装置过程中会遇到一些第三方依赖的库不全的问题,那就逢山开路遇水搭桥,把依赖都装置上就行,在这里我就不细说了,须要你本人入手想想方法,不会很难的,网上也有一些教程能够参考。 至于其它零碎我没试过,然而编译源码包装置的形式必定是行得通的。 运行 Bochs首先你需一个 Bochs 运行的配置文件 bochsrc.txt,这是我用的: # RAM sizemegs: 32# Change to your Bochs installation pathromimage: file=/usr/share/bochs/BIOS-bochs-latestvgaromimage: file=/usr/share/bochs/VGABIOS-lgpl-latest# Diskboot: diskata0: enabled=1, ioaddr1=0x01f0, ioaddr2=0x03f0, irq=14ata0-master: type=disk, path="scroll.img", mode=flat, cylinders=6, heads=16, spt=63log: bochsout.txtmouse: enabled=0keyboard_mapping: enabled=1, map=/usr/share/bochs/keymaps/x11-pc-us.mapclock: sync=realtimecpu: ips=1000000这里最重要的局部就是 Disk 相干配置,咱们这里用到了一个磁盘,这也是前面的 boot loader 和内核镜像盘,零碎就是用该盘启动。当然这不是真的磁盘。就是一个镜像文件。 其中 path="scroll.img" 就是镜像文件,这里和配置文件 bochsrc.txt 在同一门路下,所以就很简略。名字你轻易起,我的叫 scroll,至于为什么前面再说。当然当初咱们还没有内核镜像文件,你能够轻易找个空文件。 前面的 cylinders,heads,spt 几个都是硬盘硬件相干的一些参数了,什么柱面柱头之类的不用深究。Bochs 有一个命令行工具 bximage,能够帮你确定这个参数,你只须要通知它你的镜像文件会有多大。 ...

June 6, 2021 · 1 min · jiezi

关于操作系统:从零开始写-OS-内核-序篇

这是一个很早就想要做的系列,从零开始写一个小的操作系统,算是致敬以前学校里做的 OS 我的项目,也是心愿能帮忙到对这一块感兴趣的同学。 操作系统是计算机专业的外围学科,但我想即便是很多大学的 CS 本科操作系统专业课,也未必会设置这样规模和难度的我的项目。我是在 CMU 读研时修了它们的本科课程 15-410,这是课程的最大一个我的项目作业。对于很多计算机专业的同学,以及非科班转计算机的同学,尝试去做,哪怕是读懂这样一个我的项目,我想都是大有益处的。 我的项目预期网上也有很多相似的很好的系列教程,我也心愿我的这个系列可能略微带来些不一样的中央,你能够大略地有个心理预期: 这不是一个手把手教学的系列,有些入手的中央须要你本人实际解决,当然我会给出我的 Git 我的项目地址供你参考;我不会过多地贴代码逐行解释,也不会过多地解释一些课本或者手册上的专业知识(我认为你应该晓得,或者有能力自学/查);这是一个偏实际+原理阐释的系列文章,我会尽可能多地用图片,而不是文字;常识筹备为了读懂这个系列,你须要有足够的专业知识筹备,总的来说就是大学操作系统专业课的所有理论知识,不要求精通,但最起码能理解,须要用到的时候晓得怎么去查。这里举荐《深刻了解计算机系统》这本书,如果你的程度曾经笼罩了这本书,那么能够持续了。 再具体一些,心愿你的实践和实际能力能够笼罩以下这些: 扎实的 C 语言能力;还够用的 x86 汇编能力(能够在我的项目中进步);纯熟应用 Linux 零碎,以及 Linux 下的零碎编程教训;gcc,Makefile 等工具的应用;对编译,链接,执行代码的原理有足够的意识;硬件以及 OS 对内存的治理原理,尤其是虚拟内存;程序在操作系统中的加载,执行过程,内存的应用和散布;中断的概念和解决;过程,线程的概念原理;多线程编程,同步与锁的概念;是不是看上去有点多......这确实是对你的根本要求,但还是那句话,不要求纯熟精通,只须要能”理解“,你能够只有一个含糊的实践概念,单薄的实际根底,能够在我的项目中进步这些能力,这其实也是这个我的项目的终极目标所在。 举荐书目和教程仅仅看我这个系列可能未必足够,也不肯定对每个人适宜,所以我举荐一些我看过的书和教程: JamesM's kernel development tutorials这个系列是十分举荐的,作者的重点把握明确,组织构造和代码十分清晰。但这个系列我的项目规模偏小,没有 boot loader 和内核成形后的线程,过程以及零碎调用方面的内容,整体比拟偏 Demo 和教学性质,很适宜初学。 操作系统假相还原这也是很举荐的一本书,与下面的系列相同,这本书从头到尾残缺地实现了一个性能比较完善的内核,规模比拟大,而且作者的解说很粗疏,不厌其烦,甚至我都感觉有些啰嗦,比拟适宜在理论我的项目开发中作为参考和实践手册查问,而不适宜从头到尾通读。 我的这个系列我的项目其实参考并综合了以上两者,取了折中,把规模和难度管制在了我认为比拟正当的水平。 我的项目目录在开始系列之前,咱们首先列一下我的项目的目录清单,前面把这些坑一个个填上。 从 BIOS 开始启动到实模式进入保护模式加载并进入内核显示打印GDT 和 IDT,中断解决关上虚拟内存实现堆和 malloc创立第一个内核线程多线程运行与切换进入用户态过程的实现,加载可执行程序零碎调用的实现

June 5, 2021 · 1 min · jiezi

关于操作系统:内存分段

程序是由若干个逻辑分段组成的,如可由代码分段、数据分段、栈段、堆段组成。不同的段是有不同的属性的,所以就用分段(Segmentation)的模式把这些段分离出来。 分段机制下,虚拟地址和物理地址是如何映射的? 分段机制下的虚拟地址由两局部组成,段抉择子和段内偏移量。 内存分段-寻址的形式段抉择子就保留在段寄存器外面。段抉择子外面最重要的是段号,用作段表的索引。段表外面保留的是这个段的基地址、段的界线和特权等级等。 虚拟地址中的段内偏移量应该位于 0 和段界线之间,如果段内偏移量是非法的,就将段基地址加上段内偏移量失去物理内存地址。 在下面了,晓得了虚拟地址是通过段表与物理地址进行映射的,分段机制会把程序的虚拟地址分成 4 个段,每个段在段表中有一个项,在这一项找到段的基地址,再加上偏移量,于是就能找到物理内存中的地址,如下图: 图片内存分段-虚拟地址与物理地址如果要拜访段 3 中偏移量 500 的虚拟地址,咱们能够计算出物理地址为,段 3 基地址 7000 + 偏移量 500 = 7500。 分段的方法很好,解决了程序自身不须要关怀具体的物理内存地址的问题,但它也有一些不足之处: 第一个就是内存碎片的问题。 第二个就是内存替换的效率低的问题。 接下来,说说为什么会有这两个问题。 咱们先来看看,分段为什么会产生内存碎片的问题? 咱们来看看这样一个例子。假如有 1G 的物理内存,用户执行了多个程序,其中: 游戏占用了 512MB 内存 浏览器占用了 128MB 内存 音乐占用了 256 MB 内存。 这个时候,如果咱们敞开了浏览器,则闲暇内存还有 1024 - 512 - 256 = 256MB。 如果这个 256MB 不是间断的,被分成了两段 128 MB 内存,这就会导致没有空间再关上一个 200MB 的程序。 内存碎片的问题这里的内存碎片的问题共有两处中央: 内部内存碎片,也就是产生了多个不间断的小物理内存,导致新的程序无奈被装载; 外部内存碎片,程序所有的内存都被装载到了物理内存,然而这个程序有局部的内存可能并不是很常应用,这也会导致内存的节约; 针对下面两种内存碎片的问题,解决的形式会有所不同。 解决内部内存碎片的问题就是内存替换。 能够把音乐程序占用的那 256MB 内存写到硬盘上,而后再从硬盘上读回来到内存里。不过再读回的时候,咱们不能装载回原来的地位,而是紧紧跟着那曾经被占用了的 512MB 内存前面。这样就能空缺出间断的 256MB 空间,于是新的 200MB 程序就能够装载进来。 ...

May 25, 2021 · 1 min · jiezi

关于操作系统:OS大作业三实现银行家算法

数据定义:E:Existing Resource 示意以后资源总量A:Available Resource 示意以后可分配资源MaxR:示意每种资源须要的最大需求量C:Current Resource 示意以后已调配的资源R:每种资源的最大可能申请N:以后过程申请的资源数据间的关系:$$R = maxR – C$$$$\sum{C}+ A = E$$ 采纳的数据结构:假如资源品种有RNum种,过程数量共有PNum个 则E、A、N均为大小为RNum的向量,含意为:E[i]:示意第i种资源的资源总量A[i]:示意第i种资源的以后残余资源,即为以后可分配资源N[i]:示意以后提出申请的过程,对第i种资源的申请量 因而,以上三种数据的数据结构均用一维数组示意即可 maxR、C、R为二维矩阵,矩阵的行示意过程的各种资源量,矩阵的列代表不同过程,如下表: maxRCRP1(70,40,60)(20,20,20)(50,20,40)P2(40,40,50)(10,10,10)(30,30,40)P3(50,30,60)(30,30,30)(20,0,30)各数据的定义如下: int E[MAX_RNUM],A[MAX_RNUM],PNum,RNum;int maxR[MAX_PNUM][MAX_RNUM], C[MAX_PNUM][MAX_RNUM], R[MAX_PNUM][MAX_RNUM];设计思路:要在过程调配时防止死锁,采纳如下实现思路:在每次过程分配资源之前,对以后零碎进行死锁检测,判断零碎是否有可能产生死锁,若可能,则回绝此次资源分配。 要进行死锁检测,采纳银行家算法:银行家算法的根本思维是分配资源之前,判断零碎是否是平安的;若是,才调配。它是最具备代表性的防止死锁的算法。根据上述的数据结构,进行如下操作:设过程Pi提出申请N[i],则银行家算法按如下规定进行判断。(1)如果N[i] [j]<= Ri,则转(2);否则,出错。(2)如果N[i] [j]<= A[j],则转(3);否则,期待。(3)零碎试探分配资源,批改相干数据:A[j]-=Ni;Ci+=Ni;Ri-=Ni;(4)零碎执行安全性查看,如平安,则调配成立;否则试探险性调配作废,零碎恢复原状,过程期待。 咱们采纳一个标记数组finished[PNum]来标记过程P[i]是否通过上述试探性调配如果所有过程全副都通过试探性调配,即所有过程都被标记,阐明系统安全,并可依据资源分配的程序,输入此时零碎的安全性序列Order。否则,未被标记的过程(即Finished[i] = false)是可能产生死锁的过程。 因而,只有所有过程都被标记,FinishedNum == PNum时,死锁检测算法能力返回true,否则返回false。 int Safe(FILE * fp){ int FinishedNum = 0,i,Finished[MAX_PNUM] = {0}; int testA[MAX_RNUM]; for(int i = 1;i <= RNum;i++){ testA[i] = A[i]; } int Order[MAX_PNUM] = {0},k = 0; while(FinishedNum != PNum){ for(i = 1;i <= PNum;i++){ if(Finished[i] == 0 && find(R[i], testA)){ for(int j = 1;j <= RNum;j++){ testA[j] += C[i][j]; } Finished[i] = 1; Order[k++] = i;//记录 FinishedNum++; if(FinishedNum == PNum){ //输入Order到文件 fprintf(fp,"Process Safe Order:"); for(i = 0;i < PNum;i++){ fprintf(fp,"%d ",Order[i]); } fprintf(fp,"\n"); break; } break; } } if(i > PNum){ return -1; } } return 1;}若死锁检测为真,则依据以后过程P[i]提出的资源申请N[RNum]进行资源分配,更新数据结构。 代码如下: ...

May 14, 2021 · 4 min · jiezi

关于操作系统:进程管理

前言:Linux过程深入分析,从内核设计方面学习什么是过程程序自身并不是过程,过程是处于执行期的程序以及相干资源的总称。 过程构造工作队列(task_list),存储若干过程描述符过程描述符(task_struct),存储一个过程的所有信息过程调配和寄存过程描述符的调配,通过 slab 分配器调配,能够实现对象复用(预调配和重复使用)过程的寄存,先在内核栈顶压入一个 thread_info,thread_info 存储了指向 task_struct 的指针 过程状态 过程间关系过程间有一个显著的继承关系,所有过程都是 PID=1 init 过程的后辈。每一个过程都有一个父过程,和一个子过程列表。 内存屏障是一组解决指令,用来实现对内存操作的程序限度,底层通过锁 CPU 总线(总线同一时刻只能有一个程序拜访)的形式实现只有通过零碎调用和异样解决接口能力陷入内核拜访执行,或者叫做内核“代替过程执行”

April 28, 2021 · 1 min · jiezi

关于操作系统:操作系统持久化文件系统

文件和目录概念随着工夫的推移,无关存储虚拟化造成了两个要害的形象。第一个是文件(file)。文件就是一个线性字节数组,每个字节都能够读取或写入。每个文件都有某种低级名称,通常是某种数字,用户通常不晓得这个名字。因为历史起因,文件的低级名称通常称为inode号(inode number)。每个文件都有一个与其关联的inode号。 第二个形象是目录(directory)。一个目录,像一个文件一样,也有一个低级名字(即inode号),然而它的内容十分具体:它蕴含一个(用户可读名字,低级名字)对的列表。例如,假如存在一个低级别名称为“10”的文件,它的用户可读的名称为“foo”。“foo”所在的目录因而会有条目(“foo”,“10”),将用户可读名称映射到低级名称。目录中的每个条目都指向文件或其余目录。通过将目录放入其余目录中,用户能够构建任意的目录树(directory tree,或目录层次结构,directory hierarchy),在该目录树下存储所有文件和目录。 创立文件通过调用open()并传入O_CREAT标记,程序能够创立一个新文件。上面是示例代码,用于在当前工作目录中创立名为“foo”的文件。 int fd = open("foo", O_CREAT | O_WRONLY | O_TRUNC);函数open()承受一些不同的标记。在本例中,程序创立文件(O_CREAT),只能写入该文件,因为以(O_WRONLY)这种形式关上,并且如果该文件曾经存在,则首先将其截断为零字节大小,删除所有现有内容(O_TRUNC)。 open()的一个重要方面是它的返回值:文件描述符(file descriptor)。文件描述符只是一个整数,是每个过程公有的,在UNIX零碎中用于拜访文件。因而,一旦文件被关上,如果你有权限的话,就能够应用文件描述符来读取或写入文件。这样来看,一个文件描述符就是一种权限(capability),即一个不通明的句柄,它能够让你执行某些操作。另一种对待文件描述符的办法,是将它作为指向文件类型对象的指针。一旦你有这样的对象,就能够调用其余“办法”来拜访文件,如read()和write()。 读写文件文件胜利关上后,就能够对文件进行读写。read()是读取文件的零碎调用,它的原型如下: size_t read(int fildes, void *buf, size_t nbytes);read()的第一个参数是文件描述符,一个过程能够同时关上多个文件,因而描述符使操作系统可能晓得某个特定的读取援用了哪个文件。第二个参数指向一个用于搁置read()后果的缓冲区。第三个参数是缓冲区的大小。对read()的胜利调用返回它读取的字节数。 零碎调用write()的原型如下: size_t write(int fildes, const void *buf, size_t nbytes);它的作用是把缓冲区buf的前nbytes个字节写入与文件描述符fildes关联的文件中,它返回理论写入的字节数。 扭转文件偏移量有时可能读取或写入文件中的特定偏移量是有用的。例如,如果你在文本文件上构建了索引并利用它来查找特定单词,最终可能会从文件中的某些随机偏移量中读取数据。为此,咱们能够应用lseek()零碎调用。上面是函数原型: off_t lseek(int fildes, off_t offset, int whence);第一个参数是一个文件描述符。第二个参数是偏移量,它将文件偏移量定位到文件中的特定地位。第三个参数,因为历史起因而被称为whence,指定了搜寻的执行形式。 对于每个过程所有关上的文件,操作系统都会跟踪一个“以后”偏移量,这将决定在文件中下一次读取或写入开始的地位。因而,关上文件的形象包含它以后的偏移量,偏移量的更新有两种形式。第一种是当产生N个字节的读或写时,N被增加到以后偏移,因而每次读取或写入都会隐式更新偏移量。第二种是lseek,它显式扭转下面指定的偏移量。 请留神,lseek()调用只是在OS内存中更改一个变量,该变量跟踪特定过程的下一个读取或写入开始的偏移量。调用lseek()与挪动磁盘臂的磁盘的寻道(seek)操作无关,执行I/O时,依据磁头的地位,磁盘可能会也可能不会执行理论的寻道来实现申请。 同步写入大多数状况下,当程序调用write()时,它只是通知文件系统:在未来的某个时刻,将此数据写入长久存储。出于性能的起因,文件系统会将这些写入在内存中缓冲(buffer)一段时间。在稍后的工夫点,才会将写入理论发送到存储设备。 从应用程序的角度来看,写入仿佛很快实现,并且只有在极少数状况下(例如,在write()调用之后但写入磁盘之前,机器解体)数据会失落。然而,有些应用程序须要的不只是这种保障。例如,在数据库管理系统(DBMS)中,常常要求可能强制写入磁盘。 为了反对这些类型的应用程序,大多数文件系统都提供了一些额定的管制API。在UNIX中,提供给应用程序的接口被称为fsync。当过程针对特定文件描述符调用fsync()时,文件系统通过强制将所有脏数据写入磁盘来响应。 文件重命名罕用的Linux命令mv,就应用了零碎调用rename(char old, char new),它只须要两个参数:文件的原来名称和新名称。 rename()调用提供了一个保障:它通常是一个原子(atomic)调用。如果零碎在重命名期间解体,文件将被命名为旧名称或新名称,不会呈现奇怪的中间状态。因而,对于反对某些须要对文件状态进行原子更新的应用程序,rename()十分重要。 获取文件信息除了文件拜访之外,咱们还心愿文件系统可能保留对于它正在存储的每个文件的信息,咱们通常将这些数据称为文件元数据(metadata)。要查看特定文件的元数据,咱们能够应用stat()或fstat()零碎调用。 每个文件系统通常将这种类型的信息保留在一个名为inode的stat构造体中。stat构造体的详细信息如下所示: struct stat { dev_t st_dev; /* ID of device containing file */ ino_t st_ino; /* inode number */ mode_t st_mode; /* protection */ nlink_t st_nlink; /* number of hard links */ uid_t st_uid; /* user ID of owner */ gid_t st_gid; /* group ID of owner */ dev_t st_rdev; /* device ID (if special file) */ off_t st_size; /* total size, in bytes */ blksize_t st_blksize; /* blocksize for filesystem I/O */ blkcnt_t st_blocks; /* number of blocks allocated */ time_t st_atime; /* time of last access */ time_t st_mtime; /* time of last modification */ time_t st_ctime; /* time of last status change */};你能够看到有对于每个文件的大量信息,包含其大小、低级名称(即inode号)、一些所有权信息以及无关何时文件被拜访或批改的一些信息等等。 ...

April 21, 2021 · 2 min · jiezi

关于操作系统:操作系统持久化IO设备

概念零碎架构咱们先来看一个典型计算机系统的架构。其中,CPU通过某种内存总线(memory bus)或互连电缆连贯到零碎内存。图像或者其余高性能I/O设施通过惯例的I/O总线(I/O bus)连贯到零碎,在许多古代零碎中会是PCI或它的衍生模式。更上面是外围总线(peripheral bus),比方SCSI、SATA或者USB。它们将最慢的设施连贯到零碎,包含磁盘、鼠标及其他相似设施。 为什么要用这样的分层架构?起因在于物理布局及造价老本。越快的总线越短,因而高性能的内存总线没有足够的空间连贯太多设施。另外,在工程上高性能总线的造价十分高。所以,零碎的设计采纳了这种分层的形式,这样能够让要求高性能的设施离CPU更近一些,低性能的设施离CPU远一些。将磁盘和其余低速设施连到外围总线的益处很多,最重要的就是能够在外围总线上连贯大量的设施。 规范设施当初来看一个规范设施(不是实在存在的),通过它来帮忙咱们更好地了解设施交互的机制。从图中能够看到一个蕴含两个重要组件的设施。第一局部是向零碎其余局部展示的硬件接口(interface),让系统软件来管制它的操作。因而,所有设施都有本人的特定接口以及典型的交互协定。 第二局部是它的内部结构(internal structure)。这部分蕴含设施展现给零碎的形象接口相干的特定实现,非常简单的设施通常用一个或几个芯片来实现它们的性能。更简单的设施会蕴含简略的CPU、一些通用内存、设施相干的特定芯片,来实现它们的工作。 标准协议在上图中,一个简化的设施接口蕴含3个寄存器:一个状态(status)寄存器,能够读取并查看设施的以后状态;一个命令(command)寄存器,用于告诉设施执行某个具体任务;一个数据(data)寄存器,将数据传给设施或从设施接收数据。通过读写这些寄存器,操作系统能够管制设施的行为。 操作系统与该设施的典型交互协定如下: While (STATUS == BUSY) ; // wait until device is not busy Write data to DATA registerWrite command to COMMAND register (Doing so starts the device and executes the command) While (STATUS == BUSY) ; // wait until device is done with your request该协定蕴含4步。 操作系统通过重复读取状态寄存器,期待设施进入能够接管命令的就绪状态。咱们称之为轮询(polling)设施。操作系统下发数据到数据寄存器。例如,你能够设想如果这是一个磁盘,须要屡次写入操作,将一个磁盘块(比方4KB)传递给设施。操作系统将命令写入命令寄存器;这样设施就晓得数据曾经筹备好了,它应该开始执行命令。操作系统再次通过一直轮询设施,期待并判断设施是否执行实现命令。这个协定的益处是足够简略并且无效,然而难免会有一些低效和不不便。咱们留神到轮询会导致在期待设施执行实现时节约大量CPU工夫,如果此时操作系统能够切换执行下一个就绪过程,就能够大大提高CPU的利用率。 利用中断缩小CPU开销多年前,工程师们创造了咱们目前曾经很常见的中断(interrupt)来缩小CPU开销。有了中断后,CPU 不再须要一直轮询设施,而是向设施收回一个申请,而后就能够让对应过程睡眠,切换执行其余工作。当设施实现了本身操作,会抛出一个硬件中断,引发CPU跳转执行操作系统事后定义好的中断服务例程(Interrupt Service Routine,ISR),或更为简略的中断处理程序(interrupthandler)。中断处理程序是一小段操作系统代码,它会完结之前的申请(比方从设施读取到了数据或者错误码)并且唤醒期待I/O的过程继续执行。 不过,应用中断并非总是最佳计划。如果有一个十分高性能的设施,它解决申请很快:通常在CPU第一次轮询时就能够返回后果。此时如果应用中断,反而会使零碎变慢:切换到其余过程,解决中断,再切换回之前的过程会有肯定的代价。因而,如果设施十分快,那么最好的方法反而是轮询。如果设施比较慢,那么采纳中断更好。如果设施的速度未知,或者时快时慢,能够思考应用混合(hybrid)策略,先尝试轮询一小段时间,如果设施没有实现操作,此时再应用中断。 另一个最好不要应用中断的场景是网络。网络端收到大量数据包,如果每一个包都产生一次中断,那么有可能导致操作系统发生存锁(livelock),即一直解决中断而无奈解决用户层的申请。 还有一个基于中断的优化就是合并(coalescing)。设施在抛出中断之前往往会期待一小段时间,在此期间,其余申请可能很快实现,因而屡次中断能够合并为一次中断抛出,从而升高解决中断的代价。当然,期待太长会减少申请的提早,这是零碎中常见的折中。 利用DMA进行更高效的数据传送标准协议还有一点须要咱们留神。如果应用这种形式,CPU的工夫会节约在向设施传输数据或从设施传出数据的过程中。如何能力拆散这项工作,从而进步CPU的利用率? 解决方案就是应用DMA(Direct Memory Access)。DMA引擎是零碎中的一个非凡设施,它能够协调实现内存和设施间的数据传递,不须要CPU染指。 DMA工作过程如下:为了可能将数据传送给设施,操作系统会通过程序通知DMA引擎数据在内存的地位,要拷贝的大小以及要拷贝到哪个设施。在此之后,操作系统就能够解决其余申请了。当DMA的工作实现后,DMA控制器会抛出一个中断来通知操作系统曾经实现数据传输。 ...

April 20, 2021 · 1 min · jiezi

关于操作系统:操作系统并发常见并发问题与事件并发模型

常见并发问题多年来,钻研人员花了大量的工夫和精力钻研并发编程的缺点。并发缺点有很多常见的模式,从大的方面来说能够分为两类:非死锁缺点和死锁缺点。理解这些模式是写出强壮、正确程序的第一步。 非死锁缺点钻研表明,非死锁问题占了并发问题的大多数。它们是怎么产生的?以及如何修复?咱们次要探讨其中两种:违反原子性(atomicity violation)缺点和违反程序(order violation)缺点。 违反原子性缺点这是一个MySQL中呈现的例子。 1 Thread 1::2 if (thd->proc_info) {3 ...4 fputs(thd->proc_info, ...);5 ...6 }78 Thread 2::9 thd->proc_info = NULL;这个例子中,两个线程都要拜访thd构造中的成员proc_info。第一个线程查看proc_info非空,而后打印出值;第二个线程设置其为空。显然,如果当第一个线程查看之后,在fputs()调用之前被中断,第二个线程把指针置为空;当第一个线程复原执行时,因为援用空指针,会导致程序解体。 正式的违反原子性的定义是:“违反了屡次内存拜访中预期的可串行性(即代码段本意是原子的,但在执行中并没有强制实现原子性)”。 这种问题的修复通常很简略。咱们只有给共享变量的拜访加锁,确保每个线程拜访proc_info字段时,都持有锁。当然,拜访这个构造的所有其余代码,也应该先获取锁。 1 pthread_mutex_t proc_info_lock = PTHREAD_MUTEX_INITIALIZER;23 Thread 1::4 pthread_mutex_lock(&proc_info_lock);5 if (thd->proc_info) {6 ...7 fputs(thd->proc_info, ...);8 ...9 }10 pthread_mutex_unlock(&proc_info_lock);1112 Thread 2::13 pthread_mutex_lock(&proc_info_lock);14 thd->proc_info = NULL;15 pthread_mutex_unlock(&proc_info_lock);违反程序缺点上面是一个简略的例子。 1 Thread 1::2 void init() {3 ...4 mThread = PR_CreateThread(mMain, ...);5 ...6 }78 Thread 2::9 void mMain(...) {10 ...11 mState = mThread->State;12 ...13 }你可能曾经发现,线程2的代码中仿佛假设变量mThread曾经被初始化了。然而,如果线程1并没有率先执行,线程2就可能因为援用空指针解体(假如mThread初始值为空,否则可能会产生更加奇怪的问题,因为线程2中会读到任意的内存地位并援用)。 ...

April 18, 2021 · 2 min · jiezi

关于操作系统:操作系统并发条件变量与信号量

条件变量之前咱们介绍了锁,然而锁并不是并发程序设计中所需的惟一原语。在很多状况下,线程须要查看某一条件(condition)满足之后,才会持续运行。例如,父线程须要查看子线程是否执行结束。这种期待如何实现呢? 注:并发程序有两大需要,一是互斥,二是期待。互斥是因为线程间存在共享数据,期待则是因为线程间存在依赖。 咱们能够尝试用一个共享变量,如图所示。这种解决方案个别能工作,然而效率低下,因为主线程会自旋查看,节约CPU工夫。咱们心愿有某种形式让父线程休眠,直到期待的条件满足(即子线程实现执行)。 1 volatile int done = 0;23 void *child(void *arg) {4 printf("child\n");5 done = 1;6 return NULL;7 }89 int main(int argc, char *argv[]) {10 printf("parent: begin\n");11 pthread_t c;12 Pthread_create(&c, NULL, child, NULL); // create child13 while (done == 0)14 ; // spin15 printf("parent: end\n");16 return 0;17 }定义和应用线程能够应用条件变量(condition variable),来期待一个条件变成真。条件变量是一个显式队列,当某些执行状态(即条件,condition)不满足时,线程能够把本人退出队列,期待该条件。当其余线程扭转了上述状态时,就能够通过在该条件上发送信号唤醒队列中的期待线程,让它们继续执行。 在POSIX库中,要申明一个条件变量,只有像这样写:pthread_cond_t c(留神:还须要适当的初始化)。条件变量有两种相干操作:wait()和signal()。线程要睡眠的时候,调用wait();当线程想唤醒期待在某个条件变量上的睡眠线程时,调用signal()。上面是一个典型示例: 1 int done = 0;2 pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;3 pthread_cond_t c = PTHREAD_COND_INITIALIZER;45 void thr_exit() {6 Pthread_mutex_lock(&m);7 done = 1;8 Pthread_cond_signal(&c);9 Pthread_mutex_unlock(&m);10 }1112 void *child(void *arg) {13 printf("child\n");14 thr_exit();15 return NULL;16 }1718 void thr_join() {19 Pthread_mutex_lock(&m);20 while (done == 0)21 Pthread_cond_wait(&c, &m);22 Pthread_mutex_unlock(&m);23 }2425 int main(int argc, char *argv[]) {26 printf("parent: begin\n");27 pthread_t p;28 Pthread_create(&p, NULL, child, NULL);29 thr_join();30 printf("parent: end\n");31 return 0;32 }wait()调用除了条件变量外还有一个参数,它是一个互斥锁。它假设在wait()调用时,这个互斥锁是已上锁状态。wait()的职责是原子地开释锁,并让调用线程休眠。当线程被唤醒时,它必须从新获取锁,再返回调用者。这样简单的步骤也是为了防止在线程陷入休眠时,产生一些竞态条件。 ...

April 18, 2021 · 7 min · jiezi

关于操作系统:操作系统并发锁

概念通过对并发的介绍,咱们看到了并发编程的一个最根本问题:因为单处理器上的中断(或者多个线程在多处理器上并发执行),一些咱们心愿能原子执行的指令并不能正确运行。锁(lock)就是用来解决这一问题最根本的办法。程序员在源代码中加锁,放在临界区四周,保障临界区可能像单条原子指令一样执行。 锁的根本思维上面是应用锁的一个简略示例: 1 pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;23 Pthread_mutex_lock(&lock); // wrapper for pthread_mutex_lock()4 balance = balance + 1;5 Pthread_mutex_unlock(&lock);锁就是一个变量,这个锁变量保留了锁在某一时刻的状态。它要么是可用的(available,或unlocked,或free),示意没有线程持有锁;要么是被占用的(acquired,或locked,或held),示意有一个线程持有锁,正处于临界区。咱们也能够保留其余的信息,比方持有锁的线程,或申请获取锁的线程队列,但这些信息会暗藏起来,锁的使用者不会发现。 锁个别只反对两个操作:lock()和unlock()。调用lock()尝试获取锁,如果没有其余线程持有锁,该线程会取得锁,进入临界区,这个线程被称为锁的持有者(owner)。如果另外一个线程对雷同的锁变量调用lock(),因为锁被另一线程持有,该调用不会返回。这样,当持有锁的线程在临界区时,其余线程就无奈进入临界区。 锁的持有者一旦调用unlock(),锁就变成可用了。如果没有其余期待线程(即没有其余线程调用过lock()并卡在那里),锁的状态就变成可用了。如果有期待线程,其中一个会(最终)留神到(或收到告诉)锁状态的变动,获取该锁,进入临界区。 如何实现锁显然,咱们须要硬件和操作系统的帮忙来实现一个可用的锁。近些年来,各种计算机体系结构的指令集都减少了一些不同的硬件原语,咱们能够应用它们来实现像锁这样的互斥原语。 在实现锁之前,对于锁是否能工作良好,应该当时设立一些规范。首先是锁是否能实现它的根本工作,即提供互斥(mutual exclusion),是否可能阻止多个线程进入临界区。 第二是公平性(fairness)。当锁可用时,是否每一个竞争线程有偏心的机会抢到锁?是否有竞争锁的线程会饿死(starve),始终无奈取得锁? 最初是性能(performance),也即应用锁之后减少的工夫开销。有几种场景须要思考:一种是只有一个线程抢锁、开释锁的开销如何?另外一种是一个CPU上多个线程竞争,最初一种是多个CPU、多个线程竞争时的性能。 切入点一:管制中断最早提供的互斥解决方案之一,就是在临界区敞开中断。这个解决方案是为单处理器零碎开发的。通过在进入临界区之前敞开中断(应用非凡的硬件指令),能够保障临界区的代码不会被中断,从而原子地执行。完结之后,咱们从新关上中断,程序失常运行。 这个办法的次要长处就是简略,然而毛病很多。首先,这种办法要求咱们容许所有调用线程执行特权操作(关上敞开中断),然而恶意程序可能会利用这点。例如一个恶意程序可能在它开始时就调用lock(),从而独占处理器。零碎无奈从新取得管制,只能重启零碎。 第二,这种计划不反对多处理器。如果多个线程运行在不同的CPU上,每个线程都试图进入同一个临界区,敞开中断也没有作用。 第三,敞开中断导致中断失落,可能会导致重大的零碎问题。如果磁盘设施实现了读取申请,但CPU因为敞开了中断错失了这一信号,操作系统如何晓得去唤醒期待的过程? 最初一个不太重要的起因就是效率低。与失常指令执行相比,古代CPU对于敞开和关上中断的代码执行得较慢。 基于以上起因,只在很无限的状况下用敞开中断来实现互斥原语。 切入点二:测试并设置指令因为敞开中断的办法无奈工作在多处理器上,所以零碎设计者开始让硬件反对锁。最简略的硬件反对是测试并设置指令(test-and-set instruction),也叫作原子替换(atomic exchange)。测试并设置指令的工作大抵能够用上面的C代码来定义: 1 int TestAndSet(int *old_ptr, int new) {2 int old = *old_ptr; // fetch old value at old_ptr3 *old_ptr = new; // store 'new' into old_ptr4 return old; // return the old value5 }它返回old_ptr指向的旧值,同时更新为new的新值。当然,要害是这些代码是原子地(atomically)执行。因为既能够测试旧值,又能够设置新值,所以咱们把这条指令叫作“测试并设置”。 为了了解该指令如何结构一个可用的锁,咱们首先尝试实现一个不依赖它的锁。 失败的尝试在第一次尝试中,想法很简略:用一个变量来标记锁是否被某些线程占用。第一个线程进入临界区,调用lock(),查看标记是否为1,而后设置标记为1,表明线程持有该锁。完结临界区时,线程调用unlock(),革除标记,示意锁未被持有。 ...

April 15, 2021 · 3 min · jiezi

关于操作系统:操作系统虚拟化内存分段

介绍利用基址和界线寄存器,操作系统很容易将不同过程重定位到不同的物理内存区域。然而,对于这些内存区域,栈和堆之间,有一大块“闲暇”空间。栈和堆之间的空间并没有被过程应用,却仍然占用了理论的物理内存。因而,简略的通过基址寄存器和界线寄存器实现的虚拟内存很节约。 泛化的基址/界线为了解决这个问题,分段(segmentation)的概念应运而生。这个想法很简略,在MMU中引入不止一个基址和界线寄存器对,而是给地址空间内的每个逻辑段(segment)一对。一个段只是地址空间里的一个间断定长的区域,在典型的地址空间里有3个逻辑不同的段:代码、栈和堆。分段的机制使得操作系统可能将不同的段放到不同的物理内存区域,从而防止了虚拟地址空间中的未应用局部占用物理内存。 咱们来看一个例子,如图所示,64KB的物理内存中搁置了3个段。 你会想到,须要MMU中的硬件构造来反对分段:在这种状况下,须要一组3对基址和界线寄存器。下表展现了下面的例子中的寄存器值,每个界线寄存器记录了一个段的大小。 援用的是哪个段硬件在地址转换时应用段寄存器。它如何晓得段内的偏移量,以及地址援用了哪个段? 一种常见的形式,有时称为显式(explicit)形式,就是用虚拟地址的结尾几位来标识不同的段。在咱们之前的例子中,有3个段,因而须要两位来标识。如果咱们用14位虚拟地址的前两位来标识,那么虚拟地址如下所示: 前两位通知硬件咱们援用哪个段,剩下的12位是段内偏移。因而,硬件就用前两位来决定应用哪个段寄存器,而后用后12位作为段内偏移。偏移量与基址寄存器相加,硬件就失去了最终的物理地址。请留神,偏移量也简化了对段边界的判断。咱们只有查看偏移量是否小于界线,大于界线的为非法地址。 下面应用两位来辨别段,但理论只有3个段(代码、堆、栈),因而有一个段的地址空间被节约。因而有些零碎中会将堆和栈当作同一个段,因而只须要一位来做标识。 硬件还有其余办法来决定特定地址在哪个段。在隐式(implicit)形式中,硬件通过地址产生的形式来确定段。例如,如果地址由程序计数器产生(即它是指令获取),那么地址在代码段。如果基于栈或基址指针,它肯定在栈段。其余地址则在堆段。 栈的问题栈和其余的内存段有一点要害区别,就是它反向增长,因而地址转换必须有所不同。首先,咱们须要一点硬件反对。除了基址和界线外,硬件还须要晓得段的增长方向(用一位辨别,比方1代表自小而大增长,0反之)。 反对共享随着分段机制的不断改进,人们很快意识到,通过再多一点的硬件反对,就能实现新的效率晋升。具体来说,要节俭内存,有时候在地址空间之间共享(share)某些内存段是有用的。 为了反对共享,须要一些额定的硬件反对,这就是爱护位(protection bit)。基本上就是为每个段减少了几个位,标识程序是否可能读写该段,或执行其中的代码。通过将代码段标记为只读,同样的代码能够被多个过程共享,而不必放心毁坏隔离。尽管每个过程都认为本人独占这块内存,但操作系统机密地共享了内存,过程不能批改这些内存,所以假象得以放弃。 有了爱护位,后面形容的硬件算法也必须扭转。除了查看虚拟地址是否越界,硬件还须要查看特定拜访是否容许。如果用户过程试图写入只读段,或从非执行段执行指令,硬件会触发异样,让操作系统来解决出错过程。 操作系统反对分段也带来了一些新的问题。第一个是老问题:操作系统在上下文切换时应该做什么?答案不言而喻:各个段寄存器中的内容必须保留和复原。每个过程都有本人独立的虚拟地址空间,操作系统必须在过程运行前,确保这些寄存器被正确地赋值。 第二个问题更重要,即治理物理内存的闲暇空间。新的地址空间被创立时,操作系统须要在物理内存中为它的段找到空间。之前,咱们假如所有的地址空间大小雷同,物理内存能够被认为是一些槽块,过程能够放进去。当初,每个过程都有一些段,每个段的大小也可能不同。 个别会遇到的问题是,物理内存很快充斥了许多闲暇空间的小洞,因此很难调配给新的段,或扩充已有的段。这种问题被称为内部碎片(external fragmentation)[R69],如图所示。 该问题的一种解决方案是紧凑(compact)物理内存,重新安排原有的段。例如,操作系统先终止运行的过程,将它们的数据复制到间断的内存区域中去,扭转它们的段寄存器中的值,指向新的物理地址,从而失去了足够大的间断闲暇空间。然而,内存紧凑老本很高,因为拷贝段是内存密集型的,个别会占用大量的处理器工夫。 一种更简略的做法是利用闲暇列表治理算法,试图保留大的内存块用于调配。相干的算法可能有成千盈百种,包含传统的最优匹配(best-fit,从闲暇链表中找最靠近须要调配空间的闲暇块返回)、最坏匹配(worst-fit)、首次匹配(first-fit)以及像搭档算法(buddy algorithm)这样更简单的算法。但遗憾的是,无论算法如许精妙,都无奈齐全打消内部碎片,因而,好的算法只是试图减小它。 闲暇空间治理咱们暂且将对虚拟内存的探讨放在一边,先来探讨闲暇空间治理(free-space management)的一些问题。在操作系统用分段(segmentation)的形式实现虚拟内存时,以及在用户级的内存调配库(如malloc()和free())中,须要治理的闲暇空间由大小不同的单元形成。在这两种状况下,会呈现内部碎片的问题,让治理变得较为艰难。 假如首先,咱们假设根本的接口就像malloc()和free()提供的那样。具体来说,void * malloc(size t size)须要一个参数size,它是应用程序申请的字节数。函数返回一个指针,指向这样大小的一块空间。对应的函数void free(void *ptr)函数承受一个指针,开释对应的内存块。 进一步假如,咱们次要关怀的是内部碎片的问题,先将外部碎片问题置之脑后。咱们还假如,内存一旦被调配给客户,就不能够被重定位到其余地位。最初咱们假如,分配程序所治理的是间断的一块字节区域。 底层机制分隔与合并闲暇列表蕴含一组元素,记录了堆中的哪些空间还没有调配。假如有上面的30字节的堆: 这个堆对应的闲暇列表如下: 能够看出,任何大于10字节的调配申请都会失败(返回NULL),因为没有足够的间断可用空间。然而,如果申请小于10字节空间,会产生什么?假如咱们只申请一个字节的内存,此时分配程序会执行所谓的宰割(splitting)动作:它找到一块能够满足申请的闲暇空间,将其宰割,第一块返回给用户,第二块留在闲暇列表中。在咱们的例子中,假如这时遇到申请一个字节的申请,分配程序抉择应用第二块闲暇空间,对malloc()的调用会返回20(1字节调配区域的地址),闲暇列表会变成这样: 许多分配程序中也有一种机制,名为合并(coalescing)。如果应用程序调用free(10),偿还堆两头的空间,会产生什么?分配程序不只是简略地将这块闲暇空间退出闲暇列表,还会在开释一块内存时合并可用空间。通过合并,最初闲暇列表应该像这样: 追踪已调配空间的大小能够看到,free(void *ptr)接口没有块大小的参数。要实现这个工作,大多数分配程序都会在头构造(header)中保留一点额定的信息,它在内存中的地位通常就在返回的内存块之前。该头块中至多蕴含所调配空间的大小,它也可能蕴含一些额定的指针来减速空间开释,蕴含一个幻数来提供完整性检查,以及其余信息。咱们假设,一个简略的头块蕴含了调配空间的大小和一个幻数: typedef struct header_t { int size; int magic;} header_t;在内存中看起来像是这样: 用户调用free(ptr)时,库会通过简略的指针运算失去头块的地位。取得头块的指针后,库能够很容易地确定幻数是否合乎预期的值,作为正常性查看(assert(hptr->magic == 1234567)),并简略计算要开释的空间大小(即头块的大小加区域长度)。因而,如果用户申请N字节的内存,库不是寻找大小为N的闲暇块,而是寻找N加上头块大小的闲暇块。 让堆增长大多数传统的分配程序会从很小的堆开始,当空间耗尽时,再向操作系统申请更大的空间。通常,这意味着它们进行了某种零碎调用(例如,大多数UNIX零碎中的sbrk),让堆增长。操作系统在执行sbrk零碎调用时,会找到闲暇的物理内存页,将它们映射到申请过程的地址空间中去,并返回新的堆的开端地址。这时,就有了更大的堆,申请就能够胜利满足。 根本策略现实的分配程序能够同时保障疾速和碎片最小化。遗憾的是,因为调配及开释的申请序列是任意的,任何策略在某些特定的输出下都会变得十分差。 最优匹配最优匹配(best fit)策略非常简单:首先遍历整个闲暇列表,找到和申请大小一样或更大的闲暇块,而后返回这组候选者中最小的一块。最优匹配背地的想法很简略:抉择最靠近用户申请大小的块,从而尽量避免空间节约。然而,简略的实现在遍历查找正确的闲暇块时,要付出较高的性能代价。 最差匹配最差匹配(worst fit)办法与最优匹配相同,它尝试找最大的闲暇块,宰割并满足用户需要后,将残余的块退出闲暇列表。最差匹配尝试在闲暇列表中保留较大的块,而不是像最优匹配那样可能剩下很多难以利用的小块。最差匹配同样须要遍历整个闲暇列表,大多数钻研表明它的体现十分差,导致适量的碎片,同时还有很高的开销。 首次匹配首次匹配(first fit)策略就是找到第一个足够大的块,将申请的空间返回给用户。首次匹配有速度劣势(不须要遍历所有闲暇块),但有时会让闲暇列表结尾的局部有很多小块。因而,分配程序如何治理闲暇列表的程序就变得很重要。一种形式是基于地址排序(address-basedordering),通过放弃闲暇块按内存地址有序,合并操作会很容易,从而缩小了内存碎片。 下次匹配不同于首次匹配每次都从列表的开始查找,下次匹配(next fit)算法多保护一个指针,指向上一次查找完结的地位。其想法是将对闲暇空间的查找操作扩散到整个列表中去,防止对列表结尾频繁的宰割。这种策略的性能与首次匹配很靠近,同样防止了遍历查找。 其余形式除了上述根本策略外,人们还提出了许多技术和算法,来改良内存调配。 ...

April 11, 2021 · 1 min · jiezi

关于操作系统:操作系统虚拟化内存空间和地址转换

形象:地址空间晚期零碎从内存来看,晚期的机器并没有提供多少形象给用户。基本上,机器的物理内存看起来如图所示。 操作系统已经是一组函数(实际上是一个库),在内存中(在本例中,从物理地址0开始),而后有一个正在运行的程序(过程),目前在物理内存中(在本例中,从物理地址64KB开始),并应用残余的内存。 多道程序和时候共享过了一段时间,因为机器低廉,人们开始更无效地共享机器。因而,多道程序零碎和分时系统别离开启了。 在下图中,有3个过程(A、B、C),每个过程领有从512KB物理内存中切出来给它们的一小部分内存。假设只有一个CPU,操作系统抉择运行其中一个过程(比方A),同时其余过程(B和C)则在队列中期待运行。 随着时候共享变得风行,人们对操作系统又有了新的要求。特地是多个程序同时驻留在内存中,使爱护(protection)成为重要问题。 地址空间为了解决这些问题,操作系统须要提供一个易用(easy to use)的物理内存形象。这个形象叫作地址空间(address space),是运行的程序看到的零碎中的内存。 一个过程的地址空间蕴含运行的程序的所有内存状态。比方:程序的代码(code,指令)必须在内存中,因而它们在地址空间里。当程序在运行的时候,利用栈(stack)来保留以后的函数调用信息,调配空间给局部变量,传递参数和函数返回值。最初,堆(heap)用于治理动态分配的、用户治理的内存。当然,还有其余的货色(例如,动态初始化的变量),但当初假如只有这3个局部:代码、栈和堆。 在下图的例子中,咱们有一个很小的地址空间(只有16KB)。程序代码位于地址空间的顶部(在本例中从0开始,并且装入到地址空间的前1KB)。代码是动态的(因而很容易放在内存中),所以能够将它放在地址空间的顶部,咱们晓得程序运行时不再须要新的空间。 当咱们形容地址空间时,所形容的是操作系统提供给运行程序的形象。程序不在物理地址0~16KB的内存中,而是加载在任意的物理地址。然而运行的程序意识不到这点,它认为本人被加载到特定地址(例如0)的内存中,并且具备十分大的地址空间。这就是虚拟内存零碎须要做的事件。 指标虚拟内存(VM)零碎的一个次要指标是通明(transparency)。操作系统实现虚拟内存的形式,应该让运行的程序看不见。因而,程序不应该感知到内存被虚拟化的事实,相同,程序的行为就如同它领有本人的公有物理内存。 虚拟内存的另一个指标是效率(efficiency)。操作系统应该谋求虚拟化尽可能高效(efficient),包含工夫上(即不会使程序运行得更慢)和空间上(即不须要太多额定的内存来反对虚拟化)。在实现高效率虚拟化时,操作系统将不得不依附硬件反对,包含TLB这样的硬件性能。 最初,虚拟内存第三个指标是爱护(protection)。操作系统应确保过程受到爱护(protect),不会受其余过程影响,操作系统自身也不会受过程影响。当一个过程执行加载、存储或指令提取时,它不应该以任何形式拜访或影响任何其余过程或操作系统自身的内存内容(即在它的地址空间之外的任何内容)。 内存操作API在运行一个C程序的时候,会调配两种类型的内存。第一种称为栈内存,它的申请和开释操作是编译器来隐式治理的,所以有时也称为主动(automatic)内存。第二种类型的内存,即所谓的堆(heap)内存,其中所有的申请和开释操作都由程序员显式地实现。 mallocmalloc函数非常简单:传入要申请的堆空间的大小,它胜利就返回一个指向新申请空间的指针,失败就返回NULL。 #include <stdlib.h>...void *malloc(size_t size);free要开释不再应用的堆内存,程序员只需调用free(): int *x = malloc(10 * sizeof(int));...free(x);该函数承受一个参数,即一个由malloc()返回的指针。调配区域的大小不会被用户传入,必须由内存调配库自身记录追踪。 常见谬误在应用malloc()和free()时会呈现一些常见的谬误。 遗记分配内存许多例程在调用之前,都心愿你为它们分配内存。例如,例程strcpy(dst, src)将源字符串中的字符串复制到指标指针。然而,如果不小心,你可能会这样做: char *src = "hello";char *dst; // oops! unallocatedstrcpy(dst, src); // segfault and die没有调配足够的内存另一个相干的谬误是没有调配足够的内存,有时称为缓冲区溢出(buffer overflow)。一个常见的谬误是为指标缓冲区留出“简直”足够的空间。 char *src = "hello";char *dst = (char *) malloc(strlen(src)); // too small!strcpy(dst, src); // work properly遗记初始化调配的内存在这个谬误中,程序员正确地调用malloc(),但遗记在新调配的数据类型中填写一些值。这样的话程序最终会遇到未初始化的读取(uninitialized read),它从堆中读取了一些未知值的数据。 遗记开释内存另一个常见谬误称为内存泄露(memory leak),如果遗记开释内存,就会产生。在长时间运行的应用程序或零碎(如操作系统自身)中,这是一个微小的问题,因为迟缓泄露的内存会导致内存不足,此时须要重新启动。 在用完之前开释内存有时候程序会在用完之前开释内存,这种谬误称为悬挂指针(dangling pointer)。随后的应用可能会导致程序解体或笼罩无效的内存(例如,你调用了free(),但随后再次调用malloc()来调配其余内容,这从新利用了谬误开释的内存)。 重复开释内存程序有时还会不止一次地开释内存,这被称为反复开释(double free)。这样做的后果是未定义的。 ...

April 10, 2021 · 1 min · jiezi

关于操作系统:操作系统虚拟化进程调度

介绍工作负载假如探讨可能的策略范畴之前,咱们先做一些简化假如。这些假如与零碎中运行的过程无关,有时候统称为工作负载(workload)。在这里咱们对工作负载所做的假如是不切实际的,但未来会放宽这些假如。当初,咱们对操作系统中运行的过程(有时也叫工作工作)做出如下的假如: 每一个工作运行雷同的工夫。所有的工作同时达到。一旦开始,每个工作放弃运行直到实现。所有的工作只是用CPU(即它们不执行IO操作)。每个工作的运行工夫是已知的。调度指标除了做出工作负载假如之外,还须要一个货色能让咱们比拟不同的调度策略:调度指标。指标是咱们用来掂量某些货色的货色,在过程调度中,有一些不同的指标是有意义的。 当初,让咱们简化一下,只用一个指标:周转工夫(turnaround time)。工作的周转工夫定义为工作实现工夫减去工作达到零碎的工夫。 周转工夫是一个性能(performance)指标,另一个乏味的指标是偏心(fairness),性能和偏心在调度零碎中往往是矛盾的。 先进先出(FIFO)咱们能够实现的最根本的算法,被称为先进先出(First In First Out或FIFO)调度,有时候也称为先到先服务(First Come First Served或FCFS)。 FIFO有一些踊跃的个性:它很简略,而且易于实现,然而在某些状况下的性能很差。 假如咱们有3个工作(A、B和C),A运行100s,而B和C运行10s。而且都简直同时达到,A比B早一点点,而后B比C早达到一点点。此时零碎的均匀周转工夫是比拟高的:110s((100 + 110 + 120)/ 3 = 110)。 这个问题通常被称为护航效应(convoy effect),一些耗时较少的潜在资源消费者被排在重量级的资源消费者之后。 最短工作优先(SJF)事实证明,一个非常简单的办法解决了这个问题。这个新的调度准则被称为最短工作优先(ShortestJob First,SJF):先运行最短的工作,而后是次短的工作,如此上来。 事实上,思考到所有工作同时达到的假如,咱们能够证实SJF的确是一个最优(optimal)调度算法。然而咱们的假如依然是不切实际的,让咱们放宽另一个假如。咱们能够针对假如2,当初假如工作能够随时达到,而不是同时达到,这会导致什么问题? 举个例子,假如A在t = 0时达到,且须要运行100s。而B和C在t = 10达到,且各须要运行10s。即便B和C在A之后不久达到,它们依然被迫等到A实现,从而遭逢同样的护航问题。 最短实现工夫优先(STCF)为了解决这个问题,须要放宽假如条件(工作必须放弃运行直到实现)。咱们还须要调度程序自身的一些机制。鉴于咱们先前对于时钟中断和上下文切换的探讨,当B和C达到时,调度程序当然能够做其余事件:它能够抢占(preempt)工作A,并决定运行另一个工作,或者稍后持续工作A。依据咱们的定义,SJF是一种非抢占式(non-preemptive)调度程序,因而存在上述问题。 有一个调度程序齐全就是这样做的:向SJF增加抢占,称为最短实现工夫优先(ShortestTime-to-Completion First,STCF)或抢占式最短作业优先(Preemptive Shortest Job First ,PSJF)调度程序。每当新工作进入零碎时,它就会确定残余工作和新工作中,谁的剩余时间起码,而后调度该工作。因而,在咱们的例子中,STCF将抢占A并运行B和C以实现。只有在它们实现后,能力调度A的剩余时间。和以前一样,思考到咱们的新假如,STCF可证实是最优的。 新度量指标:响应工夫如果咱们晓得工作长度,而且工作只应用CPU,而咱们惟一的掂量是周转工夫,STCF将是一个很好的策略。然而,引入分时系统扭转了这所有。当初,用户将会坐在终端后面,同时也要求零碎的交互性好。因而,一个新的度量规范诞生了:响应工夫(response time)。响应工夫定义为从工作达到零碎到首次运行的工夫。 STCF和相干办法在响应工夫上并不是很好。例如,如果3个工作同时达到,第三个工作必须期待前两个工作全副运行后能力运行。这种办法尽管有很好的周转工夫,但对于响应工夫和交互性是相当蹩脚的。 轮转为了解决这个问题,咱们将介绍一种新的调度算法,通常被称为轮转(Round-Robin,RR)调度。根本思维很简略:RR在一个工夫片(time slice,有时称为调度量子,schedulingquantum)内运行一个工作,而后切换到运行队列中的下一个工作,而不是运行一个工作直到完结。它重复执行,直到所有工作实现。 工夫片长度对于RR是至关重要的。越短,RR在响应工夫上体现越好。然而,工夫片太短是有问题的:忽然上下文切换的老本将影响整体性能。上下文切换的老本不仅仅来自保留和复原大量寄存器的操作系统操作,程序运行时,它们在CPU高速缓存、TLB、分支预测器和其余片上硬件中建设了大量的状态。切换到另一个工作会导致此状态被刷新,且与以后运行的作业相干的新状态被引入,这可能导致显著的性能老本。 如果响应工夫是咱们的惟一指标,那么带有正当工夫片的RR,就会是十分好的调度程序。如果周转工夫是咱们的指标,那么RR却是最蹩脚的策略之一。更一般地说,任何偏心(fair)的政策(如RR),即在小规模的工夫内将CPU平均调配到流动过程之间,在周转工夫这类指标上体现不佳。 其余因素首先,咱们得放宽假如4:因为简直所有的程序都要执行I/O。调度程序显然要在工作发动I/O申请时做出决定,应该在CPU上安顿另一项工作,调度程序还必须在I/O实现时做出决定。 有了应答I/O的根本办法,咱们来看最初的假如:调度程序晓得每个工作的长度。如前所述,这可能是能够做出的最蹩脚的假如。事实上,在一个通用的操作系统中,零碎通常对每个作业的长度知之甚少。 实现:多级反馈队列咱们将介绍一种驰名的调度办法——多级反馈队列(Multi-level Feedback Queue,MLFQ)。该调度程序通过多年的一系列优化,呈现在许多古代操作系统中。多级反馈队列须要解决两方面的问题。首先,它要优化周转工夫。其次,MLFQ心愿给交互用户(如用户坐在屏幕前,等着过程完结)很好的交互体验,因而须要升高响应工夫。 根本规定MLFQ中有许多独立的队列(queue),每个队列有不同的优先级(priority level)。任何时刻,一个工作只能存在于一个队列中。MLFQ总是优先执行较高优先级的工作(即在较高级队列中的工作)。 当然,每个队列中可能会有多个工作,因而具备同样的优先级。在这种状况下,咱们就对这些工作采纳轮转调度。 至此,咱们失去了MLFQ的两条根本规定: 规定1:如果A的优先级 > B的优先级,运行A(不运行B)。规定2:如果A的优先级 = B的优先级,轮转运行A和B。尝试1:如何扭转优先级咱们必须决定,在一个工作的生命周期中,MLFQ如何扭转其优先级(在哪个队列中)。要做到这一点,咱们必须记得工作负载:既有运行工夫很短、频繁放弃CPU的交互型工作,也有须要很多CPU工夫、响应工夫却不重要的长时间计算密集型工作。上面是咱们第一次尝试优先级调整算法。 规定3:工作进入零碎时,放在最高优先级(最上层队列)。规定4a:工作用残缺个工夫片后,升高其优先级(移入下一个队列)。规定4b:如果工作在其工夫片以内被动开释CPU,则优先级不变。然而,这种算法有一些十分重大的毛病。 首先,会有饥饿(starvation)问题。如果零碎有“太多”交互型工作,就会一直占用CPU,导致长工作永远无奈失去CPU。 其次,聪慧的用户会重写程序,愚弄调度程序(game the scheduler)。愚弄调度程序指的是用一些卑劣的伎俩坑骗调度程序,让它给你远超偏心的资源。例如过程在工夫片用完之前,调用一个I/O操作(比方拜访一个无关的文件),从而被动开释CPU。如此便能够放弃在高优先级,占用更多的CPU工夫。 最初,一个程序可能在不同工夫体现不同。一个计算密集的过程可能在某段时间体现为一个交互型的过程。用咱们目前的办法,它不会享受零碎中其余交互型工作的待遇。 ...

April 10, 2021 · 1 min · jiezi

关于macos:奇技淫巧玄妙无穷-M1-mac-os苹果AppleSilicon系统的基本操作和设置

原文转载自「刘悦的技术博客」https://v3u.cn/a_id_191 最近有个敌人跟我说,说他新入职了一家公司,公司还不错,给他配了一台Mac,然而呢他以前始终在Windows环境下开发,对Mac os并不理解,他感到很徘徊,所以本次呢,咱们来分享一下,当手头儿有一部簇新的Mac,咱们应该怎么上手操作和配置,让它成为咱们开发的好帮手。 首先咱们来看一下键位上的差别,传统Windows零碎的键盘以control键为主,以左小指为基准,食指为辐射范畴,组合快捷键,比如说咱们十分相熟control+c 和 control +v: Mac零碎的键位是以command键为主,以左拇指为基准,食指为辐射范畴,组合快捷键,用习惯了windows敌人只有脑子想着本来的control键改为command键即可,这样上手呢就不便很多: 接下来,咱们来看看Mac的快捷键: 乍一看,我靠,怎么这么多,其实咱们一开始不必记那么多,只有记住一些罕用的快捷键即可: 首先咱们来关上finder,mac里的finder就相当于windows零碎的此电脑,电脑中的文件都会在finder里显示,这里如果想选中某个文件,并不需要鼠标的参加,只有点击tab键,即可让光标选中文件,多点几次呢,就能够从新抉择,这时候组合快捷键command + i 就能够显示该文件的具体信息,大家能够看到该文件的一些属性,所以第一个快捷键 command + i,为了不便记忆 i 能够了解为information信息,ok,怎么敞开这个信息窗口呢?能够应用command + w,无论什么窗口都能够command + w来敞开,w能够了解为wave,也就是挥手再见的意思,command + w ,敞开窗口。 接着来咱们来操作一下复制文件,command + c,c就是copy的意思这和windows零碎里的复制文件相差无几,command + v,粘贴文件,复制粘贴也是咱们操作频率比拟高的操作,那么windows零碎下,常常有一种操作叫做剪切,它的快捷键是control + x ,在mac中如果想剪切文件的话,须要应用组合键command + option + v,这样能力剪切文件。 有的时候咱们须要批改文件的文件名,在mac零碎中,只有选中文件按回车,就能够很不便的批改文件名,同时呢,能够应用 command +和command - 来放大批改图标。 另外一个常常会用到的组合键是:command q , 咱们晓得应用command + w 能够敞开窗口,然而无奈退出程序,如果想彻底退出程序的,应用 command + q 就能够彻底退出,q 意味着 quit,比方我关上sfari浏览器,command + q 就能够退出,而command w 只能敞开某个浏览器窗口。 除了快捷键,咱们介绍一个finder里常常会用到的门路栏设置,大家能够看到我的finder文件夹上面有一个当前目录的文件门路,能够很不便的查看它的上一级目录,这个在默认设置里是不显示的,须要手动关上,那么在finder的显示菜单,抉择显示门路栏即可,这个门路栏能够复制,而后在终端内就能够很不便的进入某个文件的终端,而在终端内,我也能够十分的在finder中关上,输出 open . 即可,十分不便 ...

April 6, 2021 · 1 min · jiezi

关于iot:LiteOS内核源码分析任务LOSSchedule

摘要:调度,Schedule也称为Dispatch,是操作系统的一个重要模块,它负责抉择零碎要解决的下一个工作。调度模块须要协调处于就绪状态的工作对资源的竞争,按优先级策略从就绪队列中获取高优先级的工作,给予资源使用权。本文分享自华为云社区《LiteOS内核源码剖析系列六 -工作及调度(5)-工作LOS_Schedule》,原文作者:zhushy 。 本文咱们来一起学习下LiteOS调度模块的源代码,文中所波及的源代码,均能够在LiteOS开源站点https://gitee.com/LiteOS/LiteOS 获取。调度源代码散布如下: LiteOS内核调度源代码包含调度模块的公有头文件kernelbaseincludelos_sched_pri.h、C源代码文件kernelbaseschedsched_sqlos_sched.c,这个对应单链表就绪队列。还有个`调度源代码文件kernelbaseschedsched_mqlos_sched.c,对应多链表就绪队列。本文次要分析对应单链表就绪队列的调度文件代码,应用多链表就绪队列的调度代码相似。 调度模块汇编实现代码调度模块的汇编函数有OsStartToRun、OsTaskSchedule等,依据不同的CPU架构,散布在下述文件里: archarmcortex_msrcdispatch.S、archarmcortex_a_rsrcdispatch.S、archarm64srcdispatch.S。 本文以STM32F769IDISCOVERY为例,剖析一下Cortex-M核的调度模块的源代码。咱们先看看调度头文件kernelbaseincludelos_sched_pri.h中定义的宏函数、枚举、和内联函数。 1、调度模块宏函数和内联函数kernelbaseincludelos_sched_pri.h定义的宏函数、枚举、内联函数。 1.1 宏函数和枚举UINT32 g_taskScheduled是kernelbaselos_task.c定义的全局变量,标记内核是否开启调度,每一位代表不同的CPU核的调度开启状态。 ⑴处定义的宏函数OS_SCHEDULER_SET(cpuid)开启cpuid核的调度。⑵处宏函数OS_SCHEDULER_CLR(cpuid)是前者的反向操作,敞开cpuid核的调度。⑶处宏判断以后核是否开启调度。⑷处的枚举用于标记是否发动了申请调度。当须要调度,又暂不具备调度条件的时候,标记下状态,等具备调度的条件时,再去调度。 ⑴ #define OS_SCHEDULER_SET(cpuid) do { g_taskScheduled |= (1U << (cpuid)); } while (0);⑵ #define OS_SCHEDULER_CLR(cpuid) do { g_taskScheduled &= ~(1U << (cpuid)); } while (0);⑶ #define OS_SCHEDULER_ACTIVE (g_taskScheduled & (1U << ArchCurrCpuid()))⑷ typedef enum { INT_NO_RESCH = 0, /* no needs to schedule */ INT_PEND_RESCH, /* pending schedule flag */ } SchedFlag;1.2 内联函数有2个内联函数用于查看是否能够调度,即函数STATIC INLINE BOOL OsPreemptable(VOID)和STATIC INLINE BOOL OsPreemptableInSched(VOID)。区别是,前者判断是否能够抢占调度时,先关中断,防止以后的工作迁徙到其余核,返回谬误的是否能够抢占调度状态。 ...

April 6, 2021 · 4 min · jiezi

关于操作系统:操作系统虚拟化进程

概念过程的非正式定义非常简单:过程就是运行中的程序。程序自身是没有生命周期的,它只是存在磁盘下面的一些指令(也可能是一些静态数据)。人们经常心愿同时运行多个程序,一个失常的零碎可能会有上百个过程同时在运行。 操作系统通过虚拟化(virtualizing)CPU来提供这种假象。通过让一个过程只运行一个工夫片,而后切换到其余过程,操作系统提供了存在多个虚构CPU的假象。这就是时候共享(time sharing)CPU技术,容许用户如愿运行多个并发过程。潜在的开销就是性能损失,因为如果CPU必须共享,每个过程的运行就会慢一点。 形象:过程操作系统为正在运行的程序提供的形象,就是所谓的过程(process)。正如咱们下面所说的,一个过程只是一个正在运行的程序。在任何时刻,咱们都能够盘点它在执行过程中拜访或影响的零碎的不同局部,从而概括一个过程。 为了了解形成过程的是什么,咱们必须了解它的机器状态(machine state):程序在运行时能够读取或更新的内容。过程的机器状态有一个显著组成部分,就是它的内存。指令存在内存中,正在运行的程序读取和写入的数据也在内存中。因而过程能够拜访的内存(称为地址空间,address space)是该过程的一部分。 过程的机器状态的另一部分是寄存器。许多指令明确地读取或更新寄存器,因而,它们对于执行该过程很重要。例如,程序计数器(ProgramCounter,PC)(有时称为指令指针,Instruction Pointer或IP)通知咱们程序以后正在执行哪个指令;相似地,栈指针(stack pointer)和相干的帧指针(frame pointer)用于治理函数参数栈、局部变量和返回地址。 最初,程序也常常拜访长久存储设备。此类I/O信息可能蕴含以后关上的文件列表。 过程创立操作系统运行程序必须做的第一件事是将代码和所有静态数据(例如初始化变量)加载(load)到内存中,也即加载到过程的地址空间中。程序最后以某种可执行格局驻留在磁盘上。因而,将程序和静态数据加载到内存中的过程,须要操作系统从磁盘读取这些字节,并将它们放在内存中的某处。 将代码和静态数据加载到内存后,操作系统在运行此过程之前还须要执行其余一些操作。必须为程序的运行时栈(run-time stack或stack)调配一些内存。 操作系统也可能为程序的堆(heap)调配一些内存。在C程序中,堆用于显式申请的动态分配数据。程序通过调用malloc()来申请这样的空间,并通过调用free()来明确地开释它。 操作系统还将执行一些其余初始化工作,特地是与输出/输入(I/O)相干的工作。例如,在UNIX零碎中,默认状况下每个过程都有3个关上的文件描述符(file descriptor),用于规范输出、输入和谬误。 通过将代码和静态数据加载到内存中,通过创立和初始化栈以及执行与I/O设置相干的其余工作,OS当初为程序执行搭好了舞台。而后它有最初一项工作:启动程序,在入口处运行,即main()。通过跳转到main()例程,OS将CPU的控制权转移到新创建的过程中,从而程序开始执行。 过程状态简而言之,过程能够处于以下3种(稳固)状态之一。 运行(running):在运行状态下,过程正在处理器上运行。这意味着它正在执行指令。就绪(ready):在就绪状态下,过程已筹备好运行,但因为某种原因,操作系统抉择不在此时运行。阻塞(blocked):在阻塞状态下,一个过程执行了某种操作,直到产生其余事件时才会筹备运行。一个常见的例子是,当过程向磁盘发动I/O申请时,它会被阻塞,因而其余过程能够应用处理器。数据结构操作系统是一个程序,和其余程序一样,它有一些要害的数据结构来跟踪各种相干的信息。例如,为了跟踪每个过程的状态,操作系统可能会为所有就绪的过程保留某种过程列表(process list),以及跟踪以后正在运行的过程的一些附加信息。操作系统还必须以某种形式跟踪被阻塞的过程。当I/O事件实现时,操作系统应确保唤醒正确的过程,让它筹备好再次运行。 操作系统追踪过程的一些重要信息。对于进行的过程,寄存器上下文将保留其寄存器的内容。当一个过程进行时,它的寄存器将被保留到这个内存地位。通过复原这些寄存器(将它们的值放回理论的物理寄存器中),操作系统能够复原运行该过程。 除了运行、就绪和阻塞之外,还有其余一些过程能够处于的状态。有时候零碎会有一个初始(initial)状态,示意过程在创立时处于的状态。另外,一个过程能够处于已退出但尚未清理的最终(final)状态(在基于UNIX的零碎中,这称为僵尸状态)。 注:存储对于过程的信息的个体构造称为过程管制块(Process Control Block,PCB)。

April 5, 2021 · 1 min · jiezi

关于操作系统:android-os-基础

冯诺依曼结构1> 采纳二进制,摈弃十进制2> 程序存储 |================================| 内存储器 | | 运算器 控制器 || || 输出设施 输出设备 ||=================================|简化为: CPU(中央处理器) + 内存储器 + I/O 设施1、操作系统必须针对 "硬件" 来开发, 即 某些架构, arm/x86 等等2、操作系统提供可用的 人机交互 界面3、操作系统 反对用户 编写和安装程序 个别 操作系统两大次要内容:1> 面向上层: 治理硬件2> 面向下层: 提供人机交互、为第三方程序研发提供 API 定义: 计算机操作系统是 负责管理系统硬件,并为下层利用提供稳固编程接口 和 人机交互界面的 软件汇合其中 要钻研的 Andoird os 是基于 Linux kernel 过程间通信 IPC指 运行在 不同过程( 无论是否在 1 台机器上 )中的若干线程的 数据交互实现形式: 消息传递、管道、socket、、、、 共享内存 、文件共享 [1]共享内存 shared memery因为两个过程能够间接共享拜访 同一块 内存区域, 缩小了数据的复制操作, 因此速度较快实现共享内存:1> 创立共享内存区        // linux 中用 shmget 函数实现,生成的 共享内存快将 与特定的某个 key (shmget 的第1个参数) 绑定2> 映射共享内存区       // 把申请的共享内存映射到过程的空间能力进一步操作,  linux 下用  shmat 实现3> 拜访共享内存区     // 过程 1 创立了内存共享区, 过程 2 拜访: 利用过程 1 创立时 的 key,  过程 2 shmget 传入同 1 个 key 值      // 而后 过程 2 执行 shmat 将内存映射到它的空间 ...

March 29, 2021 · 1 min · jiezi

关于操作系统:敲击键盘后字符怎么出现在显示器

咱们每敲击一次电脑键盘,按键对应的字符就会呈现在显示器上。这两头产生了什么?请听我缓缓细说。 相干概念人对按键的操作,从两个维度去形容,一个是“动作”,另一个是“内容”。 按下一个按键,松开一个按键,按下一个按键并且放弃按住状态一段时间(长按),这些都是动作。 每个按键和动作组合起来,会传送给计算机一个编码,这就是内容,术语是“扫描码”,对应的英语词汇是scan code。 按下按键、长按键对应的扫描码叫“Make Code”,松开按键对应的扫描码叫“Break Code”。 Break Code和Make Code的关系是:Break Code = Make Code & 0x80。 为什么两种编码之间有下面的关系?设计人员特意这样设计的。 三个硬件8048在键盘上执行某种操作(按键、长按、松开键)时,8048会检测到这个操作,把这个操作对应的扫描码发送给8042。 到当初为止,呈现了三套编码方案,咱们当初的键盘个别应用第2套计划。 80428042从8048接管到第2套计划的扫描码后,把它转换成第1套扫描码,并且放入缓冲区,最初,告诉8259A产生了键盘中断。 中断例程取走缓冲区的数据后,8042才会接管新的数据。缓冲区的数据不被取走,8042就不会接管新数据。 8259A8259A接管来自8042的键盘中断,让操作系统分派中断例程解决缓冲区的数据。 流程人类敲击键盘,8048监测到”敲击了哪个键“,把对应的扫描码传送给8042。8042接管到8048的数据后,将数据转换成第1套扫描码,放入缓冲区;而后告诉8259A。8259A接管到告诉后,通知操作系统产生了键盘中断。操作系统运行键盘中断例程把缓冲区的数据取走;8042又能够从新接管新数据了。解析扫描码映射数组在第1套扫描码中,一个Make Code对应一个按键。咱们能够通过Make Code辨认出以后被按下的键是哪个键。例如,A键的Make Code是0x1E;当操作系统接管到的Make Code是0x1E时,就能够认为接管到的数据是A。 然而,问题呈现了。咱们在理论输出中有输出A和a的需要。可扫描码计划中只有A的扫描码,没有a的扫描码。相似的按键还有数字键1、2、3等。怎么解决这个问题呢? 先看看上面这个表格。 Make Code 0 1 2 0x1E a A 0 0x02 1 ! 0 在下面的表格中,Make Code是行号,每行有三个不同的值。0x1E行的第0列表示意a,0x1E行的第1列示意A。 这可能实现一个键示意两种不同的值。 咱们平时怎么取得一个键的不同值呢?以数字键1为例。敲击数字键1时,获取的值是1;同时按下shift键和数字键1时,获取的值是感叹号!。 在具体实现中,依据是否同时按下了shift键来决定是获取第X行的第0列还是第1列。显然,按下shift键,获取第1列;没有按下shift键,获取第0列。 第1套扫描码一共有0x80个,其实就是ASCII码表中元素的个数。 咱们仿照下面的表格建设一个元素更多的表格TB(表头雷同,行数裁减到0x80行)。 第一列是行号,值是Make Code,假如是A。第二列是A对应的键的默认值(没有按下shift键)。第二列是A对应的键的另一个值(按下了shift键)。然而,在键盘上存在不可打印的字符,例如esc、F1、F2等。在TB中,这些不可打印的键对应的值是咱们设置的某个数值(这一行的第0列和第1列的数值雷同)。 怎么应用TB?依据Make Code找到对应的行,依据是否按下了shift键决定是获取第0列还是第1列的值。 要在C语言中应用这个表格,只能将它用数组示意进去。把这个表格的每一行的第0列、第1列、第2列按程序组成一个数组keyMap。 如果接管到的Make Code是MC,没有按下shift键,对应的值是keyMap[MC * 3];按下shift键,对应的值是keyMap[MC * 3 + 1]。 反正就是这么回事,硬是要解释一下怎么弄出这个数组的,我解释不分明。 PausePause键只有Make Code,没有Break Code。这是仅有的特例,其余键同时具备Make Code和Break Code。 ...

March 20, 2021 · 1 min · jiezi

关于嵌入式:RTThread学习笔记-7RTThread中断管理学习总结

后面几篇文章总结了RT-Thread多线程相干内容的学习过程,包含多线程的应用,多线程的同步,多线程的通信,对于多线程相干的学习总结,能够查看之前的文章。 本文的内容是对于RT-Thread中断治理的学习总结,包含简略地介绍了什么是中断,裸机中断与RT-Thread中断有什么区别,RT-Thread是如何解决中断的,RT-Thread内核提供哪些中断相干的接口,等等。 对于RT-Thread中断治理相干的内容,官网提供了比拟丰盛的文档作为参考,具体能够查看以下链接:https://www.rt-thread.org/doc... 本文尝试从以下几个方面总结一下RT-Thread中断治理的学习过程中断相干的概念形容 什么是中断?中断,顾名思义就是一项正在进行的工作,忽然间被其余事件打断,导致原来正在进行的工作不能持续失常进行,而须要去把其余事件解决完,能力回来持续进行原来的工作。 如何艰深地了解中断?设想一下这样的场景,周末你正在家里欢快地写着代码,忽然间你的手机铃声响了,你必须停下手里的工作,记录代码写到哪个阶段,而后就去接这个电话了。“写代码”就是正在进行的工作,“电话响起”就是中断事件。 这个电话是媳妇打过去的,她让你去菜市场买点韭菜和猪肉,早晨包饺子吃,媳妇的话哪敢不听,于是你感觉菜市场买货色比拟重要,挂掉电话后就去买货色了,买完货色回来后,再接着写刚刚还没实现的代码。“菜市场买货色”就是中断服务程序,这就是一个典型的中断处理过程。 对于中断的操作模式和特权级别,Cortex-M的处理器有三种状态划分,别离是:特权级解决模式,特权级线程模式,用户级线程模式。这三种状态的关系,如下图所示。 从上图能够看出,中断或异样的服务程序,总是处于特权级解决模式的。而RT-Thread零碎内核复位上电时启动的主线程(main线程),是运行在特权级线程模式的。其余用户创立的线程,是运行在用户级线程模式的。 为什么处理器要辨别特权级和用户级?特权,顾名思义就是处理器如果工作在这个级别下,权限就会比拟高,就能够拜访一些非凡的寄存器,以避免用户级的代码拜访这些非凡寄存器,对数据进行毁坏。中断因为其特殊性,所以,中断函数是工作在特权级别下的。 裸机中断与操作系统中断两者有什么区别呢?咱们在裸机代码中解决硬件中断的时候,个别只有编写中断处理函数就能够了,这种形式解决中断,简略且间接。 然而,有了操作系统之后,所有的货色都变了,要思考的问题就多了很多。因为操作外面运行了很多线程,中断来了之后,就要告知操作系统,把以后运行线程的信息保留到栈外面,再去解决中断服务程序,解决完中断要再回去解决线程,此时又可能波及到线程切换调度,而线程切换自身又须要PendSV中断参加。 所以,在裸机解决中断和在操作系统中解决中断,几乎就是天壤之别。RT-Thread 中断解决机制 理解过Cortex-M系列单片机的工程师,个别都晓得在芯片的汇编启动文件startup_xxx.s外面,有一个中断向量表,所有的中断都是通过这个中断向量表来进行解决的。 当一个中断异样触发的时候,处理器将会判断是哪个中断源,而后跳转到固定地位进行解决,每个中断服务程序的地址入口必须是放到对立的地址上,也就是须要设置到NVIC的中断向量偏移寄存器外面,中断向量表如下图所示。 其实,不论有没有操作系统的参加,一旦硬件发送中断和异样之后,中断的入口都是在这个中断向量表的。区别无非就是在裸机环境下,间接解决中断服务程序,而在有操作系统的状况下,须要先保留线程的运行状况,而后再解决中断,解决完中断后,再复原线程的运行环境。 硬件中断的优先级是最高的,任何线程的优先级都要低于硬件中断,因而,只有产生了硬件中断事件,零碎就必须要进行相应的解决。 RT-Thread在解决中断的时候,个别都会有三个阶段:中断前导程序,中断服务程序,中断后续程序,这三个阶段,如下图所示。 中断前导程序的次要工作是,当中断事件产生的时候,处理器的硬件会把以后CPU相干的寄存器参数主动压入中断栈外面。程序须要调用rt_interrupt_enter()函数,把全局变量rt_interrupt_nest进行加1操作,这个全局变量是用来记录中断的嵌套层数的。 用户中断服务程序的次要工作分两种状况,一种是不进行线程切换,另一种是进行线程切换。不进行线程切换的话,中断服务程序和中断后续程序运行实现后,将返回被中断的线程。 而如果要进行线程切换,则会调用rt_hw_context_switch_interrupt() 函数进行上下文切换,这个函数次要是设置变量rt_interrupt_to_thread,而后触发PendSV中断。 在这里要留神一下:因为PendSV中断的优先级最低,不能进行中断抢占,因而即便触发了该中断,但因为此时还在用户中断处理函数外面,所以PendSV中断还处于期待阶段,只有退出了中断后续程序,才会进行PendSV中断解决,才会进行线程的上下文切换。所以,线程的上下文切换是不会在用户中断外面进行的,是在中断完结后进行的。 中断后续程序的次要工作是,告诉零碎内核来到中断状态,通过调用rt_interrupt_leave()函数,将全局变量rt_interrupt_nest进行减1操作,而后从中断栈外面复原复原CPU相干的寄存器参数。 这里复原CPU寄存器参数的时候须要留神,如果在用户中断外面波及到线程切换,那么这个时候就须要复原到新的线程CPU寄存器参数,而不是复原到被中断打断的线程CPU寄存器参数。 RT-Thread操作系统在解决中断的时候,通常采纳“上半局部(Top Half)”和“底半局部(Bottom Half)”这种形式。起因在于,操作系统自身不会对中断服务程序的解决工夫做任何假如和限度,但为了保证系统的实时性,用户须要保障中断服务程序在尽可能短的工夫内实现。 如何了解“上半局部(Top Half)”和“底半局部(Bottom Half)”这种中断解决形式?还是以买菜为例。媳妇来电话让你到菜市场买菜(中断事件),但你思考到如果长时间中断不写代码,会导致思路断链,为了防止这种状况(防止长时间解决中断服务),齐全能够在网上下单购买(短时间的中断解决),生鲜超市收到下单信息(信号量、邮件、音讯队列),就会安顿快递小哥送货上门,买菜这么耗时的工作就由其他人(其余线程)去实现了。 “上半局部(Top Half)”和“底半局部(Bottom Half)”这种中断解决形式,次要是利用在一些须要耗时解决中断事务的场合,比方数据的接管和解决。通常接收数据的工夫比拟短,只有把接管到的数据保留下来即可,但解决数据的过程就可能比拟耗时,这样就须要离开来解决,上半局部就是接收数据,底半局部就是耗时的数据处理。RT-Thread中断相干的API函数接口 为了把操作系统和硬件底层的中断异样隔离开来,RT-Thread零碎内核把中断和异样封装为一组形象的接口,具体的函数接口如下图所示。RT-Thread中断相干的利用示例 RT-Thread中断相干的利用示例,次要是为了验证中断相干的API接口函数,例如全局中断开关的应用示例,通过按键中断示例来验证“上半局部(Top Half)”和“底半局部(Bottom Half)”这种中断解决形式。 示例源码下载链接:https://github.com/embediot/r... 全局中断开关示例,次要是为了验证多线程拜访同一个变量时,应用开关全局中断的形式对该全局变量进行临界区爱护。 按键中断示例次要是为了验证“上半局部(Top Half)”和“底半局部(Bottom Half)”这种中断解决形式。通过按键触发中断事件,在中断服务函数外面发送邮件,告诉线程进行相应的解决。在irq_test.h头文件外面,通过关上相应的宏定义开关,从新编译工程源码,下载到开发板即可验证试验景象,如下图所示。RT-Thread中断利用的注意事项 中断是一种异样,当零碎产生中断异样的时候就必须要进行解决,在RT-Thread实时操作系统外面解决中断的时候,如果不及时处理或对中断处理不当,轻则会造成零碎出错或逻辑凌乱,重则会导致系统毁灭性地瘫痪。 在解决RT-Thread中断异样的时候,有以下注意事项: 1.中断服务程序工作在特权级解决模式,优先级比任何线程要高,任何线程都不能抢占中断服务程序。 2.在操作系统外面,能够反对中断嵌套,高优先级中断能够抢占低优先级中断,线程的从新调度是在所有中断都解决完之后才重新启动的。 3.在Cortex-M架构外面,中断产生时CPU的寄存器入栈是由硬件主动实现的,中断的前导程序通常只是记录中断的嵌套层数。 4.RT-Thread采纳独立的内存空间作为中断栈,而不是采纳线程栈作为中断栈,这种形式随着线程的减少,缩小内存占用的成果也越显著。 5.倡议采纳“上半局部(Top Half)”和“底半局部(Bottom Half)”这种形式来解决中断异样,中断服务程序的解决工夫应尽可能短。 6.应用全局中断开关是禁止多线程拜访临界区最简略的一种形式,这种形式能够利用在任何场合,但要留神这种形式对系统实时性影响微小,使用不当会毁坏零碎的实时性能。应用全局中断锁的工夫应尽可能短。 7.全局中断开关反对多级中断嵌套应用,每次调用rt_hw_interrupt_enable()函数,能够让零碎复原到关中断之前的状态(这个状态有可能是关中断也有可能是开中断)。 8.中断服务程序是运行在特权解决模式下的,在这种运行模式外面是不能应用挂起以后线程操作的相干函数的,因为中断服务程序的运行环境外面基本不存在线程。 感激浏览! 原文链接:https://club.rt-thread.org/as...

March 16, 2021 · 1 min · jiezi

关于嵌入式:RTThread学习笔记-6RTThread线程间通信学习过程总结

前两篇文章总结了RT-Thread多线程以及多线程同步的学习过程,对于前两篇学习总结,能够查看之前的文章。 本篇文章持续总结对于RT-Thread多线程相干的最初一个重要知识点:线程间通信。后面的文章屡次提及到,一个大的工作拆分为多个小工作,这些小工作之间必然存在着各种各样的关系,导致这些小工作的线程不能各自为政,必须要思考其余工作线程的运行状况。 既然曾经有了线程间同步,能够让多个线程之间进行互相沟通,那为啥还须要线程间通信呢?线程间通信到底是什么货色,这种形式有什么利用场景? 对于多线程之间的通信,RT-Thread官网提供了比拟丰盛的文档作为参考,具体能够查看以下链接:https://www.rt-thread.org/doc... 本文尝试从以下几个方面总结一下RT-Thread线程间通信的学习过程线程间通信的相干概念 什么是线程间通信?通信,顾名思义,就是单方须要进行沟通与对话。艰深地概括,就是A线程在工作运行期间,有某些数据或者信息,要通知B线程,让B线程接管到这些数据或信息后,可能持续实现指定的工作和工作。 两个线程之间为什么要进行通信呢?还是那句话,多个工作线程并不是独立的,它们在工作的时候是须要依据业务场景进行肯定的沟通的,还是以音乐播放器举例,当歌词读取线程把歌词从硬盘外面读出来了,要把这一串读到的歌词通知给显示线程,让它把歌词显示进去。这个“通知”的动作,就是通过线程间通信来进行的。2.png既然都是为了协调线程的工作状态,线程间同步和线程间通信这两者有什么区别呢?区别就是线程间同步能做的事件太无限了,线程间同步只是通知一下对方“别跑太快,等等我嘛~”,而线程间通信,就是有一大堆的数据和信息要告知对方,万一A线程有很多话要跟B线程说,线程同步这种形式就不能满足要求了,所以须要线程间通信。线程间通信的形式 针对RT-Thread实时操作系统,线程间通信次要有三种形式:邮箱,音讯队列,信号。这三种线程间通信机制都有各自的特点,在理论开发工作外面,须要依据不同的利用场景进行辨别应用。 邮箱是线程间通信的其中一种形式,这个邮箱的概念,跟咱们生存中应用的邮箱概念,其实是大同小异的,在生活中,如果咱们有函件要寄,就把函件往邮筒一扔就能够了,邮局会负责把函件送往目的地。 同样的情理,当A线程有函件(即数据)要发送给B线程,只须要调用操作系统提供的邮箱相干接口函数,把数据发送进来,操作系统就会负责把数据转发到指标线程,整个转发过程是怎么实现的,收和发的线程都不须要关怀。 应用邮箱进行线程间通信,特点是开销低,效率高。这是因为,每个邮件信息最多只能是4个字节的内容,所以,这个邮件信息能够是某个数据块的指针,通过指针传递的形式,来传输更多的数据。 邮箱在应用过程中,可能会存在邮箱空或邮箱满的状况,在邮箱空的状况下,接管邮件的线程会抉择挂起期待,或者等超时工夫到来。在邮箱满的状况下,发送邮件的线程会抉择挂起或间接返回一个邮箱满的返回值。 零碎内核提供以下邮箱相干的API函数接口,如下图所示。音讯队列是另外一种比拟罕用的线程间通信形式,相当于邮箱的扩大。跟邮箱不同的是,音讯队列是能够接管不定长的数据的,并且把这个不定长的数据复制到本身线程的内存空间。 音讯队列其实就是一个数据存储空间,这个存储空间遵循先进先出的准则,也就是说,不论是什么音讯,期待音讯的线程取得的是最先进入队列的音讯。 音讯队列管制块外面,其实有两个链表,一个链表是用来挂接空的音讯块(也就是没有内容的音讯队列),另一个链表是用来挂接存有音讯的音讯块,具体形象如下图所示。 当线程A要发送一个音讯时,先从闲暇音讯块链表取出一个块空间,把音讯装进去后,把这个音讯块挂接到非空音讯块链表的队尾。如果应用紧急形式发送音讯,则把该音讯块挂接到非空音讯链表的队首。线程获取音讯的时候,总是会获取链表头的音讯的。 零碎内核提供以下音讯队列相干的API函数接口,如下图所示。 信号,在软件档次上其实相当于一种软中断的形式,这种中断机制是操作系统模仿进去的,一个线程收到一个信号,跟硬件处理器收到一个硬件中断请求,这个过程基本上是相似的。 当一个线程在失常运行期间,如果其余线程有突发的事件或异样告诉须要解决,就能够通过信号的形式发送进来,线程在失常运行期间不须要期待信号的到来(因为不晓得信号什么时候会到来)。 收到信号的线程,对各种信号的解决有以下三种办法:1、相似中断的处理程序,能够针对须要解决的信号指定处理函数,由该函数来解决。2、间接疏忽某个信号,对该信号不做任何解决,就像未产生过一样。3、应用零碎保留的默认值来解决该信号。 零碎内核提供以下信号相干的API函数接口,如下图所示。多线程通信的利用示例 多线程通信的利用示例,次要是为了验证邮箱,音讯队列,信号的API接口函数,并且通过试验景象察看这三种线程通信形式的运行状况。 示例源码下载链接:https://github.com/embediot/r... 邮箱示例次要是初始化了2个动态线程,一个动态的邮箱对象,线程 2 发送邮件,共发送 11 次,线程 1 接管邮件,共接管到 11 封邮件,将邮件内容打印进去,并判断完结。 音讯队列示例次要初始化了2个动态线程,线程 1 会从音讯队列中收取音讯,线程 2 定时给音讯队列发送一般音讯和紧急音讯。因为线程 2 发送音讯 “I” 是紧急音讯,会直接插入音讯队列的队首,所以线程 1 在接管到音讯 “B” 后,接管的是该紧急音讯,之后才接管音讯“C”。 信号示例次要是创立了 1 个线程,在装置信号时,信号处理形式设为自定义解决,定义的信号的处理函数为 thread1_signal_handler(),待此线程运行起来装置好信号之后,给此线程发送信号,此线程将接管到信号,并打印信息。 具体示例的实现能够查看工程源码,在thread_communication.h头文件中,关上相应的宏定义开关,从新编译工程并下载到开发板即可。线程间通信的注意事项 在进行多线程间通信的时候,对于邮箱、音讯队列、信号这三种线程间通信形式,有以下一些注意事项: 1.应用邮箱进行线程间通信时,因为一封邮件最多只能是4个字节长度,因而如果要传递较多数据信息,能够应用构造体进行信息封装,通过指针形式进行传递。 2.邮件发送是非阻塞的,因而能够利用于中断服务程序中。但邮件接管是阻塞的,能够设置接管超时的工夫,不能在中断服务程序外面应用邮件接管。 3.当邮箱没有邮件且超时工夫不为0 ,邮件的接管过程主动变为阻塞形式。当邮箱满了后,发送线程能够抉择挂起期待或间接返回邮箱满的错误码。 4.音讯队列是一种异步的通信形式,音讯队列外面的音讯总是遵循先进先出的准则。 5.能够在线程或中断服务程序外面能够给音讯队列发送音讯,但不能在中断服务程序外面接管音讯。 6.能够往音讯队列外面发送紧急音讯,紧急音讯会被搁置到音讯队列的链表头,会首先被期待的线程获取。 7.信号跟信号量不同,不能混同两者的概念,信号是软件层面上的一种软中断形式。 8.线程不会用阻塞的形式期待信号的到来,因为线程本身也不晓得这个信号(软中断)什么时候会到。 9.线程对信号的解决,能够设置为捕获信号,疏忽信号,应用默认形式解决信号。 原文链接:https://club.rt-thread.org/as...

March 15, 2021 · 1 min · jiezi

关于操作系统:RTThread学习笔记-4RTThread多线程学习过程总结

多线程是实时操作系统外面最重要的知识点之一,要学习RTOS,多线程是必须(没错,是必须)要熟练掌握的内容,只有熟练掌握多线程的应用,能力在平时的我的项目工作外面用好实时操作系统。 对于多线程的应用和治理,RT-Thread官网提供了比拟丰盛的文档作为参考,具体能够查看以下链接:https://www.rt-thread.org/doc...,本文是对RT-Thread多线程学习后的总结,并尝试从如图所示的以下几个方面进行总结。什么是多线程? 在单片机上学习RT-Thread的多线程之前,要先把“过程”这个概念先放一边,因为单片机是没有多过程概念的。单片机运行操作系统,不论多少个工作,他们都是多个(或单个)线程之间进行解决这些工作,单片机个别不波及多过程。 什么是多线程?在哪些状况下要用到多线程?先来举一个音乐播放器的例子,这个音乐播放器要做以下这些根本的工作:读取音乐文件并播放、读取歌词并显示、读取MV文件并播放。 如果这三个根本的工作不必多线程来实现,单片机应用裸机的形式去做这三个工作的话,必然会造成音乐播放卡顿,歌词显示不同步,MV视频播放与音乐不同步。 因为单片机做这三件事件的时候,是Step by Step的,必须实现一件事件之后,再去做下一件事件,这三件事件是有先后顺序的,并且一直循环反复,如下图所示。 而如果采纳多线程这种形式来实现这个工作,这个过程就变得绝对简略了,比方针对音乐播放器这个场景,能够设计这几个线程来解决:音乐文件读取线程,歌词文件读取线程,MV文件读取线程,音视频和歌词显示线程。 (此处只为举例形容多线程的概念,不思考音视频编解码的简单过程,不思考线程同步,实际上音乐播放器的实现比此处形容更简单) 音乐文件读取线程只负责从磁盘读取音乐文件,歌词文件读取线程和MV文件读取线程也是同样的情理,它们只做文件读取工作,而音视频和歌词显示线程,是负责把读取到的数据进行显示。这几个线程的工作过程,如下图所示。 如上图所示,这几个工作看上去是“同时”进行的,每个工作都只实现本人的事件,通过多线程,就能够把本来串行实现的工作改为并行实现,大大提高了工作效率。 所以,艰深地对多线程进行了解,就是把一个比拟大型的工作,拆分为多个小型的工作,而后通过正当的调度形式,让这几个小型的工作“同时”运行,当这几个小型工作实现后,大型的工作也随之实现,这样能够大大提高工作的实现效率。多线程的几种状态 对于运行RT-Thread操作系统,线程都处于以下五种状态的其中一种(初始状态、就绪状态、运行状态、挂起状态、敞开状态),通过调用操作系统提供的接口函数,能够让线程在这五种状态中进行来回切换。 对于这五种线程状态的形容,如下表所示:多线程的API函数 如上图的状态机所示,多线程能够通过调用零碎提供的函数接口,在多个状态之间进行切换。这些API函数在官网提供的参考文档外面都有具体的阐明形容,以下列举一些比拟罕用的函数接口。多线程的利用示例 多线程的利用示例,次要是为了验证以上的多线程API接口函数,并且通过试验景象察看多线程的运行状况,次要有以下三个示例: 示例源码下载链接:https://github.com/embediot/r... 1、线程动态创建与动态创立、线程退出示例。 这个示例次要是通过动静形式创立线程1,,通过动态形式创立线程2,线程1的优先级比线程2的优先级低,因而能够被线程2抢占。线程2运行10次后就会被动退出,初始化代码如下图所示。 2、雷同优先级线程的工夫片轮转调度示例。 这个示例次要是通过动静形式创立线程1和线程2,这两个线程都是雷同的优先级,并且共用一个线程入口函数,次要是通过传入不同的线程参数以辨别线程1和线程2。线程2运行所占用的工夫片比线程1要少,因而线程2运行的工夫比拟短,初始化代码如下图所示。 3、线程调度器的钩子函数应用示例。 这个示例次要测试了线程在进行调度时,对于钩子函数的调用状况。通过线程调度器的钩子函数,打印出线程间的切换信息,初始化的代码如下图所示。多线程利用的注意事项 在应用RT-Thread实时操作系统进行多线程利用开发的时候,应该要留神以下事项: 1.RT-Thread的线程调度器是抢占式的,也就是可能保障就绪队列外面,最高优先级的工作总能取得CPU的使用权,在工作设计的时候,要充分考虑好工作的优先级。 2.在硬件中断服务程序运行期间,如果有高优先级的工作就绪,那么被中断的低优先级工作将被挂起,高优先级的工作将会取得CPU的使用权。 3.每个线程都有独立的线程栈,用来保留线程调度时上下文的信息,因而在创立线程调配栈空间的时候,要充分考虑栈的大小。 4.在线程的循环体外面,应该要设置某些条件,在必要的时候被动让出CPU的使用权,特地对于高优先级的线程,如果程序外面有死循环操作而又不被动让出CPU使用权,那么这个线程将会始终占用CPU,并且低优先级的线程永远不会被调度执行。 5.对于没有始终循环执行的线程,线程执行结束后,资源的回收状况实际上是在闲暇线程外面进行的,线程变为敞开状态后,不代表资源马上被回收。 6.零碎闲暇线程是最低优先级且永远为就绪状态的,闲暇线程是一个死循环,永远不会被挂起,但能够被其余高优先级工作抢占,闲暇线程次要执行僵尸线程的资源回收工作。 7.闲暇线程也能够设置钩子函数,用来进行功耗治理,看门狗喂狗等工作。 8.通过动静形式创立的线程,须要设置好零碎堆内存的大小,而通过动态形式创立的线程,线程栈和线程句柄在程序编译的时候就曾经确定,不能被动态分配,也不能被开释。 9.大多数线程都是在一直循环执行的,无需进行删除,个别不举荐被动删除线程。线程运行结束后,系统调度器将会主动把线程退出僵尸队列,资源回收工作将在闲暇线程外面进行。 感激浏览! 原文链接:https://club.rt-thread.org/as...

March 11, 2021 · 1 min · jiezi

关于操作系统:RTThread学习笔记-3RTThread自动初始化机制分析

置信不少工程师在浏览RT-Thread相干源代码的时候,都会常常看到如下图所示的宏定义,依照宏定义的命名来了解,这些宏定义仿佛都是对一些初始化函数进行某些申明工作。 如上图所示,通过对源码的跟踪发现,这些INIT_XXX_EXPORT的宏定义,最终都是调用了INIT_EXPORT这个宏定义,而这个宏定义,就是把该初始化函数放在自定义的rti_fn符号段外面,源码在rtdef.h头文件外面,如下图所示。 把初始化函数放到自定义的符号段外面,有什么作用呢?答案就是,能够通过这种形式,让这些初始化函数被隐式调用,不必手动往RT-Thread的初始化过程外面增加该函数。 什么是隐式调用?隐式调用的意思就是,当咱们往工程代码外面增加某个零碎组件或外接设备的时候,这个组件或设施都须要进行初始化,而这个初始化函数,咱们不须要在main函数或RT-Thread的启动函数外面间接增加调用,这样能够防止批改RT-Thread的启动过程代码。 先来看一下RT-Thread的启动函数调用流程,注意红色方框外面的内容,如下图所示。启动函数外面,有两个函数:rt_components_board_init() 与 rt_components_init()是专门用来解决主动初始化的,这两个函数的原型和正文,如下图所示。 从下面的函数原型能够看出,这两个函数都是从符号段区间外面,通过for循环不断遍历符号段外面的初始化函数,并获取这些初始化函数的指针,而后进行调用,以达到对设施或组件初始化的目标。 rt_components_board_init()函数最先执行,这个函数是用来初始化芯片相干的硬件的,这个函数会遍历用 INIT_BOARD_EXPORT(fn)申明的函数列表。 rt_components_init()函数是在系统启动后,在main线程外面被调用执行,这个函数是用来初始化其余用 INIT_XXX_EXPORT(fn)申明的函数列表的。 目前RT-Thread内核外面,用来实现主动初始化性能的宏定义接口,如下图所示。 综上所述,要应用RT-Thread的主动初始化流程,能够概括为以下如图所示的步骤。 为什么初始化函数退出了符号段之后,就能够被主动调用?符号段是什么?应用这种形式有什么益处? 把函数退出符号段,其实就是应用了MDK编译器的__attribute__((section(x)))关键字,对函数进行申明,通过section关键字进行申明的函数,在编译器进行链接的时候,就会主动收集这些函数并把他们放到一个集中的区域外面,查看以下.map文件可知。如上图红框所示,rt_hw_pin_init和rt_hw_usart_init都是应用 INIT_BOARD_EXPORT(fn)申明的函数,因而,它们是寄存在橙色竖线所在的区间的,应用rt_components_board_init()函数就能够对这个区间进行遍历。 如上图蓝框和绿框所示,它们别离是用 INIT_COMPONENT_EXPORT(fn)和 INIT_APP_EXPORT(fn)申明的函数,这些函数是寄存在红色竖线所在的区间的,应用rt_components_init()函数就能够对这个区间进行遍历。 从下面的剖析能够看出,应用符号段的形式来寄存初始化函数,益处就是当我须要增加某一个初始化函数的时候,就不须要再去改变RT-Thread的启动代码了,间接通过section关键字,把初始化函数增加到相应的符号段即可。https://club.rt-thread.org/as...感激浏览! 原文链接:https://club.rt-thread.org/as...

March 10, 2021 · 1 min · jiezi

关于操作系统:RTThread学习笔记-3RTThread自动初始化机制分析

置信不少工程师在浏览RT-Thread相干源代码的时候,都会常常看到如下图所示的宏定义,依照宏定义的命名来了解,这些宏定义仿佛都是对一些初始化函数进行某些申明工作。 如上图所示,通过对源码的跟踪发现,这些INIT_XXX_EXPORT的宏定义,最终都是调用了INIT_EXPORT这个宏定义,而这个宏定义,就是把该初始化函数放在自定义的rti_fn符号段外面,源码在rtdef.h头文件外面,如下图所示。 把初始化函数放到自定义的符号段外面,有什么作用呢?答案就是,能够通过这种形式,让这些初始化函数被隐式调用,不必手动往RT-Thread的初始化过程外面增加该函数。 什么是隐式调用?隐式调用的意思就是,当咱们往工程代码外面增加某个零碎组件或外接设备的时候,这个组件或设施都须要进行初始化,而这个初始化函数,咱们不须要在main函数或RT-Thread的启动函数外面间接增加调用,这样能够防止批改RT-Thread的启动过程代码。 先来看一下RT-Thread的启动函数调用流程,注意红色方框外面的内容,如下图所示。启动函数外面,有两个函数:rt_components_board_init() 与 rt_components_init()是专门用来解决主动初始化的,这两个函数的原型和正文,如下图所示。 从下面的函数原型能够看出,这两个函数都是从符号段区间外面,通过for循环不断遍历符号段外面的初始化函数,并获取这些初始化函数的指针,而后进行调用,以达到对设施或组件初始化的目标。 rt_components_board_init()函数最先执行,这个函数是用来初始化芯片相干的硬件的,这个函数会遍历用 INIT_BOARD_EXPORT(fn)申明的函数列表。 rt_components_init()函数是在系统启动后,在main线程外面被调用执行,这个函数是用来初始化其余用 INIT_XXX_EXPORT(fn)申明的函数列表的。 目前RT-Thread内核外面,用来实现主动初始化性能的宏定义接口,如下图所示。 综上所述,要应用RT-Thread的主动初始化流程,能够概括为以下如图所示的步骤。 为什么初始化函数退出了符号段之后,就能够被主动调用?符号段是什么?应用这种形式有什么益处? 把函数退出符号段,其实就是应用了MDK编译器的__attribute__((section(x)))关键字,对函数进行申明,通过section关键字进行申明的函数,在编译器进行链接的时候,就会主动收集这些函数并把他们放到一个集中的区域外面,查看以下.map文件可知。如上图红框所示,rt_hw_pin_init和rt_hw_usart_init都是应用 INIT_BOARD_EXPORT(fn)申明的函数,因而,它们是寄存在橙色竖线所在的区间的,应用rt_components_board_init()函数就能够对这个区间进行遍历。 如上图蓝框和绿框所示,它们别离是用 INIT_COMPONENT_EXPORT(fn)和 INIT_APP_EXPORT(fn)申明的函数,这些函数是寄存在红色竖线所在的区间的,应用rt_components_init()函数就能够对这个区间进行遍历。 从下面的剖析能够看出,应用符号段的形式来寄存初始化函数,益处就是当我须要增加某一个初始化函数的时候,就不须要再去改变RT-Thread的启动代码了,间接通过section关键字,把初始化函数增加到相应的符号段即可。https://club.rt-thread.org/as...感激浏览! 原文链接:https://club.rt-thread.org/as...

March 10, 2021 · 1 min · jiezi

关于操作系统:RTThread学习笔记-2RTThread启动过程分析

在一些不应用操作系统的单片机软件工程外面,除了汇编启动文件之外,普遍认为程序入口就是main函数,很多程序代码都是从main函数开始进行剖析的。 而对于RT-Thread实时操作系统,程序在跑到main函数之前,其实是进行了一系列的启动流程初始化工作,而这些初始化操作是针对RT-Thread内核和具体的板卡进行的,用户不须要干涉这个启动流程。 在进入main函数之前,RT-Thread进行了如图所示的启动操作。 不带操作系统的单片机程序,个别都会从启动文件startup_xx.s间接跳转到main函数开始执行,而带RT-Thread操作系统的程序,在进入main函数之前,还进行了如上图所示的一系列操作。以上的操作看似简单繁多,但其实次要是在调用main函数之前,调用了rtthread_startup函数。对于如何在调用main函数之前,调用rtthread_startup函数,不同的编译器有不同的操作。 对于MDK编译器,次要是应用了MDK的扩大性能 $Sub和$Super ,而对于IAR编译器,则是通过__low_level_init()函数,对于GCC编译器,则是通过entry函数,这些函数都是会在调用main函数之前被调用的。 以MDK编译器为例,给main函数增加一个 $Sub$$ 前缀,就造成了一个新的性能函数,这个性能函数会在调用main函数之前被调用,这是MDK编译器所规定的,具体能够查看以下链接:ARM® Compiler v5.06 for µVision®armlink User Guide 对于程序从启动文件跳转到main函数入口的关系,总结概括如下图所示。 在$Sub$$main函数外面,次要是调用了rtthread_startup()函数,这个函数是RT-Thread规定的对立启动入口,这个函数次要进行了如图所示的一系列初始化工作。 以下是对于rtthread_startup()函数外面各个函数的具体阐明。 1.对于rt_hw_board_init()函数,次要是初始化了中断向量表,实现了零碎时钟的初始化,如果有应用到零碎组件的话,同时初始化零碎组件,并且设置打印信息的输入控制台,同时初始化零碎堆内存,程序代码如下图所示。 2.对于rt_show_version()函数,次要是在信息控制台初始化胜利后,打印RT-Thread内核的零碎版本信息,这个函数的具体实现,如下图所示。 3.对于rt_system_timer_init()和rt_system_scheduler_init()函数,次要是初始化了零碎定时器链表和RT-Thread系统调度器,因为调度器的实现原理略为简单,此处暂不开展阐述。 4.对于rt_application_init()函数,次要是创立了一个名为main的主线程,这个线程的函数入口是main_thread_entry,这里有两种创立形式,二选一,如果应用了零碎堆内存,则应用动态创建的形式,线程应用的内存资源能够动静进行申请或开释,如果没有应用零碎堆内存,则应用动态创立的形式,线程应用的内存资源是固定好的,不能被开释,函数实现如下图所示。 5.对于rt_system_timer_thread_init()函数,次要是初始化软件定时器的列表,并且采纳动态形式创立一个名为timer的软件定时器,并且把软件定时器线程放入调度器外面,函数实现如下图所示。 6.对于rt_thread_idle_init()函数,次要是依据芯片CPU的数量,应用动态形式创立闲暇线程,实际上,闲暇线程并不闲暇,这个线程在零碎没有任何用户线程调度的时候,就会被调度起来,这个闲暇线程次要是查看零碎有没有曾经沦亡的线程,如果有,则把沦亡线程的资源进行回收,如果零碎使能了电源治理,则会让零碎进行低功耗模式,函数的具体实现,如下图所示。 7.对于rt_system_scheduler_start()函数,次要是开始使能操作系统调度器,调度器启动后,会依据零碎的调度规定,从线程就绪列表外面,抉择优先级最高的线程进行启动。 8.从以上剖析可知,RT-Thread零碎在启动的时候,至多会启动一个main主线程和一个idle闲暇线程,如果系统配置有使能软件定时器,还会启动一个timer定时器线程,也就是说,零碎一旦启动后,就会有两个(或三个)线程在进行调度,如下图所示。 感激浏览! 原文链接:https://club.rt-thread.org/as...

March 10, 2021 · 1 min · jiezi

关于操作系统:RTThread学习笔记-2RTThread启动过程分析

在一些不应用操作系统的单片机软件工程外面,除了汇编启动文件之外,普遍认为程序入口就是main函数,很多程序代码都是从main函数开始进行剖析的。 而对于RT-Thread实时操作系统,程序在跑到main函数之前,其实是进行了一系列的启动流程初始化工作,而这些初始化操作是针对RT-Thread内核和具体的板卡进行的,用户不须要干涉这个启动流程。 在进入main函数之前,RT-Thread进行了如图所示的启动操作。 不带操作系统的单片机程序,个别都会从启动文件startup_xx.s间接跳转到main函数开始执行,而带RT-Thread操作系统的程序,在进入main函数之前,还进行了如上图所示的一系列操作。以上的操作看似简单繁多,但其实次要是在调用main函数之前,调用了rtthread_startup函数。对于如何在调用main函数之前,调用rtthread_startup函数,不同的编译器有不同的操作。 对于MDK编译器,次要是应用了MDK的扩大性能 $Sub和$Super ,而对于IAR编译器,则是通过__low_level_init()函数,对于GCC编译器,则是通过entry函数,这些函数都是会在调用main函数之前被调用的。 以MDK编译器为例,给main函数增加一个 $Sub$$ 前缀,就造成了一个新的性能函数,这个性能函数会在调用main函数之前被调用,这是MDK编译器所规定的,具体能够查看以下链接:ARM® Compiler v5.06 for µVision®armlink User Guide 对于程序从启动文件跳转到main函数入口的关系,总结概括如下图所示。 在$Sub$$main函数外面,次要是调用了rtthread_startup()函数,这个函数是RT-Thread规定的对立启动入口,这个函数次要进行了如图所示的一系列初始化工作。 以下是对于rtthread_startup()函数外面各个函数的具体阐明。 1.对于rt_hw_board_init()函数,次要是初始化了中断向量表,实现了零碎时钟的初始化,如果有应用到零碎组件的话,同时初始化零碎组件,并且设置打印信息的输入控制台,同时初始化零碎堆内存,程序代码如下图所示。 2.对于rt_show_version()函数,次要是在信息控制台初始化胜利后,打印RT-Thread内核的零碎版本信息,这个函数的具体实现,如下图所示。 3.对于rt_system_timer_init()和rt_system_scheduler_init()函数,次要是初始化了零碎定时器链表和RT-Thread系统调度器,因为调度器的实现原理略为简单,此处暂不开展阐述。 4.对于rt_application_init()函数,次要是创立了一个名为main的主线程,这个线程的函数入口是main_thread_entry,这里有两种创立形式,二选一,如果应用了零碎堆内存,则应用动态创建的形式,线程应用的内存资源能够动静进行申请或开释,如果没有应用零碎堆内存,则应用动态创立的形式,线程应用的内存资源是固定好的,不能被开释,函数实现如下图所示。 5.对于rt_system_timer_thread_init()函数,次要是初始化软件定时器的列表,并且采纳动态形式创立一个名为timer的软件定时器,并且把软件定时器线程放入调度器外面,函数实现如下图所示。 6.对于rt_thread_idle_init()函数,次要是依据芯片CPU的数量,应用动态形式创立闲暇线程,实际上,闲暇线程并不闲暇,这个线程在零碎没有任何用户线程调度的时候,就会被调度起来,这个闲暇线程次要是查看零碎有没有曾经沦亡的线程,如果有,则把沦亡线程的资源进行回收,如果零碎使能了电源治理,则会让零碎进行低功耗模式,函数的具体实现,如下图所示。 7.对于rt_system_scheduler_start()函数,次要是开始使能操作系统调度器,调度器启动后,会依据零碎的调度规定,从线程就绪列表外面,抉择优先级最高的线程进行启动。 8.从以上剖析可知,RT-Thread零碎在启动的时候,至多会启动一个main主线程和一个idle闲暇线程,如果系统配置有使能软件定时器,还会启动一个timer定时器线程,也就是说,零碎一旦启动后,就会有两个(或三个)线程在进行调度,如下图所示。 感激浏览! 原文链接:https://club.rt-thread.org/as...

March 10, 2021 · 1 min · jiezi

关于操作系统:ASPLOS2020Elastic-Cuckoo-Page-Tables

☕️ 并行化地址翻译论文浏览笔记 弹性布谷哈希页表 Elastic Cuckoo Page Tables: Rethinking Virtual Memory Translation for Parallelism https://dl.acm.org/doi/pdf/10... ???? 简介???? 根本思维将传统多层基数页表的程序指针追踪,转化为齐全并行查找,第一次实现了内存级并行的地址翻译。 ???? 背景地址转换是存取的性能瓶颈传统多层基数页表的程序指针追踪,没有充分利用内存级并行,理当被取代。哈希页表局限:空间局部性的缺失;每个页表项都须要哈希标签,耗费更多空间;哈希抵触须要屡次存取操作。(前两项可通过精心设计页表[73]来解决,页表项汇集和压缩)凋谢地址、链地址需昂扬存取开销。为防止哈希抵触,哈希页表需动静调整大小,开销大。繁多全局哈希页表的毛病:在不减少额定地址翻译层下,不反对过程间页表共享、多种页表大小;完结过程时,需线性扫描整个页表,删除相干项。可调整大小的哈希页表:超过占用阈值,需调配新的大哈希表,需从新哈希、迁徙到新哈希表,十分耗时。渐进式可调整大小的哈希页表:保护新、旧哈希表约需两倍内存开销;需查找新、旧页表,两倍cache存取时间。布谷哈希:多个哈希函数,踢出法。???? 弹性布谷页表地址翻译时应用齐全并行查找,升高指针追踪开销。应用弹性布谷哈希,这种哈希无效反对页表大小动静调整。这种页表可无效解决哈希抵触,提供过程公有页表,反对多种页表大小,反对过程间共享页表,以及按需动静调整页表大小。试验:8核处理器全系统模拟,包含图剖析、生物信息学、HPC、零碎负载试验。与传统基数页表相比,在地址转换上有41%晋升,在应用程序执行上有3-18%的减速。???? 弹性布谷哈希$d$-路布谷哈希。 从旧$d$-表路中随机选取一路$T_i$,用哈希函数$H_i$,若哈希值落在live区,直接插入;否则,应用新的$d$-路表的同一路$T_i'$的哈希函数$H_i'$,被踢出的元素插入$T_i'$。 查找元素只需$d$次探查:应用旧的$d$-表的所有$H_D$函数,对每一路,若哈希值落在live区,则探查到,否则应用$T_i'$。 从两方面改良布谷哈希的渐进式扩大:$d$次探查(存取),而非$2d$次;不用从旧表的迁徙区取条目到cache中。 ???? Rehash ???? Look-up 能够并行化。 例子: case1:探查$T_1[H_1(x)],T_2[H_2(x)]$ case2:探查$T_1'[H_1'(x)],T_2'[H_2'(x)]$ case3:探查$T_1'[H_1'(x)],T_2[H_2(x)]$ case4:探查$T_1[H_1(x)],T_2'[H_2'(x)]$ ???? Delete查找到后,革除条目。耗时与Look-up雷同。 ???? Insert$d$ 组中随机选取一组$i$若$H_i(x) \ge P_i $,元素插入$T_i[H_i(x)]$地位;否则,插入$T_i'[H_i'(x)]$ 地位若原来的地位有元素$y$,则$y$被踢出。从剩下的路中随机选一组,对$y$进行同样的插入操作。 ⊥示意空值 ???? Hash Table Resize参数:再哈希阈值 $r_t$ ,乘法因子 $k$。 一个哈希表的占有率达到 $r_t$ 时,调配一个新的哈希表,其大小为原来的 $k$ 倍。选$ r_t $ ,使得有较小的插入抵触和渺小的插入失败。$d$ = 3 时,较好的 $r_t$ 为0.6或更小。选$k$, 使得不占用过多间断内存,且不造成过多页表大小调整。$ k > (r_t + 1) / r_t $ (附录A)若在调整大小之外产生插入失败,则触发调整大小;若在调整大小期间产生插入失败,则将多个元素散列到其余中央,再从新插入。 试验证实,选取适合的 $r_t$,可完全避免插入失败。downsize参数:downsize阈值 $d_t$, downsize 因子 $g$。???? 弹性布谷哈希页表???? 结构 ...

March 3, 2021 · 1 min · jiezi

关于操作系统:啃碎操作系统一操作系统概念

前言最近在看《古代操作系统》这本书,想写几篇文章记录一下。 注释什么是操作系统?古代计算机系统由一个或多个处理器、主存、打印机、键盘、鼠标、显示器、网络接口以及各种输出/输出设备形成。 一般而言,古代计算机是一个简单的零碎。如果每位利用程序员都不得不把握零碎的所有细节,那就不可能再编写代码了。所以,计算机装置了一层软件,称为操作系统,它的工作是为用户程序提供一个更好、更简略、更清晰的计算机模型,并治理方才提到的所有设施。 这是一个操作系统的简化图,最上面的是硬件,硬件包含芯片、电路板、磁盘、键盘、显示器等咱们下面提到的设施,在硬件之上是软件。大部分计算机有两种运行模式:内核态 和 用户态,软件中最根底的局部是操作系统,它运行在 内核态 中。操作系统具备硬件的拜访权,能够执行机器可能运行的任何指令。软件的其余部分运行在 用户态 下。 计算机硬件介绍操作系统与运行操作系统的内核硬件关系密切。操作系统扩大了计算机指令集并治理计算机的资源。因而,操作系统因而必须足够理解硬件的运行,这里咱们先简要介绍一下古代计算机中的计算机硬件。 从概念上来看,一台简略的个人电脑能够被形象为下面这种类似的模型,CPU、内存、I/O 设施都和总线串联起来并通过总线与其余设施进行通信。 CPUCPU 是计算机的大脑,它次要和内存进行交互,从内存中提取指令并执行它。一个 CPU 的执行周期是从内存中提取第一条指令、解码并决定它的类型和操作数,执行,而后再提取、解码执行后续的指令。反复该循环直到程序运行结束。 因为拜访内存获取执行或数据要比执行指令破费的工夫长,因而所有的 CPU 外部都会蕴含一些寄存器来保留要害变量和长期后果。因而,在指令集中通常会有一些指令用于把关键字从内存中加载到寄存器中,以及把关键字从寄存器存入到内存中。还有一些其余的指令会把来自寄存器和内存的操作数进行组合,例如 add 操作就会把两个操作数相加并把后果保留到内存中。 除了用于保留变量和长期后果的通用寄存器外,大多数计算机还具备几个非凡的寄存器,这些寄存器对于程序员是可见的。其中之一就是 程序计数器(program counter),程序计数器会批示下一条须要从内存提取指令的地址。提取指令后,程序计数器将更新为下一条须要提取的地址。 内存计算机中第二个次要的组件就是内存。现实状况下,内存应该十分疾速(比执行一条指令要快,从而不会拖慢 CPU 执行效率),而且足够大且便宜,然而目前的技术手段无奈满足三者的需要。于是采纳了不同的解决形式,存储器零碎采纳一种分档次的构造: 顶层的存储器速度最高,然而容量最小,老本十分高,层级构造越向下,其拜访效率越慢,容量越大,然而造价也就越便宜。 寄存器存储器的顶层是 CPU 中的寄存器,它们用和 CPU 一样的资料制成,所以和 CPU 一样快。程序必须在软件中自行治理这些寄存器(即决定如何应用它们) 高速缓存位于寄存器上面的是高速缓存。当应用程序须要从内存中读取关键词的时候,高速缓存的硬件会查看所须要的高速缓存行是否在高速缓存中。如果在的话,那么这就是高速缓存命中(cache hit)。高速缓存满足了该申请,并且没有通过总线将内存申请发送到主内存。高速缓存命中通常须要破费两个时钟周期。缓存未命中须要从内存中提取,这会耗费大量的工夫。高速缓存行会限度容量的大小因为它的造价十分低廉。 主存在下面的层次结构中再下一层是主存,这是内存零碎的主力军,主存通常叫做 RAM(Random Access Memory)。所有不能再高速缓存中失去满足的内存拜访申请都会转往主存中。 除了主存之外,许多计算机还具备大量的非易失性随机存取存储器。它们与 RAM 不同,在电源断电后,非易失性随机拜访存储器并不会失落内容。ROM(Read Only Memory) 中的内容一旦存储后就不会再被批改。它十分快而且便宜。 磁盘下一个档次是磁盘(硬盘),磁盘同 RAM 相比,每个二进制位的成本低了两个数量级,而且常常也有两个数量级大的容量。磁盘惟一的问题是随机拜访数据工夫大概慢了三个数量级。 I/O 设施 CPU 和存储器不是操作系统须要治理的全副,I/O 设施也与操作系统关系密切。能够参考下面这个图片,I/O 设施个别包含两个局部:设施控制器和设施自身。控制器自身是一块芯片或者一组芯片,它可能管制物理设施。它可能接管操作系统的指令,例如,从设施中读取数据并实现数据的解决。 在许多状况下,理论管制设施的过程是非常复杂而且存在诸多细节。因而控制器的工作就是为操作系统提供一个更简略(但依然非常复杂)的接口。也就是屏蔽物理细节。任何简单的货色都能够加一层代理来解决,这是计算机或者人类社会很普世的一个解决方案。 总线总线(Bus)是计算机各种性能部件之间传送信息的公共通信支线,它是由导线组成的传输线束, 依照计算机所传输的信息品种,计算机的总线能够划分为数据总线、地址总线和管制总线,别离用来传输数据、数据地址和管制信号。总线是一种内部结构,它是cpu、内存、输出、输出设备传递信息的专用通道,主机的各个部件通过总线相连接,外部设备通过相应的接口电路再与总线相连接,从而造成了计算机硬件零碎。 计算机启动过程那么有了下面一些硬件再加上操作系统的反对,咱们的计算机就能够开始工作了,那么计算机的启动过程是怎么的呢?上面只是一个简要版的启动过程: 在每台计算机上有一块双亲板,也就是母板,母板也就是主板,它是计算机最根本也就是最重要的部件之一。主板个别为矩形电路板,下面装置了组成计算机的次要电路系统,个别有 BIOS 芯片、I/O 管制芯片、键盘和面板管制开关接口、指示灯插接件、裁减插槽、主板及插卡的直流电源供电接插件等元件。 在母板上有一个称为 根本输入输出零碎(Basic Input Output System, BIOS)的程序。在 BIOS 内有底层 I/O 软件,包含读键盘、写屏幕、磁盘I/O 以及其余过程。现在,它被保留在闪存中,它是非易失性的,然而当BIOS 中发现错误时,能够由操作系统进行更新。 ...

February 8, 2021 · 2 min · jiezi

关于操作系统:积累若干年的Mac奇技淫巧

Mac不出声刚开机出声,过一会声音隐没尝试在:资源库/Audio/Plug-Ins/门路下,删除外面的HAL文件夹 开启任何起源装置任何起源的软件sudo spctl --master-disable 查找TimeMachine备份sudo tmutil listlocalsnapshots /tmutil deletelocalsnapshots 2017-11-27-005359 brew update失败显示proxy unset的时候,预计是git代理生效了 git config --global --unset https.proxygit config --global --unset http.proxy 清理生效图标/private/var/folders/8q/gp77q5m91_q4q9y45mpvrxvc0000gn/0/com.apple.dock.launchpad/db 进入相似门路,应用命令 sqlite3 db "delete from apps where title='利用名称';"&&killall Dock删除对应图标 Mac快捷键向下移行发出声音问题:VSCode编辑器里,向下挪动一行用快捷键会有DuangDuang的声音办法:创立文件及门路~/Library/KeyBindings/DefaultKeyBinding.dict,增加如下内容,重启编辑器{ "@^UF701" = "noop:"; "@^UF702" = "noop:"; "@^UF703" = "noop:"; } Catalina打不开文件已损坏关上终端,输出以下命令,回车sudo xattr -r -d com.apple.quarantine /Applications/xxxx.app 留神:/Applications/xxxx.app 换成你的App门路(将 App 拖到终端里即可) 重启 App。 时间机器备份速度解封sudo sysctl debug.lowpri_throttle_enabled=00是高优先级,疾速,1是默认速度,慢速。 Mac破解TNT证书codesign --force --deep --sign - /Applications/name.app

February 1, 2021 · 1 min · jiezi

关于操作系统:深度解读设备的万能语言HarmonyOS的分布式软总线能力

摘要:本文分享鸿蒙分布式软总线,并对相干源代码进行解析,为在鸿蒙零碎平台上工作的相干人员的信息参考和领导。总线是一种内部结构,在计算机系统中,主机的各个部件通过总线相连,外部设备通过相应的接口电路再与总线相连接,是CPU、内存、输出、输出设备传递信息的专用通道。按所传输的信息品种,可划分为数据、地址和管制总线,别离用来传输数据、数据地址和管制信号。 HarmonyOS零碎的使命和指标是将不同的设施串联,成为设施的“万能语言”,让一个零碎连贯起所有上网的智能设施,实现万物互联的终极目标。其外围能力之一,【分布式软总线】让多设施交融为“一个设施”,带来设施内和设施间高吞吐、低时延、高牢靠的晦涩连贯体验。 本文分享鸿蒙分布式软总线,并对相干源代码进行解析,作为在此平台上工作的相干人员的信息参考和领导。具体开发请参考鸿蒙官网。 1. 介 绍设施的通信形式多种多样,譬如USB、WIFI、BT,通信形式差别大且繁琐,链路的交融、共享、抵触、平安等问题也难以保障。 鸿蒙分布式软总线致力于实现近场设施间对立的分布式通信能力,提供不辨别链路的设施发现和传输接口,具备疾速发现并连贯设施,高效散发工作和传输数据。作为多终端设备的对立基座,是鸿蒙架构中的底层技术,是鸿蒙的大动脉,其总的指标是实现设施间无感发现,零期待传输。对开发者而言,无需关注组网形式与底层协定。 2. 分布式软总线个性鸿蒙分布式软总线的设计指标在于推动极简通信协议技术,在设施平安场景下,即连即用。关键技术个性笼罩设施的主动发现&连贯、组网(多跳自组网、多协定混合组网)、传输(多元化协定与算法、智能感知与决策)。 2.1 设施间自发现&连贯分布式软总线提出主动发现设施,实现用户零期待的自发现体验,左近同账号的设施主动发现无需期待,主动平安连贯。 IoT设施分为发现端和被发现端。发现端个别为申请应用服务的设施或称为主控设施,常指智慧屏设施(如手机、平板等)。被发现端为公布服务的设施,指轻量设施(如AI音箱、智能家居、智能穿戴等设施)。目前软总线的设施互联,需保障发现端和被发现端处于同一个局域网内。 2.2 多设施互联、组网基于网络互联、交互的零碎,开发者往往须要适配不同网络协议和标准规范。而在鸿蒙零碎设定的分布式开发模式中,无需关怀网络协议的差别及组网形式,业务开发与设施组网解耦,仅需监听设施高低线,开发成本大大降低。 分布式软总线提出了异构网络组网,主动构建一个逻辑全连贯网络,以解决设施间不同协定交互的问题。设施上线后会向网络层注册,同时网络层会与设施建设通道连贯,实时检测设施的变换。网络层负责管理设施的上线、下线变换,设施间能够监听本人感兴趣的设施,设施上线后能够立刻与其建设连贯,实现零期待体验。 2.3 多设施间数据传输提供对立的基于Session的认证、传输性能,下层业务零碎能够通过sessionId收发数据或获取其相干根本属性,实现业务音讯、流、控制指令等操作交互。 3. 软总线协定COAP互联网的WEB利用无处不在,很多依赖于REST协定架构。为在大多的受限节点上(如RAM和ROM很无限的8位单片机)及受限网络上(如6LoWPAN)也能反对REST,工程师们着手解决“受限制的restful环境”,即CoRE。如6LoWPAN的受限网络反对将IPv6数据分成小包,但极大升高了传输效率。 CoAP(Constrained Application Protocol)的次要指标之一是设计一个通用的Web协定,放弃非常低的开销,以满足受限环境的特殊要求,如能源、楼宇自动化或其它M2M利用。实现REST的一个通用HTTP子集,针对M2M利用做了简化,而非自觉压缩HTTP。COAP协定可很容易转换为HTTP,不便和现有WEB体系转化,同时还能满足诸如内置发现、组播反对和异步音讯传输等。 3.1 COAP协定特色属于一种应用层协定,运行于UDP协定之上而不是像HTTP那样运行于TCP之上。 1) COAP协定网络传输层由TCP改为UDP; 2) 基于REST,server的资源地址也相似URL格局,客户端同样有POST,GET,PUT,DELETE办法来拜访server,对HTTP做了简化; 3) COAP是二进制格局,HTTP是文本格式,COAP比HTTP更加紧凑; 4) 玲珑、轻量化,最小长度仅仅4 Bytes,一个HTTP的head都要几十Bytes; 5) 反对牢靠传输,数据重传,块传输; 6) 反对IP多播, 可同时向多个设施发送申请,鸿蒙设施的发现性能就是用的这个个性; 7) 非长连贯通信,实用于低功耗物联网场景; 8) 反对察看模式; 3.2 协定类型及构造COAP协定有4种音讯类型。 CON: 须要确认,如果CON申请被发送,那对方必须做出响应,确认收到音讯,用以可靠消息传输;NON: 不须要被确认的申请,如果NON申请被发送,那对方不用作出回应。实用于音讯会反复频繁的发送,丢包不影响失常操作。和UDP很像,用于不可靠消息传输;ACK: 应答音讯,对应的是CON音讯的应答;RST: 复位音讯,牢靠传输时候接管的音讯不意识或谬误时,必须回RST音讯;协定构造定义 在源码discovery/coap/include/coap_def.h中对COAP协定的构造体进行了定义。 3.3 COAP包的传输传输方式为客户端和服务器端模式,服务器端启动COAP包的监听服务。 源码discovery/coap/include/coap_socket.h中提供了COAP包的发送和接管函数定义。 3.4 COAP设施发现源码discovery/coap/source/coap_discover.c实现了基于COAP的设施发现性能。 3.5 COAP的安全性TLS不能用来保障UDP上传输的数据的平安,因而Datagram TLS试图在现存的TLS架构上提出扩大,使之反对UDP。 ...

January 22, 2021 · 1 min · jiezi

关于操作系统:统信-UOS-版完成主流应用适配其中包括微信

近日,统信官网颁布已在 UOS 中实现对罕用支流 Windows 利用的适配, 进入2020年以来,统信软件旗下UOS零碎在生态适配上停顿迅速,反对的各类软硬件产品曾经超过3800款,其中包含微信、QQ、搜狗输入法、360 系列软件、金山 WPS、钉钉、数科阅读器、福昕阅读器等。其中微信版本为原生反对 Linux 的版本,同时反对龙芯、鲲鹏、麒麟等国产 CPU。 据悉,统信 UOS 版的微信桌面客户端由腾讯公司研发,已与统信 UOS 专业版实现适配,同时还反对 AMD64、ARM64、MIPS64 等指令集架构,以及龙芯、鲲鹏、海思麒麟、兆芯、海光、飞腾等 CPU 平台,可实现一键装置的原生体验。 评估一款操作系统是否好用,一个十分重要的指标就是软件生态是否丰盛,因为常用软件的反对状况,间接影响用户的应用体验,统信走出的这一步无疑是国产操作系统重大的提高。 统信UOS桌面罕用利用适配停顿(排名不分先后): 统信的倒退门路在 12 月23日的 2020 统信UOS生态大会上统信还发表曾经实现11亿元人民币A轮融资。 统信软件董事长王继平对媒体示意,公司本轮投后估值达到59亿元,并心愿将来几年内可能在科创板上市。 统信软件还公开了将来十年三步走门路: 2020-2022年:圆满完成国家各要害行业的撑持需要,市场占有率第一,用户超千万量级;成为中国操作系统领军企业;产品能力超过Windows7/8零碎。2023-2025年,倒退中国2B2C市场利用,中国市场占有率30%;成为全球排名前三的独立操作系统企业;达到国内支流程度,造成独立倒退的中国操作系统生态与社区。2026-2030年,统信UOS力争成为寰球支流操作系统产品;实现寰球范畴的根底软件生态;产品与解决方案笼罩全应用领域,与国外一流厂商同台竞技。作为最早从事操作系统研发的工作者,中国工程院院士沈昌祥评估统信UOS零碎是整个软硬件的外围零碎,承前启后,起着指挥作用。他还示意中国必须实现操作系统自主研发,并且还必须平安。第一,要弄清楚呈现平安问题的起因是什么;第二,必须调整理念,要从根本上改良计算理念,边计算、边防护;第三,形成零碎肯定要平安、可信,该当建设平安治理体系全程管控,这样能力构建可信的网络安全社会保障体系。

December 30, 2020 · 1 min · jiezi

关于操作系统:15分钟一文帮小白搞懂操作系统之内存

前言操作系统是一门比拟难啃的课程,同时操作系统常识对开发者们来说是非常重要,置信各位在学操作系统的时候,有太多的形象难以了解的词汇与概念,把咱们间接劝退,即便怀着满腔热血的情绪学操作系统,不到 3 分钟睡意就忽然袭来。 所以自己想把本人的想法通过图解+大白话的模式,产出操作系统系列文章,让小白也能看懂,帮忙大家疾速科普入门 本篇开始介绍内存,内存在操作系统中还是比拟重要的,了解了它,对整个操作系统的工作会有一个初步的轮廓。 内容纲要 内容纲要 注释什么是内存小故事咱们想去摆地摊(筹备运行程序过程)须要通过那几 个步骤,这里猜想一下。 首先要去城管申请摊位(申请内存),城管(操作系统)依据当初残余的地毯空间与你地毯的规模划分一块相应大小的摊位(内存)给你,接着你就能够欢快的摆摊(运行程序过程)赚钱啦。 城管也会时不时的来查看(整顿内存空间碎片),摊位是否规整,有没有妨碍失常的人行道。 简而言之,电脑上的程序(过程)运行是须要应用到对应大小的物理内存。 虚拟内存实际上运行的过程并不是间接应用物理内存地址,而是把过程应用的内存地址与理论的物理内存地址做隔离,即操作系统会为每个过程调配独立的一套「虚拟地址」。 每个过程玩本人的地址,互不干涉,至于虚拟地址怎么映射到物理地址,对过程来说是通明的,操作系统曾经把这些安顿的明明白白了。 操作系统会提供一种机制,将不同过程的虚拟地址和不同内存的物理地址映射起来,如下图所示 在这里插入图片形容 由此咱们引出了两个概念: 过程中应用的内存地址叫虚拟地址存在计算硬件里的空间地址叫物理地址简略来说操作系统引入虚拟空间,过程持有的虚拟地址会通过 CPU 芯片中的内存治理单元(MMU)的映射关系,来转换成物理地址,再通过物理地址拜访物理内存 操作系统是如何治理虚拟地址与物理内存地址之间关系?次要有三种形式,别离是分段、分页、段页,上面咱们来看看这三种内存治理形式 内存分段程序蕴含若干个逻辑分段,如可由代码段、数据段、栈段、堆段组成,每个分段都有不同的属性,所以内存以分段的模式把这些段分离出来进行治理 在内存分段形式下,虚拟地址和物理地址是如何映射的?分段治理下的虚拟地址由两局部组成,段号和段内偏移量 在这里插入图片形容 通过段号映射段表的项从项中获取到段基地址段基地址+段内偏移量=应用的物理内存通过上述晓得了,应用段号去映射段表的项,应用项中的段基地址与偏移量计算出物理内存地址,但实际上,分段形式会把程序的虚拟地址分为4段,每个段在段表中有一个项,在这一项找到段的基地址,再加上偏移量计算出物理内存地址 分段的形式,很好的解决了,程序自身不须要关怀具体物理内存地址的问题,然而它仍有不足之处: 内存碎片的问题内存替换的效率低的问题接下来对这两个问题进行剖析 分段形式是如何产生内存碎片的?在说内存碎片之前,还是先弄明确,什么是内存碎片?,8集体去里面吃饭,因为饭点起因,人比拟多,剩下的都是4人小餐桌,这些4人小餐桌就是咱们所说的内存碎片,此时会有小伙伴说,把2个4人小餐桌拼凑在一起就解决了这个问题,非常简单,咱们把这种形式称为内存碎片整顿(波及到内存替换)。 回到正题,咱们来看一例子,假如物理内存只有1GB (1024MB),用户电脑上运行了多个程序: 浏览器占用128MB音乐软件占用256MB游戏占用了512MB这个时候咱们敞开浏览器,残余物理内存1024MB -(256MB+512MB)= 256MB。然而这残余的256MB物理内存不是间断的,被分为了两段128MB,导致没有空间再关上一个200MB的程序,如下图所示 在这里插入图片形容 这里的内存碎片问题共有两点: 内部内存碎片,就是多个不间断的小物理内存空间,导致新的程序无奈被装载外部内存碎片,程序所有的内存都被装载进了物理内存,然而程序有局部的内存,可能不常常应用,造成内存的节约解决内部内存碎片的办法就是应用内存碎片整顿 内存碎片整顿通过内存替换的形式来实现,咱们能够把音乐软件占用的256MB加载到硬盘下面去,再从硬盘读取回来,然而读取回来的地位不再是原来的地位,而是紧跟曾经占用的游戏512MB前面,这样两个128MB的闲暇物理内存就合并成了一个256MB的间断物理内存,于是新的200MB新程序就能被装载进来 内存替换空间,在 Linux 零碎里,是咱们常看到的 Swap 空间,这块空间是从硬盘划分进去的,用于内存与硬盘的空间替换。 分段形式为什么内存替换效率低?首先分段治理容易造成内存碎片,导致内存替换的频率较高,因为硬盘的访问速度比内存慢太多了,而后每次替换的时候,把一大段间断的内存写入到硬盘,再又从硬盘读取进去,如果替换的是一个占内存空间很大的程序,这样整个机器都会显得卡顿,过程也很慢的,所以说分段形式内存替换效率低。 为了解决内存分段治理造成的内存碎片与内存替换效率低的问题,就呈现了内存分页 内存分页分段的益处是能产生间断的内存空间,然而会呈现大量内存碎片与内存替换效率低的问题 先思考一下怎么解决这两个问题,内存碎片是由多个不间断的小物理内存空间造成,如果把这些不间断的小物理内存空间组合起来,是不是解决了这个问题?同样的,内存替换的时候咱们保障替换的数据小,是不是能进步内存替换的效率? 这个方法就是内存分页,分页是把整个虚构与物理空间切成一段段固定尺寸的大小,这样一个间断并且尺寸固定的空间,咱们叫页,在 Linux 下,每一页的大小为 4KB。(虚拟空间是指存储一套虚拟地址的空间) 虚拟地址与物理地址是通过页表来映射,虚拟空间内的虚拟地址肯定是间断的,物理地址不肯定,但能够通过间断的虚拟地址把多个不间断的物理内存组合应用。 在这里插入图片形容 而当过程拜访的虚拟地址在页表中查不到时,零碎会产生一个缺页异样,进入零碎内核空间调配物理内存、更新过程页表,最初再返回用户空间,复原过程的运行。 分页形式是如何解决内存碎片与内存替换效率慢的问题呢?内存碎片的解决: 因为应用内存的单位变成固定大小的页,所以每个程序的虚拟空间保护的也是间断的页(虚拟地址),通过页表再映射到物理内存页,尽管映射的物理内存页不间断,然而虚拟空间是间断的,能够让它们组合起来应用,但这也只能解决内部内存碎片问题,没有解决外部内碎片问题,因为每页都有固定大小,可能某一页只应用了局部,仍然会造成一些节约。 内存替换效率慢的解决: 之前说过,缩小替换数据的大小,能够进步内存替换效率,分页形式是这样解决的,如果内存空间不够时,操作系统会把其余正在运行的过程中的「最近没被应用」的内存页开释掉,也就是加载到硬盘,称为换出,一旦须要的时候再加载进来,称为换入。所以一次性写入硬盘的也只有一个页或几个页,内存的替换效率天然就晋升了。 分页形式使加载程序的时候,不再须要一次性都把程序加载到物理内存中。齐全能够在进行虚拟内存和物理内存的页之间的映射之后,并不真的把页加载到物理内存里,而是只有在程序运行中,须要用到对应虚拟内存页外面的指令和数据时,再加载到物理内存外面去(用大白话说,当你须要用到的时候才会去应用对应的物理内存)。 在内存分页形式下,虚拟地址和物理地址是如何映射的?在分页机制下,每个过程都会调配一个页表,虚拟地址会分为两局部,页号和页内偏移量,页号作为页表的索引,页表蕴含物理页每页所在物理内存的基地址,页内偏移量+物理内存基地址就组成了物理内存地址,如下图所示 在这里插入图片形容 就是上面这几步 ...

December 28, 2020 · 1 min · jiezi

关于操作系统:线程是如何调度的

在操作系统中,线程是如何调度的?或者是线程调度有哪些办法?最近在补充一些操作系统的常识,线程调度是操作系统无奈回避的问题。对其由浅入深的解决思路大有感触,在此记录。 先到先解决对于操作系统而言,线程相当于一个个待执行的工作。最常见用于任务调度的是队列,队列是任务调度的最简略模型,听从先到先解决的准则,一个工作解决实现之后,才会解决下一个工作。 相对而言,队列模型是最偏心的,工作的执行程序只与进入队列的工夫相干,同时,因为不存在工作切换等,所以没有额定的逻辑代码开销。 上面是用js实现的一个任务调度的队列模型: // 模仿sleepfunction sleep(ms) { for (let t = Date.now(); Date.now() - t <= ms;);}// 调度器对象class scheduler { constructor() { // 队列 this.queue = [] } // 压入工作 pushTask(task) { this.queue.push(task) } // 获取下一个待执行的工作 next() { if (this.queue.length > 0) { // 获取最先进入的一个 return this.queue.shift() } return undefined } // 执行 excute() { // 一直的循环执行 while (true) { const nextTask = this.next() if (nextTask) { nextTask() } else { sleep(1000) } } }}队列模型对于调度器而言是偏心的,调度器平等的解决每一个工作,然而对于工作来说是不偏心的,因为后进入队列的工作须要期待后面的工作执行实现能力执行。对于一个须要执行1天的工作,让其期待10分钟是能够承受的,然而如果一个须要执行10分钟的工作,须要期待一天,就无奈承受,对于这种状况须要思考短工作优先。 ...

December 21, 2020 · 2 min · jiezi

关于操作系统:Fuchsia学习笔记0下载和编译

下载代码代码下载,官网提供的形式运行脚本 curl -s "https://fuchsia.googlesource.com/fuchsia/+/HEAD/scripts/bootstrap?format=TEXT" | base64 --decode | bash创立fuchsia目录下载代码管理工具jiri和二进制包管理工具cipd引入jiri我的项目, 主动运行jiri update下载源码会十分慢,除非你的代理十分快,更好的方法是从国内的镜像仓库中下载代码 国内镜像下载的办法下载 cipd jiri 工具 mkdir fuchsia && cd fuchsia curl --location --create-dirs --output .jiri_root/bin/cipd https://fuchsia.fsf.org.cn/bootstrap/cipd-linux-amd64 curl --location --create-dirs --output .jiri_root/bin/jiri https://fuchsia.fsf.org.cn/bootstrap/jiri-linux-amd64 chmod +x .jiri_root/bin/jiri .jiri_root/bin/jiri export PATH=${PATH}:${PWD}/.jiri_root/bin下载代码计划一mkdir -p build && echo "internal_access = false" >build/cipd.gnijiri init -keep-git-hooks=truejiri import -name=integration flower https://fuchsia.fsf.org.cn/git/fuchsia-integrationjiri update -vecho "have_firmware = false" >zircon/prebuilt/config.gni计划二manifest 文件自行从 https://fuchsia.fsf.org.cn/ma... 抉择,一个月以内的文件确保无效。 mkdir -p build && echo "internal_access = false" >build/cipd.gnijiri init -keep-git-hooks=truecurl --location --output .jiri_manifest https://fuchsia.fsf.org.cn/manifest/fuchsia-20060102.xmljiri update -vecho "have_firmware = false" >zircon/prebuilt/config.gni切换国内镜像先删除相干文件 ...

December 17, 2020 · 1 min · jiezi

关于操作系统:HarmonyOS-20-手机版使用初体验-手机开发者-Beta版

12月16日上午10点,华为在北京举办华为开发者日暨HarmonyOS2.0手机开发者Beta版公布流动。华为此次发表面向手机开发者凋谢残缺的HarmonyOS 2.0零碎能力、丰盛的API(利用开发接口),以及弱小的开发工具DevEco Studio等技术装备,开发者可拜访华为开发者联盟官网,申请获取HarmonyOS2.0手机开发者Beta版降级。▣ 博主主站地址:微笑涛声 【www.cztcms.cn】▣ 博主其余平台: CSDN 简书 开源中国 思否 华为云博客 华为消费者业务软件部总裁王成录示意:“划时代的HarmonyOS重构了人、设施、场景的关系。将来不再是产品限度用户的应用场景,而是用户所处的场景定义设施的状态。HarmonyOS岂但将为用户带来更多取得应用服务的设施入口,也将带来全新的超级服务、超级利用,为咱们的生存发明颠覆式体验。 今天上午我拜访HarmonyOS2.0官网发现,手机版SDK也曾经同步上线,所以就急不可待的筹备体验一下HarmonyOS 2.0手机模拟器。首先必定要下载华为HarmonyOS2.0 官网的开发工具DevEco Studio。下载和装置教程能够参考我以前的博客:华为鸿蒙零碎利用开发工具介绍 DevEco Studio。如果以前曾经装置过,那要进行软件降级,目前最新版本为2.0.12.201。点击软件的help就能够进行软件更新。 软件更新实现当前就能够新建HarmonyOS 2.0 手机利用和平板利用了。 那咱就开始吧——HarmonyOS 2.0 手机版应用初体验一、新建鸿蒙利用1、关上DevEco Studio软件,点击Create HamonyOS Project。 2、能够看到相比上一代多了手机和平板利用,抉择新建Phone(手机利用),点击下一步。 3、依照下图进行配置我的项目即可,点击实现。 4、首次运行我的项目会主动下载gradle(我的项目管理器),期待下载实现。 5、接下来就是漫长的编译过程,大略10分钟左右,个别不会报错。 二、揭开HarmonyOS 2.0手机版的神秘面纱1、我的项目编译实现当前,咱们先来看看鸿蒙操作系统长什么样,点击Tools——HVD Manager。这个时候浏览器会弹出窗口,在弹出的窗口登录华为账号,而后点击容许即可。能够参考文章后面提到的教程。 2、进入到模拟器治理界面,终于见到了装置有HarmonyOS 2.0 的手机和平板,虚构型号别离为P40和MatePad Pro。 3、点击模拟器治理界面的P40手机运行按钮,DevEco Studio软件右上方自动弹出手机模拟器的运行窗口。鸿蒙零碎手机版的下层界面用的的EMUI 11,所以界面看起来和当初的华为手机没有区别;操作系统底层应用的是华为自主研发的HarmonyOS 2.0,手机运行速度相较于Android大大晋升。 4、关上手机设置,点击对于手机,能够看到大大的Harmony OS几个字,零碎版本为第一个测试版本(Beta 1)。 5、查看手机状态栏。 6、查看了手机当前,也想来看看平板又是什么样子。点击模拟器治理界面的MatePad Pro运行按钮,DevEco Studio软件右上方自动弹出平板模拟器的运行窗口。 7、关上平板设置,点击对于平板电脑,也能够看到大大的Harmony OS几个字,零碎版本为第一个测试版本(Beta 1)。 三、我的项目运行,测试运行后果1、批改congfig.json文件外面的label标签,进行利用自定义命名(当然也能够不做任何批改,间接运行我的项目) 2、批改主界面显示的文字。 3、点击软件右上角的运行按钮, 能够看到我的我的项目胜利的运行在了鸿蒙零碎上,我的第一次HarmonyOS 2.0 手机利用开发胜利。 4、回到桌面,也能够看到咱们刚刚新建的我的项目在手机桌面上了。 5、在build目录下也能够找到刚刚编译生成的我的项目软件安装包(hap文件)。 四、应用领会与总结上午观看了HarmonyOS 2.0 手机开发者 (Beta版)的发布会,下午本人又对鸿蒙零碎进行实际体验,的确十分不错,我就感觉发现了新大陆一样。心愿鸿蒙零碎越做越好!遗憾的是当初的鸿蒙零碎手机版只能提供给开发者,商用降级可能要到明年了。

December 16, 2020 · 1 min · jiezi

关于操作系统:什么是操作系统

本文曾经收录至我的GitHub,欢送大家踊跃star 和 issues。 https://github.com/midou-tech/articles 外甥上大一了,起初我还十分放心他,放心次要有两方面的起因: 从小始终是校草,长的太帅,会不会天天谈恋爱去了放心在大学没能做好本人的布局于是常常和他视频聊天,外甥小我没几岁,咱们常常以兄弟相称,聊起来天然谐和 从我这几次和他聊天能够看出,我的担心有些太过了 他如同从没刻意他的帅气,而是一直的空虚本人的才华,篮球打得好、街舞跳的好、还去加入各种志愿者流动 要害是最近还对计算机产生了强烈的趣味。 唉,帅就算了,还这么认真,这年轻人不讲武德啊 上周末打电话,居然问我什么是操作系统? 为什么要有操作系统? 于是就有了这篇文章,也筹备把我外甥的计算机相关疑难全副承包了,喜爱我记得关注我 公号 龙跃十二 喜爱我几个关注我 什么是操作系统?说实话很难有一个精确的定义去形容操作系统,看了多本对于操作系统的书中是这样说的。 操作系统是一个运行在内核态的软件,该软件对底层各种硬件资源做了形象和治理,并提供对立的API接口供应用程序去调用这些资源。这句话涵盖了好几个知识点 操作系统是一个软件操作系统软件运行在内核状态下的软件,不在用户态下,利用开发者开发的各种系统软件都是在用户态下运行的撒是用户态,撒是内核态?你很喜爱剖根问底,这个前面再说 对各种底层硬件资源进行了形象和治理 形象了处理器、存储器、时钟、磁盘、网络接口、外设等底层硬件资源,把各种资源都形象为一个个的文件描述符 构想一下,多核状况下,多个应用程序同时应用一种资源,就会造成凌乱场面,此时操作系统就会表演一个管理者去治理这些资源的使用者。 提供对立对外拜访的接口 操作系统处在中间层,下面有各种零碎应用程序、开发人员、用户,上面有各种计算机硬件资源。 怎么脑海中浮现了 高低尴尬 的表情包,哈哈哈有内味了 操作系统设计者和硬件编程人员能够间接去操作硬件资源 操作系统设计者这不必解释了,都懂的,都在设计操作系统了,总不至于不能操作硬件资源吧 硬件编程人员这个大家可能略微生疏点,有一类开发人员是间接对硬件进行操作的,如果是你是学习电子信息相干业余的,应该很相熟烧程序。 大学做试验的时候龙叔就常常把写好的汇编程序或者C语言程序编译实现了间接烧到计算机硬件上,这叫烧程序。 我写的程序外面有对硬件资源进行操作,比方操作某某管脚、某个串口等等 所以没有操作系统也是能够间接操作计算机硬件资源,也是能够跑程序的 那为啥还要操作系统呢? 为什么要有操作系统?下面曾经说了,没有操作系统是能够操作计算机硬件资源的,那为什么要有呢? 1)最晚期计算机是没有操作系统的,每次要变更一下性能,都要手动去调整硬件,费时费力。 2)随着电子管技术的倒退,各种硬件资源能够做成通用的,此时就急切需要一个操作系统去管制这些资源,每次改性能,只须要批改输出的信号即可。 3)晋升计算机的功能性和灵活性 操作系统的演变串行解决20世纪40-50年代,电子管技术失去了倒退,诞生了第一台电子管计算机 因为整机的集成度太低,一台计算机必须用一个大house来搁置 而且这台机器过后数百万美元的标价,价格是真的贵,个别人用不起,都是一些豪横的组织才用得起 晚期的计算机是没有操作系统的,操作人员间接和计算机硬件交互,相似上面这样 所有的操作在管制台上进行,管制台上有显示灯、触发器、输入输出设施 每次作业都须要有专门的的人员来操作,应用汇编语言写程序到纸片上,再穿孔成卡片,再将这些卡片交给专门的操作人员去操作,再破费工夫等操作后果。 计算机老本高,操作过程简单,工夫还长 人们很天然就想到缩小机器工夫的节约,于是就诞生了批处理零碎。 批处理零碎到了20世纪50年代General Motors开发了批处理零碎,那时候被称为 监控零碎 监控零碎是常住在内存中的,他做的事件就是 加载用户程序到用户程序段把控制权交给用户程序读取用户程序指令交给处理器将处理器的执行后果输入到设施用户程序实现后交回控制权进行下一个循环 总结一下,监控程序次要次要实现调度性能,一批作业排队期待,处理器尽可能的被充分利用,不让他有任何闲暇工夫。 监控程序很大水平上进步了处理器的利用率,缩小了处理器闲暇工夫 多道零碎批处理零碎曾经进步了程序的利用率,但还是没最大化压迫 监控程序和用户程序须要来回切换转换控制权,这部分工夫处理器闲置 监控程序在做IO操作时,因为IO是比较慢的,磁盘读写很慢的,此时处理器须要期待 为了更好的利用处理器的计算性能,大佬们搞出了多道零碎 多道零碎就是同时加载多个用户程序,当其中一个用户程序须要IO期待时,切换到另一个不在期待IO的用户程序进行解决。 这就很好的缩小处理器资源的节约,同时进步了程序处理的效率。 多道零碎会比单道零碎简单很多,比方 所有程序都在内存中,波及到 内存治理 ...

November 30, 2020 · 1 min · jiezi

关于操作系统:死锁的解决与预防

死锁要理解如何解决与预防死锁,就得先理解什么是死锁。 产生起因因为零碎中存在着一些不可剥夺资源,当两个或两个以上过程占有本身资源,并且申请对方的资源时,因为资源的不可剥夺,单方都得不到须要的资源,导致每个过程都无奈后退,从而产生死锁。 产生条件1.互斥零碎中的资源都是互斥的,如果是共享的话就不会产生抢夺资源的状况。2.非抢占要求零碎中的资源都是过程实现之后自主开释资源,其余的过程不能争夺此资源,3.循环期待要求存在一条过程资源的循环期待链,链中的每一个过程占有的资源同时被另一个过程所申请。 解决办法1.毁坏互斥2.毁坏非抢占3.毁坏环路期待 预防毁坏申请条件:一次性调配所有资源,这样就不会有申请了。毁坏申请放弃:只有有一个资源得不到调配,就不给这个过程调配其余资源。毁坏不可剥夺:当某过程取得了局部资源,但得不到其余资源,就开释已有资源。毁坏环路期待条件:零碎给每类资源赋予一个编号,每一个过程按编号递增的程序申请资源,开释则相同。 防止银行家算法过程首次申请资源时测试,过程所须要的最大资源量,如果零碎的资源满足需要则分配资源,否则推延调配。

November 26, 2020 · 1 min · jiezi

关于操作系统:使用HiBurn烧录鸿蒙bin文件到Hi3861开发板

鸿蒙官网文档的“Hi3861开发板第一个示例程序”中形容了——如何应用DevEco Device Tool工具烧录二进制文件到Hi3861开发板; 本文将介绍如何应用HiBurn工具烧录鸿蒙的.bin文件到Hi3861开发板。 获取HiBurn工具通过鸿蒙官网文档咱们晓得DevEco Device Tool是一个VSCode插件,它以.vsix文件的模式向开发者提供。事实上,这个文件.vsix文件是一个zip格局的压缩文件。你能够在git bash或Linux环境中,应用file命令将它辨认进去: $ file DevEcoDeviceTool-1.0.0.vsixDevEcoDeviceTool-1.0.0.vsix: Zip archive data, at least v2.0 to extract在Windows上,你能够应用个别的压缩解压软件将它关上,并将它解压开。 解压之后,能够在extension\deveco\tools子目录中看到一个文件名为 HiBurn.exe 的文件,它就是咱们明天要介绍的HiBurn工具(你也能够从本文的附件中间接下载 HiBurn.zip 解压)。 应用HiBurn烧写.bin文件到Hi3861Win10零碎执行前须要右键“属性”->解除锁定,否则会零碎默认会报平安正告,不容许执行。 双击单开后,界面如下: 点界面左上角的Setting->Com settings进入串口参数设置界面,串口参数设置界面上,Baud为波特率,默认115200,能够抉择921600,2000000,或者 3000000(实测最快反对的值),其余参数放弃默认,点“确定”保留; 依据设施管理器,抉择正确的COM口,例如我的开发板是COM8,如果是关上程序之后才插串口线的,能够点一下“Refresh”刷新串口下拉框的可选项;![在这里插入图片形容](https://img-blog.csdnimg.cn/20201123170245919.png#pic_center)点“Select file”弹出文件抉择对话框,抉择编译生成的allinone.bin文件,这个bin其实是多个bin合并的文件,从命名上也能看得出来,例如,我抉择的Z:\harmonyos\openharmony\out\wifiiot\Hi3861_wifiiot_app_allinone.bin勾选“Auto burn”,主动下载多个bin文件,到这里,配置结束,应如下图; 点击Connect,连贯串口设施,这时HiBurn会关上串口设施,并尝试开始烧写,须要确保没有其余程序占用串口设施(烧写之前可能正在用超级终端或串口助手查看串口日志,须要确保其他软件曾经敞开了以后应用的串口);复位设施,按开发板的RESET按键;期待输入框呈现三个"=========================================="以及上方均呈现successful,即阐明烧录胜利; 烧录胜利后,须要手动点“Disconnect”断开串口连贯,否则会提醒“Wait connect success flag (hisilicon) overtime.”;和 DevEco Device Tool形式比照应用HiBurn烧录绝对于应用DevEco Device Tool烧录而言,益处次要有以下几点: 不依赖VSCode,你能够不必装置VSCode、nodejs、JDK、以及一些npm包;下载速度更快,HiBurn.exe最大波特率能够设置到3000000,而DevEco Device Tool最大只能为921600,是它的三倍;HiBurn形式烧录目前的毛病次要是: 须要手动点Disconnect被动断开连接,否则默认会反复下载;烧录胜利后,如果你一直开串口,并且再次按了一下RESET按键,你会发现,它又烧录了一遍。HiBurn的串口参数无奈保留,你敞开之后下次关上还须要从新设置,而DevEco则会能够保留串口参数;HiBurn绝对DevEco来说,操作步骤更多一些,也略微简单一点;

November 23, 2020 · 1 min · jiezi

关于操作系统:韦东山鸿蒙移植02必备基础知识

1. 基础知识移植内核对技术的要求比拟高、比拟细。 1.1 单片机相干的常识栈的作用加载地址、链接地址重定位几个简略的硬件常识 串口 定时器 定时器中断的概念1.2 Linux操作相干的常识Linux常用命令简略的脚本:脚本就是把命令写在一个文件里MakefileGCC编译命令1.3 芯片相干常识能浏览芯片手册(英文):移植最小零碎时,波及的手册内容不多能看懂硬件原理图:移植最小零碎时,波及的原理图内容不多2. 驱动程序常识对于只有单片机常识的人来说,怎么去操作硬件? 间接读写寄存器应用库函数在RTOS中,实质也是去读写寄存器,然而须要有对立的驱动程序框架。所以:RTOS驱动 = 驱动框架 + 硬件操作 2.1 以点灯为例2.1.1 硬件原理 2.1.2 单片机点灯 办法1:间接读写寄存器 办法2:应用厂家的HAL库 2.1.3 FreeRTOS点灯 2.1.4 rt-thread点灯办法1:间接操作寄存器 办法2:应用驱动程序 驱动模型如下: 驱动示例如下: 调用过程框架 调用过程示例 2.1.5 Liteos-a/Linux怎么点灯应用MMU时,个别APP与内核是互相隔离的。APP通过规范的open/read/write等文件操作函数去调用驱动程序。如下图所示: 为何要多此一举? 它们反对MMU(内存治理单元)用户程序跟内核是分隔开的,用户程序不能间接读写寄存器用户程序通过标准接口拜访驱动程序基于这些内核的软件个别都比单片机软件简单,术业有专攻不应该让写APP的人去看原理图、写驱动、写寄存器软件和硬件隔离,硬件再怎么变动,只须要改驱动,APP不须要改2.1.6 Android怎么点灯 Android是Linux操作系统上的一套操作系统Android通过Linux来拜访硬件,实质还是Linux驱动个别的C程序、C++程序,能够间接调用open/read/write等函数应用java编写的程序要拜访C函数须要通过JNI2.2 以LCD为例2.2.1 硬件原理 设置LCD控制器,它会主动从FrameBuffer中读取每个像素的数据发送到LCD上把要显示的文字、图像放入FrameBuffer中2.2.2 Liteos-a/Linux怎么操作LCD 为何要多此一举? 它们反对MMU(内存治理单元)用户程序跟内核是分隔开的,用户程序不能间接读写寄存器用户程序通过标准接口拜访驱动程序基于这些内核的软件个别都比单片机软件简单,术业有专攻不应该让写APP的人去看原理图、写驱动、写寄存器软件和硬件隔离,硬件再怎么变动,只须要改驱动,APP不须要改2.2.3 Android怎么操作LCD Android/QT等GUI零碎里: LCD会被多个APP应用,如果不对立治理必然乱套所以,必定有一个管理软件,或称为服务软件APP自行结构本人的界面,提交给显示显示服务软件显示服务软件:依据多个APP的前后档次,合并最终的显示图像再调用驱动程序显示进去2.2.4 鸿蒙怎么操作LCD鸿蒙反对Liteos、Linux内核,在内核之上怎么去为多个APP治理惟一的显示设施?鸿蒙还反对软总线,实践上能够反对更多的LCD,怎么做?务软件: 依据多个APP的前后档次合并最终的显示图像再调用驱动程序显示进去原文链接:https://developer.huawei.com/consumer/cn/forum/topic/0201396913445810055?fid=0101303901040230869作者:韦东山

November 23, 2020 · 1 min · jiezi

关于操作系统:Java实现操作系统中四种动态内存分配算法BFNFWFFF

1 概述本文是利用Java实现操作系统中的四种动态内存调配形式 ,别离是: BFNFWFFF分两局部,第一局部是介绍四种调配形式的概念以及例子,第二局部是代码实现以及解说。 2 四种调配形式2.1 概念操作系统中有一个动静分区调配的概念,内存在初始化的时候不会划分区域,而是在过程装入的时候,依据所要装入的过程动静地对内存空间进行划分,以进步内存空间的利用率,升高碎片的大小,次要的办法有一下四种: 首次适应算法(First Fit):从闲暇分区链首开始查找,直到找到一个满足其大小要求的闲暇分区为止循环首次适应算法(Next Fit):从上次找到的闲暇分区的下一个开始查找最佳适应算法(Best Fit):把闲暇分区按大小递增的形式造成分区链,找到第一个能满足要求的闲暇分区就进行调配最坏适应算法(Worst Fit):与最佳适应算法相同,把闲暇分区按大小递加的形式造成分区链,找到第一个能满足要求的闲暇分区就进行调配2.2 例子假如当初有100MB的内存空间,某一时刻先后调配了20MB、4MB、10MB内存,示意图如下: 当初须要再调配5MB内存。 若采纳FF,因为FF是间接按程序分配内存,从低地址开始搜寻闲暇分区,因而便会从第一块闲暇分区调配5MB(地址0-5),示意图: 若采纳NF,NF与FF相似,只不过NF是从上一次找到的闲暇分区的下一块开始查找,因为上一次调配的是10MB,因而会从最初一块闲暇分区(地址80-100)分配内存: 若采纳BF,BF是遍历所有闲暇分区并找到一个能满足要求的最小分区,也就会找到一个比5MB大的闲暇分区,且该闲暇分区是所有闲暇分区中最小的,也就是地址为64-70的闲暇分区: 若采纳WF,WF与BF相同,总是从最大的闲暇分区开始调配,因而会从地址为30-60的闲暇分区进行调配: 3 代码实现3.1 总览代码分成了四个类: Main:测试Print:输入打印Table:示意每一个分区TableList:对分区进行管制,包含初始化,调配,回收等3.2 MainMain是测试类,代码如下: public class Main { private final static TableList list = new TableList(64); public static void main(String[] args) { list.useWF();// list.useBF();// list.useNF();// list.useFF(); list.allocate(10); list.allocate(20); list.free(10); list.show(); list.allocate(8); list.show(); list.allocate(13); list.allocate(1); list.show(); list.free(1); list.allocate(9); list.free(13); list.show(); list.allocate(18); list.show(); list.allocate(3); list.allocate(4); list.free(20); list.free(8); list.show(); list.allocate(8); list.free(9); list.show(); list.clear(); list.show(); }}通过TableList对内存进行调配以及开释,初始化调配64MB大小内存,切换调配算法时应用前四行的其中一行即可。 ...

November 21, 2020 · 3 min · jiezi

关于操作系统:当我们说起多线程与高并发时

高并发与多线程可为java畛域内的显学矣,某种程度上也能够算是软件畛域的显学,大家都谋求的货色。然而这其中也有些误会,比方很多人认为没有多线程,你的电脑就在听歌的时候不能打游戏,这是一种误会。我写的货色大多都成一个零碎,本篇是多线程篇的总纲,并不依附于特定的语言,我记得线程也是放在操作系统中探讨的。并发VS并行我在刚学Java的时候,一边看视频,一遍看书,入门选的是官网写的教程《The Java™ Tutorials》,在讲到多线程之前的话是这样的: Computer users take it for granted that their systems can do more than one thing at a time. They assume that they can continue to work in a word processor, while other applications download files, manage the print queue, and stream audio. Even a single application is often expected to do more than one thing at a time. For example, that streaming audio application must simultaneously read the digital audio off the network, decompress it, manage playback, and update its display. Even the word processor should always be ready to respond to keyboard and mouse events, no matter how busy it is reformatting text or updating the display. Software that can do such things is known as concurrent software. ...

November 15, 2020 · 1 min · jiezi

关于操作系统:用鸿蒙OS在蜂鸣器上播放一曲两只老虎

本文介绍如何在HiSpark Wi-Fi IoT套件上,应用Harmony OS IoT硬件子系统的PWM接口 驱动蜂鸣器 播放音乐。 用PWM输入方波的API 鸿蒙零碎IoT硬件子系统提供了PWM相干接口,接口头文件为wifiiot_pwm.h,其中开始输入方波的接口为: /** * @brief Outputs PWM signals based on the input parameters. * * This function outputs PWM signals from a specified port based on * the configured frequency division multiple and duty cycle. * * @param port Indicates the PWM port number. * @param duty Indicates the PWM duty cycle. * @param freq Indicates the frequency-division multiple. * @return Returns {@link WIFI_IOT_SUCCESS} if the operation is successful; * returns an error code defined in {@link wifiiot_errno.h} otherwise. * @since 1.0 * @version 1.0 */unsigned int PwmStart(WifiIotPwmPort port, unsigned short duty, unsigned short freq);PWM输入的方波频率 ...

November 11, 2020 · 2 min · jiezi

关于操作系统:SYSBASE操作系统笔记理论篇

操作系统:向应用程序提供资源集的根本形象,在相互竞争的程序之间有序地管制对处理器、存储器以及其余I/O接口设施的调配(资源管理:工夫复用、空间复用)。 计算机硬件:CPU:每个CPU根本周期中,从内存取指令-解码(以确定其类型和操作)-执行,如此重复。因为拜访内存慢,CPU内设有寄存器: 通用寄存器:用来保留要害变量和长期数据专用寄存器: 程序计数器:下个指令的内存地址堆栈指针:以后栈顶端程序状态字(PSW):条件码位(由比拟指令设置)、 CPU优先级 、 模式(用户态或内核态)等管制位古代CP设计: 流水线:有独自的取指单元 、解码单元和执行单元,能同时取出多条指令。超标量:更先进,有多个执行单元(只有有一个执行单元闲暇, 就查看缓冲区中是否还有可解决的指令, 如果有, 就把指令从缓冲区中移出并执行之。)模式: 用户态:用户程序编码可能解决的内核态:无奈编码表示的,用户程序必须应用零碎调用(内中断)陷入内核并调用操作系统。 内中断(异样) 出错(fault):保留指向触发异样的那条指令 (例:缺页异样)陷入(trap):保留指向触发异样的那条指令的下一条指令(例:调试)外中断存储器CPU寄存器 1ns <1k:32位CPU是32\32 64位CPU是64\64高速缓存 2ns 4M主存 10ns 1-8G RAM ROM Flash CMOS磁盘 10ms 1-4T 低端硬盘速率50MB/s, 高速硬盘160 MB/sI/0设施I/0设施=设施控制器+设施自身设施控制器 为操作系统提供一个简略的接口。在控制器中常常装置一个小的嵌入式计算机,运行为执行这些工作而专门编好的程序。大多数设施驱动程序(专门与控制器对话,收回命令并接管响应的软件)须要在内核态运行设施自身:设施自身有个绝对简略的接口, 这是因为接口既不能做很多工作,又曾经被标准化了。实现IO的形式:忙期待、中断、间接存储器拜访总线最次要的PCIe总线 简介:10+Gb/s是串行总线架构,取代传统PCI的并行总线架构,通过一条被称为数据通路的链路传递汇合了所有位的一条音讯,这十分像网络包。PCIe 2.0规格的16个数据通路提供64Gb/s的速度,降级到PCIe 3.0后会提速2倍,而PCIe 4.0会再提速2倍。CPU 通过DDR3总线与内存对话通过PCie总线与外围图形设施对话通过DMI总线经集成核心与所有其余设施对话集成核心 通过通用串行总线与USB设施对话通过SATA总线与硬盘和DVD驱动器对话通过PCle传输以太网络帧USB用来将所有慢速I/0设施(如键盘和鼠标)与计算机连贯的 USB 1.0 12Mb/sUSB 2.0 480Mb/sUSB 3.0 5Gb/sSCSI 一种高速总线用在 高速硬盘、 扫描仪、服务器和工作站 640MB/s即插即用:零碎主动地收集无关1/0设施的信息,集中赋予中断级别和I/0地址,而后告诉每块卡所应用的数值。启动计算机BIOS 查看所装置的RAM数量,键盘和其余根本设施 扫描PCie和PCI总线并找出连在下面的所有设施尝试存储在CMOS存储器中的设施清单决定启动设施操作系统询问BIOS, 以取得配置信息查看就绪设施驱动程序, 操作系统将它们调入内核创立须要的任何背景过程,启动登录程序或GUI操作系统基本特征并发:同一时间段内产生 并行:同一时刻产生(多道程序解决宏观上并发,宏观上交替执行)共享 互斥共享(如音频设备 打印机)同时拜访(磁盘文件)虚构 cpu:每个用户(过程)的“虚处理机”存储器:每个过程都占有的地址空间(指令+数据+堆栈)显示设施:多窗口或虚构终端打印设施:将临界资源变为同时拜访资源异步性 - 判据:无论快慢,后果雷同过程线程过程模型 一个过程就是一个正在执行程序的实例, 包含程序计数器 、寄存器和变量的以后值。多道程序设计:(伪)并行状况下运行的过程集``` CPU 利用率= 1-p^n 一 个过程期待I/0操作的工夫与其停留在内存中工夫的比为p n称为多道程序设计的道数```创立 ...

November 10, 2020 · 2 min · jiezi

关于操作系统:操作系统导论2

过程的调度思考因素 为了构建调度策略,须要做一些简化假如,这些假如和零碎中运行的过程相干,统称为工作负载(workload)(1)每一个过程(工作)运行雷同的工夫 (2)所有工作同时达到,有时候当多个工作达到的工夫相差很小的时候,也近似认为是同时达到的 (3)一旦开始工作,每个工作将放弃运行直到实现 (4)所有工作只是应用CPU,即:它们不执行I/O操作 (5)每个工作的运行工夫是已知的 为了可能掂量不同调度策略的优缺点,提出一个指标——周转工夫(turnaround time)(1)定义:工作实现工夫减去工作达到的工夫,即: (2)当满足假如同时达到时,达到工夫为0,周转工夫等于实现工夫 (3)周转工夫是一个**性能**(performance)**指标**。而性能和偏心在调度零碎往往时矛盾的。调度零碎能够优化性能,但代价时阻止一些工作运行,这就升高了偏心先进先出(FIFO) 先进先出(First In First Out):先就绪的工作先执行假如有A、B、C三个工作,A比B早一点点,B比C早一点点,此时依据咱们的假如,能够将A、B、C近似看作时同时达到的。然而依据理论状况,是A先执行,其次是B,最初是C。假如每个工作运行10s,求工作的均匀周转工夫(average turnaround time)? A的周转工夫为10s,B的周转工夫为20s,C的周转工夫为30s 均匀周转工夫为(10+20+30)/3=20当初放宽假如1,让A、B、C运行工夫不同,思考FIFO是否存在均匀周转工夫较长的状况假如A、B、C三个工作,A运行工夫为100s,B和C运行工夫为10s,如果仍旧是A先早到一点,而后是B,最初是C(依然近似认为是同时达到的),此时零碎的均匀周转工夫较长(100+110+120)/3=110 FIFO呈现4这种状况被称为护航效应(convoy effect),即:一些耗时较少的潜在资源耗费者排在重量级的资源消费者前面。例如:在杂货店只有一个排队队伍的时候,你看见后面的装满了3辆购物车的货物时,这会让你等很长时间最短工作优先(SJF) 最短工作优先(Shortest Job First):先运行最短的工夫,而后是次短的工夫,如此持续仍旧在上述4的状况下,依照SJF的策略,均匀周转工夫为(10+20+120)/3=50,和FIFO相比,显著升高了均匀周转工夫。但前提是满足假如2——同时达到当初放宽假如2,即:工作可能随时达到,思考SJF均匀周转工夫较长的状况仍旧是FIFO中4的状况,假如A在t=0时达到,并且须要运行100s,而B和C在t=10s达到,各自运行10s。则A的周转工夫为100s,B的周转工夫为110-10=100,C的周转工夫为120-10=110。均匀周转工夫为(100+100+110)/3=103.33s很显著当工作可能随时达到的状况下,SJF可能会呈现平均周转工夫较长的状况最短实现工夫优先(STCF) 最短实现工夫优先(Shortest Time-to-Completion First):放宽假如3,即:调度程序能够安顿其它工作抢占正在运行的工作占用的CPU。在SJF中增加了抢占,每当新工作进入就绪状态时,它就会确定残余工作和新工作中,谁的实现工夫起码,而后调度这个工作在上述4的状况下,STCF将抢占A并先运行完B和C后,才会持续运行。则A的周转工夫为120s,B的周转工夫为10s,C的周转工夫为20s,均匀周转工夫为(120+10+20)/3=50,显著升高了SJF雷同状况下的均匀周转工夫 减少思考因素 当合乎假如4、5成立时,即:晓得工作长度,并且工作只应用CPU,依据以后的惟一掂量指标为周转工夫时,STCF是一个很好的策略。然而,引入分时系统时,就呈现了问题,因为此时须要工作和用户进行交互,而周转工夫无奈掂量工作的交互性响应工夫(response time):可能掂量工作的交互性,定义为从工作达到零碎到首次运行的工夫轮转(RR) 轮转(Round-Robin):在一个工夫片内运行一个工作,而后切换到运行队列的下一个工作,而不是运行一个工作直到完结。它重复执行,直到所有工作实现。工夫片长度必须时时钟周期的倍数。如果不是,进行上下文切换的时候须要期待一段时间,此时CPU没工作,就节约了CPU资源假如3个工作,A、B、C在零碎中同时达到,并且它们都心愿运行5s,SJF调度程序必须运行完当前任务能力运行下一个工作,而1s工夫片的RR可能疾速循环工作。RR均匀响应工夫为:(0+1+2)/3=1(注:同时达到,达到工夫为0),SJF算法均匀响应工夫为(0+5+10)/3=5 工夫片长度对RR至关重要,越短,RR在响应工夫上的体现越好,然而工夫片不能设置得太短:忽然上下文切换会影响整体性能。因为上下文切换的老本不仅仅来自保留和复原大量寄存器的操作系统操作。程序在运行时,还会在CPU高速缓存、TLB、分支预测器和其余片上的硬件中建设了大量的状态。所以工夫片长度须要谨慎衡量,让它足够长,以便摊销上下文切换老本,而又不会让零碎不及时响应摊销(amortize):通过缩小老本的频度(即:执行较少的操作),零碎的总成本就会升高。例如:如果工夫片设置为10ms,并且上下文切换工夫为1ms,大概会节约10%的工夫用于上下文切换。为了摊销这个老本,能够把工夫片长度减少到100ms,则只有不到1%的工夫会用于上下文切换。在3中,咱们只思考了响应工夫,没思考周转工夫,如果计算RR的周转工夫,A为13,B为14,C为15,均匀14。而SJF的周转工夫为,A为5,B为10,C为15,均匀10.此时RR尽管响应工夫较好,然而周转工夫较差。到目前为止。有两类调度程序(1)SJF、STCF优化了周转工夫,然而响应工夫——交互性不好 (2)RR优化了响应工夫,然而周转工夫不好 联合I/O 放宽假如4,即:工作会执行I/O,此时调度程序会面临两个问题(1)发动I/O申请做出决定,因为以后运行的工作在I/O期间不会应用CPU,它会被阻塞期待I/O实现。这时调度程序须要思考是否期待该工作的执行还是安顿另一项工作 (2)I/O实现时做出决定。I/O实现时会产生中断,操作系统运行并将收回I/O的过程从阻塞状态移回到就绪状态。此时调度程序将思考是继续执行该工作,还是执行其余工作 假如有两项工作A、B,每项工作须要50ms的CPU工夫。A每运行10ms,就会收回一次I/O申请,而B只是单纯地应用CPU50ms.调度程序先运行A再运行B。假如构建STCF调度程序。能够将A的每个10ms的子工作看作是一项独立的工作。所以工作运行时,先执行A10ms的子工作,实现后,会执行B,当I/O申请实现后,就会抢占B并运行10ms,这样就会充分利用零碎。 当交互式作业(即:I/O申请较多)正在执行I/O时,其余CPU密集型工作(即:I/O操作很少)将运行,从而更好的利用处理器多级反馈队列(MLFQ) 多级反馈队列(Multi-level Feedback Queue,MLFQ)须要解决的问题(1)优化周转工夫 (2)放宽假如5,即:不晓得工作运行工夫 (3)升高响应工夫,获取更好的交互体验 根本形成:有许多独立的队列,每个队列有不同的优先级(priority level)根本规定:(1)规定1:如果A的优先级>B的优先级,运行A(不运行B) (2)规定2:如果A的优先级=B的优先级,轮转运行A和B 如何扭转优先级(1)?(1)零碎须要执行的工作能够分为下列两类 a. 运行工夫很短、频繁放弃CPU的交互性工作 b. 须要很多CPU工夫、响应工夫不是很重要的长时间计算密集型工作 (2)优先级调整算法 a. 规定3:工作进入零碎时,放在最高优先级(最上层队列) b. 规定4a:工作用残缺个工夫片后,升高优先级(移入下一个队列) c. 规定4b:如果工作再其工夫片内被动开释CPU,则优先级不变 (3)实例1:单个长工作 下图展现了一个有三个队列的调度程序。该工作首先进入最高优先级(Q2),执行10ms的工夫片后,优先级-1,最终进入Q1,并始终到执行结束 ...

October 25, 2020 · 1 min · jiezi

关于操作系统:两台电脑用一套键盘鼠标操作的方法

办法如下:1、首先要晓得两台电脑的ip地址。ip地址能够通bai过电脑开始菜单中的运行框,输出cmd 在弹出的对话框中输出 ipconfig来取得。别离记下两台电脑的ip地址。2、两台电脑都要下载并装置Synergy软件。3、首先在A电脑运行Synergy软件,抉择Share this computer's keyboard and mourse来将A电脑设为主机,而后点击Advance进行配置。4、在advance中,要填写电脑IP地址作为screen name。将之前取得的A电脑的ip地址填写到Screen name中。后两项不必改,点击ok。5、在主界面中,点击configure,进行配置。Screen name项点击+键填写A 和B 两台电脑的ip地址,并且在links中填写链接规定。规定是A is left of B 和 B is right of A.6、点击hotkeys进行快捷键的设置,点击+键进行增加快捷键(按住键盘的快捷键组合),对应的,在左边的对话框中增加快捷键对应的动作,是切换到A电脑还是切换到B电脑。到此,A电脑就设置实现了,点击start7、B 电脑运行Synergy软件,抉择use another computer's shared keyboard and mourse,填写A电脑ip地址。此处留神是A电脑的ip地址。而后点击start。8、这样。就实现了一套鼠标键盘管制两台电脑。鼠标键盘就能够插回A电脑了,因为之前曾经把A电脑设置为主机啦。按住快捷键组合就能够进行切换。在这里,我按下ctrl+alt+shift+w就会切换到A主机,按下ctrl+alt+shift+x就会切换到B电脑。

October 22, 2020 · 1 min · jiezi

关于操作系统:CPU-执行程序的秘密藏在了这-15-张图里

前言代码写了那么多,你晓得 a = 1 + 2 这条代码是怎么被 CPU 执行的吗? 软件用了那么多,你晓得软件的 32 位和 64 位之间的区别吗?再来 32 位的操作系统能够运行在 64 位的电脑上吗?64 位的操作系统能够运行在 32 位的电脑上吗?如果不行,起因是什么? CPU 看了那么多,咱们都晓得 CPU 通常分为 32 位和 64 位,你晓得 64 位相比 32 位 CPU 的劣势在哪吗?64 位 CPU 的计算性能肯定比 32 位 CPU 高很多吗? 不晓得也不必慌乱,接下来就循序渐进的、一层一层的攻破这些问题。 注释图灵机的工作形式要想晓得程序执行的原理,咱们能够先从「图灵机」说起,图灵的根本思维是用机器来模仿人们用纸笔进行数学运算的过程,而且还定义了计算机由哪些局部组成,程序又是如何执行的。 图灵机长什么样子呢?你从下图能够看到图灵机的理论样子: 图灵机的根本组成如下: 有一条「纸带」,纸带由一个个间断的格子组成,每个格子能够写入字符,纸带就好比内存,而纸带上的格子的字符就好比内存中的数据或程序;有一个「读写头」,读写头能够读取纸带上任意格子的字符,也能够把字符写入到纸带的格子;读写头上有一些部件,比方存储单元、管制单元以及运算单元:1、存储单元用于存放数据;2、管制单元用于辨认字符是数据还是指令,以及控制程序的流程等;3、运算单元用于执行运算指令;晓得了图灵机的组成后,咱们以简略数学运算的 1 + 2 作为例子,来看看它是怎么执行这行代码的。 首先,用读写头把 「1、2、+」这 3 个字符别离写入到纸带上的 3 个格子,而后读写头先停在 1 字符对应的格子上; 接着,读写头读入 1 到存储设备中,这个存储设备称为图灵机的状态; 而后读写头向右挪动一个格,用同样的形式把 2 读入到图灵机的状态,于是当初图灵机的状态中存储着两个间断的数字, 1 和 2; ...

October 11, 2020 · 4 min · jiezi

关于操作系统:专业级沙箱与恶意样本的自动化分析

云妹导读:沙箱的英文为sandbox,也被译作沙盒,通常用来为一些起源不可信、具破坏力或无奈断定其用意的程序提供一个隔离的运行环境,甚至很多专业级的沙箱,实质就是一个加强的虚拟机。沙箱通常能够严格控制沙箱中运行的程序所能拜访的各种资源,包含:限度、禁止、监控对网络的拜访、对实在零碎的拜访、对输出设施的读取等。总之,能够把沙箱了解为是虚拟化和监控伎俩的集合体。沙箱是咱们日常进行歹意攻打捕捉、应急响应处理等工作时的常用工具。本文将通过几个实例介绍沙箱的基本概念,以Cuckoo为例介绍沙箱环境的搭建流程,最初以gonnacry勒索软件为例简略介绍Cuckoo linux沙箱检测能力加强及signature开发过程。 为了更好的了解沙箱的相干概念,上面分析一下咱们日常可能会用到的几个典型沙箱。 微软在Windows 10 18305版本之后的专业版和企业版中退出了沙箱性能,能够通过Windows性能界面开启沙箱。 Windows沙箱基于Windows下的容器技术,构建在Windows操作系统内核之上,是一种更轻量级的虚拟化计划,专门用于平安地隔离运行应用程序。其根本架构如下图所示: Windows沙箱尽管与操作系统共享内核,但沙箱拜访内核是受限制的,内核并未提供利用程序运行所需的全副API和服务。Windows沙箱具备开箱即用的个性,咱们能够间接开启一个Windows沙箱,像在本地一样运行应用程序,然而不必放心损坏操作系统。比方,咱们在日常应用电脑的过程中不免遇到须要下载执行一些可信度不确定的应用程序,在电脑上间接运行放心中毒,虚拟机运行又太耗费资源,这时Windows沙箱便是一个很好的抉择。 现在,PDF 曾经从动态页面倒退到具备如交互式表单、多媒体内容、脚本以及其它性能的复合式文档。这些性能使得 PDF 容易受到歹意脚本或操作的攻打,这些攻打能够窃取数据甚至损坏您的计算机。应用加强的安全性,能够禁止或有选择性地容许来自信赖地位和文件的操作,从而爱护您的计算机免受这些威逼。Adobe Reader在X之后的版本中退出了沙箱性能,来缓解破绽利用。咱们能够从上面两个图片比照中理解退出沙箱前后破绽利用的难度变动状况。 ▲Adobe Reader 9未退出沙箱机制的破绽利用流程▲ ▲Adobe Reader X退出沙箱之后的破绽利用流程▲ 退出沙箱之后Adobe Reader不信赖的代码能够运行,然而只给予较低的权限,申请高权限的操作时须要通过另外一个中介过程来实现,同时很多敏感的Windows API调用会被拦挡,从而保障Adobe Reader不被轻易的破绽利用。以文件操作为例,典型的文件操作流程为:(1)调用CreateFile(2)权限容许的状况下获取文件句柄(3)执行ReadFile/WriteFile进行读写操作。退出沙箱机制之后,程序是运行在沙箱过程中的:间接调用CreateFile被禁止。文件操作须要中介过程(Broker Process)进行直达,中介过程会进行一系列的校验,避免歹意操作。当全副校验通过后才会调用CreateFile并获取文件句柄。 通过这两个案例咱们对沙箱是什么有了根本的理解。除此之外,还有一些其余类型的沙箱,基本原理相似,例如:杀毒软件沙箱(360沙箱等)、浏览器沙箱(Chrome沙箱等),这里就不一一介绍了。上面咱们聊一聊专业级沙箱及歹意样本的自动化剖析,本文以Cuckoo的linux沙箱为例进行介绍(注:Cuckoo的Windows沙箱不在本文探讨范畴,感兴趣的敌人能够去网上搜寻相干文章)。 Cuckoo沙箱是一个自动化的歹意样本剖析零碎。通过web界面或者沙箱零碎提供的web api提交可疑文件,沙箱零碎即可主动剖析,并在剖析结束后提供一个具体的报告,概述该文件在沙箱中执行时的行为。 Cuckoo由Cuckoo host、Analysis Guests、Virtual network形成。cuckoo host是调度核心,analysis guest是具体执行样本的沙箱环境,两者通过虚构网卡连贯。当提交样本到cuckoo host后,cuckoo host会调度一个闲暇的analysis guest节点,同时将样本传递给所抉择的沙箱节点进行自动化剖析,剖析完结之后将沙箱节点采集到的剖析数据进行汇总,最初输入剖析报告。 Cuckoo沙箱底层基于虚拟化技术,能够应用不同的虚拟化平台进行构建,目前反对的虚拟化平台包含:VirtualBox、KVM、VMware Workstation、XenServer。 以下装置流程基于VirtualBox虚拟化环境,分为Host和Guest两局部。所用试验环境配置为:零碎发行版 :Ubuntu 18.04硬件配置 :16核 32G 600G Host装置– 依赖库装置sudo apt-get install -y python python-pip python-dev libffi-dev libssl-devsudo apt-get install -y python-virtualenv python-setuptoolssudo apt-get install -y libjpeg-dev zlib1g-dev swig– 数据库装置sudo apt-get install mongodb– 装置pydeepapt-get install -y build-essential git libpcre3 libpcre3-dev libpcre++-dev python-dev libfuzzy-devgit clone https://github.com/kbandla/pydeep.gitcd pydeeppython setup.py buildpython setup.py install– 装置Virtualboxecho deb http://download.virtualbox.org/virtualbox/debian bionic contrib | sudo tee -a /etc/apt/sources.list.d/virtualbox.listwget -q https://www.virtualbox.org/download/oracle_vbox_2016.asc -O- | sudo apt-key add -sudo apt-get updatesudo apt-get install -y libpng16-16 libvpx5 libsdl-ttf2.0-0sudo apt-get install virtualbox– 装置tcpdumpapt-get install -y libcap2-bin tcpdump– 装置 Volatilitywget https://downloads.volatilityfoundation.org//releases/2.6/volatility_2.6_lin64_standalone.zipunzip volatility_2.6_lin64_standalone.zip– 装置M2Cryptosudo apt-get install -y swigsudo pip install m2crypto– 装置guacdsudo apt install -y libguac-client-rdp0 libguac-client-vnc0 libguac-client-ssh0 guacd– 装置cuckoo$ virtualenv venv$ . venv/bin/activate(venv)$ pip2 install -U pip setuptools(venv)$ pip2 install -U cuckoo装置胜利后如下图所示: ...

October 9, 2020 · 2 min · jiezi

关于操作系统:如何在Windows和macOS两大巨头压力下脱颖而出-IT国产化国庆特别专题

过后的背景是,中国IT产业界,以及倪光南自己始终为没有自主知识产权的操作系统和芯片耿耿于怀。(18年了,还是如此)。过后的科技部部长徐冠华曾说,“中国信息产业缺芯少魂”。其中的芯是指芯片,魂则是指操作系统。————《一段对于国产芯片和操作系统的往事》作者:梁宁(2000年到2002年负责中国工程院院士倪光南的助手,深度参加了红旗Linux操作系统、永中Office、方舟CPU和NC瘦客户机的研发工作。) 引言:从前年开始有局部美国利益集团提出并开始施行「中美科技战」,试图从科技层面打压中国。对中国来说,在科技领域,特地是IT畛域有两个不能漠视的弱点,一是芯片,二就是操作系统,接着上期内容,这期咱们来看看国产操作系统的倒退历程与现状。 提到操作系统目前最为次要的为PC桌面操作系统和挪动端操作系统。其中PC端操作系统更为「重要」,因为小到集体办公,大到国防都离不开它。目前 PC 桌面操作系统是 Windows 和 macOS 的天下 ,当然也有不到 3% 的市场份额留给了开源的 Linux。难道就没有其余操作系统生存下来了?答案是否定的。就我国来说,目前比拟出名的国产操作系统也有15个之多,但正如上文所讲,它们只有微不足道的生存空间。 中国操作系统本土化始于20世纪末,1999年4月8日,中国第一款基于Linux/Fedora的国产操作系统Xteam Linux 1.0 公布,开启了操作系统国产化之路。 之后也陆续有几家公司推出了多款操作系统,但和其余各个国家的许许多多操作系统一样,终是不敌Windows和 macOS,只能游离在市场的边缘。 2014年4月8日起,美国微软公司进行了对Windows XP SP3操作系统提供服务反对,2020年美国微软公司对 Windows7 终止服务反对,加之美国开始对中国芯片业动员「科技战」,国产操作系统再一次成为了首要议题。 咱们能做出优良的操作系统吗假使美国政府禁止咱们应用 Windows 和 macOS 操作系统,咱们是否有一款国内操作系统抗住这个重任? 目前国产操作系统基本上都是基于 Linux 内核的,但这并不是很丢人的事,因为赫赫有名的安卓也是基于 Linus内核的。用中国工程院院士倪光南话来说:「基于Linux的国产 PC 操作系统并不是技术倒退,因为自研操作系统正在向更先进的程度迭代。从目前来讲,自研操作系统在交互体验、界面设计、安全性、稳定性等方面,曾经不输 Windows 零碎」,那是什么在制约国产操作系统的倒退呢? 答案是生态,因为美国的先入为主,微软和苹果公司曾经在市场中建设了一套齐备的生态系统,新的生态系统必须通过市场的良性循环能力建设起来,这是十分艰难的事件。就连PC操作系统的「老大哥」微软在进入挪动终端操作系统都没有逃过这个难关,因为生态建设过慢,后果是只能是折戟沉沙。2010年10月,微软公布了Windows Phone 手机操作系统的第一个版本,但始终到5年后,Windows Phone在欧洲、美国以及中国的市场占有率都仍然只有可怜的个位数,当初微软对Windows Phone曾经处于根本彻底放弃的状态。 所以国产操作系统要倒退起来首要的是建设生态,让有数的开发者在这个生态上开发软件,买通国产操作在业余畛域软件寥寥无几的窘境,并试图扩大更多的利用空间,但话又说回来,没有用户根底的操作系统,谁又违心去开发软件呢? 目前国产操作系统有两大「流派」第一流派是为桌面、服务器产品开发操作系统的传统操作系统厂商。例如:河汉麒麟、中标麒麟。这些厂商领有肯定的政府、企业订单等。但技术上也更多的依附传统门路,聚焦现有业务,打磨产品成熟度,优化Linux服务器、电脑等桌面利用,创新性绝对欠缺。 另一大流派则是新兴势力。他们通过本人独特技术以及翻新思路开辟国产操作系统市场。如鸿蒙、阿里YunOS、腾讯、技德零碎等。 咱们以最近备受关注的鸿蒙来看看国产操作系统的将来,去年8月9日的华为开发者大会上,华为消费者业务CEO余承东示意,安卓/Linux内核代码宏大冗余,难以保障不同终端体验晦涩,再加上多终端互联对设施平安提出更高的要求,须要一个弱小的操作系统将硬件和软件进行整合。 鸿蒙正是基于这样的理念应运而生,认真解读余承东的话不难发现鸿蒙抱负不仅在挪动端、PC端,甚至要囊括简直所有智能终端。 用一句比拟热血的话来说:「这个万物互联时代带来的新机遇,终于被华为等到了。」5G带来的高速数据传输能力让各个产品之间更好的互联互通,假如一个场景,你用着智慧屏当屏幕,用智能音响播放着音乐,用PC进行办公,而这些设施相互连接,并都有一个操作系统——鸿蒙。简单的配置过程与不兼容的BUG等问题在这一刻都将成为历史。 尽管当初的鸿蒙2.0还在倒退中,但领有硬件和软件双重制作能力的通信巨头华为是最有心愿将这件事做成的,总结一句话就是与其在背地默默追赶,或者弯道超车才是更好的前途。 目前比拟杰出的国产PC桌面、服务端操作系统一、优麒麟(UbuntuKylin) 该零碎由工信部软件与集成电路促成核心、国防科技大学联手打造,针对中国用户定制,预装并通过软件核心提供了大量适宜中国用户应用的软件服务。最新的“优麒麟”操作系统曾经实现了反对ARM和X86架构的CPU芯片。 二、红旗Linux 该操作系统最早在1999年8月亮相,次要用于一些部门。不过当初这款零碎曾经很久没有更新过了,而且研发公司曾经在2014年2月10日遣散。不过好的音讯是中科红旗仍将持续开发红旗Linux国产操作软件。 目前国家工化部、国家电网、中国银行、CCTV等单位仍有在应用红旗Linux,最新的桌面操作系统曾经更新到10.0版本。 三、中标麒麟(NeoKylin) 该零碎是由民用的“中标Linux”操作系统和军用“河汉麒麟”操作系统合并而来,最终以“中标麒麟”的新品牌对立呈现在市场。这款零碎还成为了2018-2019中国 Linux市场占有率第一的零碎。此外该零碎还针对X86及龙芯、申威、众志、飞腾等国产CPU平台进行自主开发,率先实现了对X86及国产CPU平台的反对。 四、深度Linux 该操作系统在2004年亮相,更新速度较快,举荐应用。该零碎次要用于民用版本,其源代码凋谢可控,目前累计下载量达数千万次,已经在Distrowatch上排名最高的中国Linux操作系统。目前他们正在解决迁徙Windows平台软件带来的各种兼容性问题。 五、中兴新支点桌面操作系统 该零碎出自于中兴,是国产操作系统中最大的黑马,也是一款较为成熟的零碎。同样基于开源Linux外围进行研发的桌面操作系统,此外同样反对产芯片(兆芯、申威、龙芯、ARM)及软硬件。目前曾经汇集了一些罕用的应用软件,成熟度较高。 ...

October 3, 2020 · 1 min · jiezi

关于操作系统:手把手快速安装Deveco-studio

下载Deveco Studio 步骤 1 点击链接下载DevEco Studio安装包。下载DevEco Studio须要应用华为帐号登录HarmonyOS利用开发者门户。同时,应用DevEco Studio近程模拟器须要您的华为帐号进行实名认证,建议您在注册华为账号后,立刻提交实名认证审核,审核周期为1~3个工作日,详情请参考注册华为帐号。步骤 2 双击下载的“deveco-studio-xxxx.exe”,进入DevEco Studio装置向导,在如下装置选项界面勾选DevEco Studio launcher后,点击Next,直至装置实现。 下载和装置Node.jsNode.js软件仅在您应用到JS语言开发HarmonyOS利用时才须要装置。应用其它语言开发,不必装置Node.js,请跳过此章节。 步骤 1 登录Node.js官方网站,下载Node.js软件包。请抉择LTS版本,Windows 64位对应的软件包,始终装置。 1.1配置开发环境DevEco Studio开发环境须要依赖于您的网络环境,须要连贯上网络能力确保工具的失常应用,您能够依据如下两种状况来配置开发环境:l 如果您能够间接拜访Internet,您只需进行下载HarmonyOS SDK操作。l 如果您的网络不能间接拜访Internet,须要通过代理服务器才能够拜访,您须要依照以下的内容领导(包含设置npm代理、设置Gradle代理、设置DevEco Studio代理和下载HarmonyOS SDK),逐条设置开发环境。 设置npm代理只有在同时满足以下两个条件时,须要配置npm代理,否则,请跳过本章节。l 您须要应用JS语言开发HarmonyOS利用。l 您的网络不能间接拜访Internet,而是须要通过代理服务器才能够拜访。这种状况下,配置npm代理,便于从npm服务器下载JS依赖。 关上命令行工具,依照如下形式进行npm代理设置和验证。 l 步骤 1 执行如下命令设置npm代理。l 如果您应用的代理服务器须要认证,请依照如下形式进行设置(请将user、password、proxyserver和port依照理论代理服务器进行批改)。 npm config set proxy http://user:password@proxyserver:port npm config set https-proxy http://user:password@proxyserver:portl 如果您应用的代理服务器不须要认证(不须要帐号和明码),请依照如下形式进行设置。 npm config set proxy http:proxyserver:port npm config set https-proxy http:proxyserver:port步骤 2 代理设置实现后,执行如下命令进行验证。 npm info express执行后果如下图所示,则阐明代理设置胜利。 设置Gradle代理如果您的网络不能间接拜访Internet,而是须要通过代理服务器才能够拜访,这种状况下,您须要设置Gradle代理,来拜访和下载Gradle所需的依赖。否则,请跳过本章节。 &emsp;&emsp;步骤 1&emsp;&emsp; 关上“此电脑”,在文件夹地址栏中输出%userprofile%,进入集体数据界面。 &emsp;&emsp;步骤 2&emsp;&emsp; 创立一个文件夹,命令为.gradle。如果已有.gradle文件夹,请跳过此操作。 &emsp;&emsp;步骤 3&emsp;&emsp; 进入.gradle文件夹,新建一个文本文档,命名为gradle,并批改后缀为.properties。 &emsp;&emsp;步骤 4&emsp;&emsp; 关上gradle.properties文件中,增加如下脚本,而后保留。 其中代理服务器、端口、用户名、明码和不应用代理的域名,请依据理论代理状况进行批改。其中不应用代理的“nonProxyHosts”的配置距离符是 “|”。 ...

September 19, 2020 · 2 min · jiezi

关于操作系统:操作系统线程知识总结

线程对于多过程的这种形式,仍然会存在问题: 过程之间如何通信,共享数据?保护过程的零碎开销较大,如创立过程时,分配资源、建设 PCB;终止过程时,回收资源、撤销 PCB;过程切换时,保留以后过程的状态信息;那到底如何解决呢?须要有一种新的实体,满足以下个性: 实体之间能够并发运行;实体之间共享雷同的地址空间;这个新的实体,就是线程( Thread ),线程之间能够并发运行且共享雷同的地址空间。 什么是线程?线程是过程当中的一条执行流程。 同一个过程内多个线程之间能够共享代码段、数据段、关上的文件等资源,但每个线程都有独立一套的寄存器和栈,这样能够确保线程的控制流是绝对独立的。线程的长处: 一个过程中能够同时存在多个线程;各个线程之间能够并发执行;各个线程之间能够共享地址空间和文件等资源;线程的毛病: 当过程中的一个线程奔溃时,会导致其所属过程的所有线程奔溃。线程与过程的比拟如下: 过程是资源(包含内存、关上的文件等)调配的单位,线程是 CPU 调度的单位;过程领有一个残缺的资源平台,而线程只独享必不可少的资源,如寄存器和栈;线程同样具备就绪、阻塞、执行三种根本状态,同样具备状态之间的转换关系;线程能缩小并发执行的工夫和空间开销;对于,线程相比过程能缩小开销,体现在: 线程的创立工夫比过程快,因为过程在创立的过程中,还须要资源管理信息,比方内存治理信息、文件治理信息,而线程在创立的过程中,不会波及这些资源管理信息,而是共享它们;线程的终止工夫比过程快,因为线程开释的资源相比过程少很多;同一个过程内的线程切换比过程切换快,因为线程具备雷同的地址空间(虚拟内存共享),这意味着同一个过程的线程都具备同一个页表,那么在切换的时候不须要切换页表。而对于过程之间的切换,切换的时候要把页表给切换掉,而页表的切换过程开销是比拟大的;因为同一过程的各线程间共享内存和文件资源,那么在线程之间数据传递的时候,就不须要通过内核了,这就使得线程之间的数据交互效率更高了;所以,线程比过程不论是工夫效率,还是空间效率都要高。 对于线程和过程,咱们能够这么了解: 当过程只有一个线程时,能够认为过程就等于线程;当过程领有多个线程时,这些线程会共享雷同的虚拟内存和全局变量等资源,这些资源在上下文切换时是不须要批改的;另外,线程也有本人的公有数据,比方栈和寄存器等,这些在上下文切换时也是须要保留的。 这还得看线程是不是属于同一个过程: 当两个线程不是属于同一个过程,则切换的过程就跟过程上下文切换一样;当两个线程是属于同一个过程,因为虚拟内存是共享的,所以在切换时,虚拟内存这些资源就放弃不动,只须要切换线程的公有数据、寄存器等不共享的数据;所以,线程的上下文切换相比过程,开销要小很多。 用户级线程和内核级线程用户级线程模型多个用户线程对应同一个内核线程 用户线程的调度是基于用户空间的线程函数库来实现的,用函数库来实现线程。 把创立线程、终止线程等性能放在了这个线程库内,用户就能够通过调用这些函数来实现所须要的性能。线程库,是位于用户空间的,操作系统内核对这个库无所不知,所以从内核的角度看,它还是按失常的形式治理。 线程管制块(_Thread Control Block, TCB_) 也是在库外面来实现的,对于操作系统而言是看不到这个 TCB 的,它只能看到整个过程的 PCB。所以,用户线程的整个线程治理和调度,操作系统是不直接参与的,而是由用户级线程库函数来实现线程的治理,包含线程的创立、终止、同步和调度等。 用户级线程的一个毛病,这些线程只能占用一个核,所以做不到并行减速,而且因为用户线程的透明性,操作系统是不能被动切换线程的,换句话讲,如果 A,B 是同一个过程的两个线程的话, A 正在运行的时候,线程 B 想要运行的话,只能期待 A 被动放弃CPU,也就是被动调用 pthread_yield函数。” 注:对操作系统来说,用户级线程具备不可见性,也称透明性。用户级线程做不到像过程那样的轮转调度,尽管不能做到轮转调度,但用户级线程也有本人的益处——能够为应用程序定制调度算法,毕竟什么时候退出线程你本人说了算。 在操作系统眼里,过程阻塞了,那么整个过程就会进入阻塞态,在阻塞操作完结前,这个过程都无奈失去 CPU 资源。那就相当于,所有的线程都被阻塞了。”小白得意的答复。 用户级线程模型的优缺点用户线程的长处: 每个过程都须要有它公有的线程管制块(TCB)列表,用来跟踪记录它各个线程状态信息(PC、栈指针、寄存器),TCB 由用户级线程库函数来保护,可用于不反对线程技术的操作系统;用户线程的切换也是由线程库函数来实现的,无需用户态与内核态的切换,所以速度特地快;用户线程的毛病: 因为操作系统不参加线程的调度,如果一个线程发动了零碎调用而阻塞,那过程所蕴含的用户线程都不能执行了。当一个线程开始运行后,除非它被动地交出 CPU 的使用权,否则它所在的过程当中的其余线程无奈运行,因为用户态的线程没法打断以后运行中的线程,它没有这个特权,只有操作系统才有,然而用户线程不是由操作系统治理的。因为工夫片调配给过程,故与其余过程比,在多线程执行时,每个线程失去的工夫片较少,执行会比较慢;内核级线程模型一个用户线程对应一个内核线程 许多操作系统都曾经反对内核级线程了。为了实现线程,内核里就须要有用来记录零碎里所有线程的线程表。当须要创立一个新线程的时候,就须要进行一个零碎调用,而后由操作系统进行线程表的更新。 操作系统内核如果晓得线程的存在,就能够像调度多个过程一样,把这些线程放在好几个 CPU 外围上,就能做到实际上的并行了。 如果线程可见,那么如果线程 A 阻塞了,与他同属一个过程的线程也不会被阻塞。这是内核级线程的绝对优势。 毛病是,让操作系统进行线程调度,那意味着每次切换线程,就须要「陷入」内核态,而操作系统从用户态到内核态的转变是有开销的,所以说内核级线程切换的代价要比用户级线程大。还有很重要的一点——线程表是寄存在操作系统固定的表格空间或者堆栈空间里,所以内核级线程的数量是无限的,扩展性比不上用户级线程。” 内核级线程模型的优缺点内核线程的长处: 在一个过程当中,如果某个内核线程发动零碎调用而被阻塞,并不会影响其余内核线程的运行;调配给线程,多线程的过程取得更多的 CPU 运行工夫;内核线程的毛病: 在反对内核线程的操作系统中,由内核来保护过程和线程的上下文信息,如 PCB 和 TCB;线程的创立、终止和切换都是通过零碎调用的形式来进行,因而对于零碎来说,零碎开销比拟大;轻量级线程模型轻量级过程(_Light-weight process,LWP_)是内核反对的用户线程,一个过程可有一个或多个 LWP,每个 LWP 是跟内核线程一对一映射的,也就是 LWP 都是由一个内核线程反对。 ...

September 11, 2020 · 1 min · jiezi

关于操作系统:HarmonyOS源码获取大全

源码获取详情请参考:https://gitee.com/openharmony/docs/blob/master/get-code/%E6%BA%90%E7%A0%81%E8%8E%B7%E5%8F%96.md OpenHarmony介绍 OpenHarmony是HarmonyOS的开源版,由华为捐献给凋谢原子开源基金会(OpenAtom Foundation)开源。第一个开源版本反对在128KB~128MB设施上运行,欢送加入开源社区一起继续演进。 代码仓库地址:https://openharmony.gitee.com 源码获取概述 本文档将介绍如何获取OpenHarmony源码并阐明OpenHarmony的源码目录构造。OpenHarmony的代码以组件的模式凋谢,开发者能够通过如下其中一种形式获取: 获取形式1: 从镜像站点下载压缩文件(举荐)获取形式2: 从hpm网站组件式获取。通过HPM,查找满足需要的解决方案,筛选/裁剪组件后下载。获取形式3: 用包管理器命令行工具获取。通过HPM的hpm-cli命令行工具,执行命令下载。获取形式4: 从代码仓库获取。通过repo或git工具从代码仓库中下载。获取形式1:从镜像站点获取 为了取得更好的下载性能,您能够抉择从以下站点的镜像库获取源码或者对应的解决方案。 表 1 源码获取门路 获取形式2:从hpm网站组件式获取实用场景 对于刚接触OpenHarmony的新用户,心愿可能参考一些示例解决方案从而进行疾速开发。能够在HPM获取举荐的解决方案,以此为根底,减少或裁剪局部组件,疾速定制零碎。 操作步骤查找适合的解决方案组件包。关上包治理页面HPM,设定搜寻的对象为“解决方案“,如下图所示。自搜寻框输出关键字搜寻,如"camera"。后果中显示匹配的解决方案,能够进一步依据组件类别等过滤条件(如:适配的开发板,内核)准确筛选。查找适合的解决方案,点击查看解决方案详情介绍定制解决方案组件包。 仔细阅读解决方案的阐明,以理解该解决方案的应用场景、个性、应用办法以及如何进行定制化,如下图所示。点击「间接下载」,将解决方案下载到本地。点击「定制组件」,将对解决方案蕴含的组件进行定制。定制组件。下载的压缩文件并未蕴含源代码的原始文件,能够在IDE中导入下载的压缩包,解压后执行hpm的装置指令(hpm install),才会将所须要的组件全副下载下来。下载的组件存在工程目录下的ohos_bundles文件夹中。进入解决方案定制页面,如下图所示。通过敞开开关移除可选组件,或者通过“增加组件”减少新的组件。在左边填写您的我的项目根本信息,包含名称、版本、形容等信息。点击“下载“,零碎会依据您的抉择,生成相应的OpenHarmony代码构造文件(如name.zip),保留至本地文件。获取形式3:用包管理器命令行获取实用场景用户已通过组件式获取的形式获取源码,须要对源码中的某个或某几个组件进行独立降级。用户曾经比拟相熟OpenHarmony零碎的开发并且熟练掌握命令行工具的应用。筹备 通过命令行获取,须要先装置Node.js和hpm命令行工具,装置步骤如下: 装置Node.js。官网下载并在本地装置Node.js.举荐装置 Node.js 12.x (蕴含 npm 6.14.4)或更高版本 (举荐 12.13.0+)。 通过Node.js自带的npm装置hpm命令行工具。关上CMD,执行以下命令: npm install -g @ohos/hpm-cli装置实现后执行如下命令,显示hpm版本,即装置胜利。hpm -V 或 hpm --version如果降级hpm的版本,请执行如下命令:npm update -g @ohos/hpm-cli操作 接下来将组件增加到开发我的项目中,假设要获取的组件名为@ohos/demo,具体操作如下: 进入开发目录,执行如下命令,采纳默认模板创立一个开发我的项目。hpm init -t default执行如下命令,装置组件@ohos/demohpm install @ohos/demo工具会主动从服务器下载所有依赖的组件,下载胜利则显示Install successfully!$ hpm install @ohos/demoRequesting: https://url.foo.bar/hpm/registry/api/bundles/@ohos/demodownloading @ohos/demoRequesting: https://lfcontentcenterdev....../bMAlLrYISLqdUTFFFCdgzA.tgzextract D:\demo\ohos_bundles\@ohos\demo\@ohos-demo-1.0.7.tgzInstall successfully!获取形式4:从代码仓库获取实用场景基于OpenHarmony的稳固分支建设本人的基线,散发上游客户。曾经实现本身软件与OpenHarmony的对接,须要进行OpenHarmony官网认证。芯片/模组/app通过OpenHarmony官网认证后,奉献代码到OpenHarmony生态。修复OpenHarmony的问题。学习OpenHarmony的源码。筹备注册码云gitee账号。注册码云SSH公钥,请参考码云帮忙核心的公钥治理:https://gitee.com/help/articles/4181装置git客户端并配置用户信息。git config --global user.name "yourname"git config --global user.email "your-email-address"git config --global credential.helper store装置码云repo工具,能够执行如下命令。curl https://gitee.com/oschina/repo/raw/fork_flow/repo-py3 > /usr/local/bin/repochmod a+x /usr/local/bin/repopip install -i https://pypi.tuna.tsinghua.edu.cn/simple requests操作形式一(举荐):通过repo下载 ...

September 11, 2020 · 1 min · jiezi

关于操作系统:先行者说HarmonyOS创新与AIoT智能硬件开发

详情请关注:https://harmonyos.51cto.com/activity/ids/?id=3 流动详情 HarmonyOS正式开源,将来已来。在这你将听到HarmonyOS先行者解说HarmonyOS设计的优越性,以及HarmonyOS如何赋能智能硬件行业,并通过多个场景案例解读人们该关注和期待什么样的HarmonyOS,让开发者清晰开源共建带给将来的有限可能。 直播话题预报: HarmonyOS的设计理念和技术创新(面向场景式开发,分布式,跨设施调度,虚构终端)智能手机的倒退催生挪动互联网翻新守业大潮,但明天挪动互联网曾经红利尽逝,翻新枯竭,互联网进入下半场。AIoT的趋势,AIoT目前现状和艰难,AIoT为什么发展缓慢,如何解决HarmonyOS个性联合AIoT需要的几个案例和畅想(智能家居,直播,在线教育等),体现HarmonyOS的微小商业价值,探讨HarmonyOS开源共建有限可能。本期嘉宾: 朱有鹏 毕业于西安交通大学,具备大型企业级我的项目研发教训和丰盛的教学教训。 互联网课程品牌《朱有鹏物联网大讲堂》创始人。著有《嵌入式linux与物联网软件开发-C语言内核深度解析》等书籍。相熟ARM Cortex-A、Cortex-M3/M4等体系结构;相熟三星平台S3C2440、S3C6410、S5PV210等处理器系列的Linux、WinCE下的开发流程;相熟全志平台A10、A20、A31等系列的linux、Android平台的利用;相熟Windows下C#+Winform界面开发,相熟WinCE嵌入式操作系统驱动及利用程序开发;相熟编译技术。 授课有趣风趣,解说条理清晰、通俗易懂,对常识有本人独到见解。能触类旁通,发散学生的思维,指引学生挖掘适宜本人的学习办法。 对于《先行者说》系列直播 《先行者说》是由HarmonyOS技术社区出品的一档聚焦HarmonyOS开源生态的直播流动,每期一个话题,邀请各行业、技术畛域的HarmonyOS先行者及KOL在线探讨HarmonyOS的开源共建、生态倒退、人才培养等相干话题,以期通过多个层面的观点碰撞,为学习者开拓路线,为开发者拓展视线,为产业生态激发生机。 HarmonyOS官网开发者论坛:https://developer.huawei.com/consumer/cn/forum/blockdisplay?fid=0101303901040230869 原文链接:https://developer.huawei.com/consumer/cn/forum/topicview?tid=0202350727815050540&fid=0101303901040230869 原作者:kelly

September 11, 2020 · 1 min · jiezi

关于操作系统:操作系统进程知识总结

过程咱们编写的代码只是一个存储在硬盘的动态文件,通过编译后就会生成二进制可执行文件,当咱们运行这个可执行文件后,它会被装载到内存中,接着 CPU 会执行程序中的每一条指令,那么这个运行中的程序,就被称为「过程」。当初咱们思考有一个会读取硬盘文件数据的程序被执行了,那么当运行到读取文件的指令时,就会去从硬盘读取数据,然而硬盘的读写速度是十分慢的,那么在这个时候,如果 CPU 傻傻的等硬盘返回数据的话,那 CPU 的利用率是非常低的。当过程要从硬盘读取数据时,CPU 不须要阻塞期待数据的返回,而是去执行另外的过程。当硬盘数据返回时,CPU 会收到个中断,于是 CPU 再持续运行这个过程。这种多个程序、交替执行的思维,就有 CPU 治理多个过程的初步想法。 对于一个反对多过程的零碎,CPU 会从一个过程疾速切换至另一个过程,其间每个过程各运行几十或几百个毫秒。 尽管单核的 CPU 在某一个霎时,只能运行一个过程。但在 1 秒钟期间,它可能会运行多个过程,这样就产生并行的错觉,实际上这是并发 过程的状态过程有着「运行 - 暂停 - 运行」的流动法则。一般说来,一个过程并不是从头至尾间断不停地运行的,它与并发执行中的其余过程的执行是互相制约的。 它有时处于运行状态,有时又因为某种原因而暂停运行处于期待状态,当使它暂停的起因隐没后,它又进入筹备运行状态。 所以,在一个过程的流动期间至多具备三种根本状态,即运行状态、就绪状态、阻塞状态。上图中各个状态的意义: 运行状态(_Runing_):该时刻过程占用 CPU;就绪状态(_Ready_):可运行,但因为其余过程正在运行而暂停进行;阻塞状态(_Blocked_):该过程正在期待某一事件产生(如期待输出/输入操作的实现)而临时进行运行,这时,即便给它CPU控制权,它也无奈运行;当然,过程另外两个根本状态: 创立状态(_new_):过程正在被创立时的状态;完结状态(_Exit_):过程正在从零碎中隐没时的状态;于是,一个残缺的过程状态的变迁如下图: _NULL -> 创立状态_:一个新过程被创立时的第一个状态;_创立状态 -> 就绪状态_:当过程被创立实现并初始化后,所有就绪筹备运行时,变为就绪状态,这个过程是很快的;_就绪态 -> 运行状态_:处于就绪状态的过程被操作系统的过程调度器选中后,就调配给 CPU 正式运行该过程;_运行状态 -> 完结状态_:当过程曾经运行实现或出错时,会被操作系统作完结状态解决;_运行状态 -> 就绪状态_:处于运行状态的过程在运行过程中,因为调配给它的运行工夫片用完,操作系统会把该过程变为就绪态,接着从就绪态选中另外一个过程运行;_运行状态 -> 阻塞状态_:当过程申请某个事件且必须期待时,例如申请 I/O 事件;_阻塞状态 -> 就绪状态_:当过程要期待的事件实现时,它从阻塞状态变到就绪状态;挂起状态概念 挂起过程在操作系统中能够定义为临时被淘汰出内存的过程,机器的资源是无限的,在资源有余的状况下,操作系统对在内存中的程序进行正当的安顿,其中有的过程被临时调离出内存,当条件容许的时候,会被操作系统再次调回内存,从新进入期待被执行的状态即就绪态 过程的控制结构在操作系统中,是用过程管制块(_process control block,PCB_)数据结构来形容过程的。PCB 是过程存在的惟一标识,这意味着一个过程的存在,必然会有一个 PCB,如果过程隐没了,那么 PCB 也会随之隐没 PCB蕴含: 过程状态:状态能够包含新的、就绪、运行、期待、进行等。程序计数器:计数器示意过程将要执行的下个指令的地址。CPU 寄存器:依据计算机体系结构的不同,寄存器的类型和数量也会不同。它们包含累加器、索引寄存器、堆栈指针、通用寄存器和其余条件码信息寄存器。在产生中断时,这些状态信息与程序计数器一起须要保留,以便过程当前能正确地继续执行。CPU 调度信息:这类信息包含过程优先级、调度队列的指针和其余调度参数。内存治理信息:依据操作系统应用的内存零碎,这类信息能够包含基地址和界线寄存器的值、页表或段表。记账信息:这类信息包含 CPU 工夫、理论应用工夫、工夫期限、记账数据、作业或过程数量等。I/O 状态信息:这类信息包含调配给过程的 I/O 设施列表、关上文件列表等。PCB组织形式通常是通过链表的形式进行组织,把具备雷同状态的过程链在一起,组成各种队列。比方: ...

September 11, 2020 · 1 min · jiezi

关于操作系统:30天自制操作系统

30天自制操作系统 下载地址 https://pan.baidu.com/s/1VkNiKw3OmH4DQmwIX8qECA 扫码上面二维码关注公众号回复 100024获取分享码 本书目录构造如下: 第0天 着手开发之前 1 1 前言 1 2 何谓操作系统 3 3 开发操作系统的各种办法 4 4 无知则无畏 4 5 如何开发操作系统 6 6 操作系统开发中的艰难 7 7 学习本书时的注意事项(重要!) 9 8 各章内容摘要 11 第1天 从计算机构造到汇编程序入门 13 1 先入手操作 13 2 到底做了些什么 19 3 首次体验汇编程序 22 4 加工润色 24 第2天 汇编语言学习与Makefile入门 28 1 介绍文本编辑器 28 2 持续开发 29 3 先制作启动区 40 4 Makefile入门 41 第3天 进入32位模式并导入C语言 45 ...

August 25, 2020 · 4 min · jiezi

关于操作系统:进程

过程通信 进程同步的形式有哪些?

August 14, 2020 · 1 min · jiezi

关于操作系统:一口气搞懂文件系统就靠这-25-张图了

前言不多 BB,间接上「硬菜」。 注释文件系统的根本组成文件系统是操作系统中负责管理持久数据的子系统,说简略点,就是负责把用户的文件存到磁盘硬件中,因为即便计算机断电了,磁盘里的数据并不会失落,所以能够长久化的保留文件。 文件系统的根本数据单位是文件,它的目标是对磁盘上的文件进行组织治理,那组织的形式不同,就会造成不同的文件系统。 Linux 最经典的一句话是:「所有皆文件」,不仅一般的文件和目录,就连块设施、管道、socket 等,也都是对立交给文件系统治理的。 Linux 文件系统会为每个文件调配两个数据结构:索引节点(index node)和目录项(directory entry),它们次要用来记录文件的元信息和目录层次结构。 索引节点,也就是 inode,用来记录文件的元信息,比方 inode 编号、文件大小、拜访权限、创立工夫、批改工夫、数据在磁盘的地位等等。索引节点是文件的惟一标识,它们之间一一对应,也同样都会被存储在硬盘中,所以索引节点同样占用磁盘空间。目录项,也就是 dentry,用来记录文件的名字、索引节点指针以及与其余目录项的层级关联关系。多个目录项关联起来,就会造成目录构造,但它与索引节点不同的是,目录项是由内核保护的一个数据结构,不寄存于磁盘,而是缓存在内存。因为索引节点惟一标识一个文件,而目录项记录着文件的名,所以目录项和索引节点的关系是多对一,也就是说,一个文件能够有多个别字。比方,硬链接的实现就是多个目录项中的索引节点指向同一个文件。 留神,目录也是文件,也是用索引节点惟一标识,和一般文件不同的是,一般文件在磁盘外面保留的是文件数据,而目录文件在磁盘外面保留子目录或文件。 目录项和目录是一个货色吗?尽管名字很相近,然而它们不是一个货色,目录是个文件,长久化存储在磁盘,而目录项是内核一个数据结构,缓存在内存。 如果查问目录频繁从磁盘读,效率会很低,所以内核会把曾经读过的目录用目录项这个数据结构缓存在内存,下次再次读到雷同的目录时,只需从内存读就能够,大大提高了文件系统的效率。 留神,目录项这个数据结构不只是示意目录,也是能够示意文件的。 那文件数据是如何存储在磁盘的呢?磁盘读写的最小单位是扇区,扇区的大小只有 512B 大小,很显著,如果每次读写都以这么小为单位,那这读写的效率会非常低。 所以,文件系统把多个扇区组成了一个逻辑块,每次读写的最小单位就是逻辑块(数据块),Linux 中的逻辑块大小为 4KB,也就是一次性读写 8 个扇区,这将大大提高了磁盘的读写的效率。 以上就是索引节点、目录项以及文件数据的关系,上面这个图就很好的展现了它们之间的关系: .png) 索引节点是存储在硬盘上的数据,那么为了减速文件的拜访,通常会把索引节点加载到内存中。 另外,磁盘进行格式化的时候,会被分成三个存储区域,别离是超级块、索引节点区和数据块区。 超级块,用来存储文件系统的详细信息,比方块个数、块大小、闲暇块等等。索引节点区,用来存储索引节点;数据块区,用来存储文件或目录数据;咱们不可能把超级块和索引节点区全副加载到内存,这样内存必定撑不住,所以只有当须要应用的时候,才将其加载进内存,它们加载进内存的机会是不同的: 超级块:当文件系统挂载时进入内存;索引节点区:当文件被拜访时进入内存;虚构文件系统文件系统的品种泛滥,而操作系统心愿对用户提供一个对立的接口,于是在用户层与文件系统层引入了中间层,这个中间层就称为虚构文件系统(Virtual File System,VFS)。 VFS 定义了一组所有文件系统都反对的数据结构和标准接口,这样程序员不须要理解文件系统的工作原理,只须要理解 VFS 提供的对立接口即可。 在 Linux 文件系统中,用户空间、零碎调用、虚拟机文件系统、缓存、文件系统以及存储之间的关系如下图: Linux 反对的文件系统也不少,依据存储地位的不同,能够把文件系统分为三类: 磁盘的文件系统,它是间接把数据存储在磁盘中,比方 Ext 2/3/4、XFS 等都是这类文件系统。内存的文件系统,这类文件系统的数据不是存储在硬盘的,而是占用内存空间,咱们常常用到的 /proc 和 /sys 文件系统都属于这一类,读写这类文件,实际上是读写内核中相干的数据数据。网络的文件系统,用来拜访其余计算机主机数据的文件系统,比方 NFS、SMB 等等。文件系统首先要先挂载到某个目录才能够失常应用,比方 Linux 零碎在启动时,会把文件系统挂载到根目录。 文件的应用咱们从用户角度来看文件的话,就是咱们要怎么应用文件?首先,咱们得通过零碎调用来关上一个文件。 fd = open(name, flag); # 关上文件...write(fd,...); # 写数据...close(fd); # 敞开文件下面简略的代码是读取一个文件的过程: ...

August 13, 2020 · 3 min · jiezi

关于操作系统:一口气搞懂文件系统就靠这-25-张图了

前言不多 BB,间接上「硬菜」。 注释文件系统的根本组成文件系统是操作系统中负责管理持久数据的子系统,说简略点,就是负责把用户的文件存到磁盘硬件中,因为即便计算机断电了,磁盘里的数据并不会失落,所以能够长久化的保留文件。 文件系统的根本数据单位是文件,它的目标是对磁盘上的文件进行组织治理,那组织的形式不同,就会造成不同的文件系统。 Linux 最经典的一句话是:「所有皆文件」,不仅一般的文件和目录,就连块设施、管道、socket 等,也都是对立交给文件系统治理的。 Linux 文件系统会为每个文件调配两个数据结构:索引节点(index node)和目录项(directory entry),它们次要用来记录文件的元信息和目录层次结构。 索引节点,也就是 inode,用来记录文件的元信息,比方 inode 编号、文件大小、拜访权限、创立工夫、批改工夫、数据在磁盘的地位等等。索引节点是文件的惟一标识,它们之间一一对应,也同样都会被存储在硬盘中,所以索引节点同样占用磁盘空间。目录项,也就是 dentry,用来记录文件的名字、索引节点指针以及与其余目录项的层级关联关系。多个目录项关联起来,就会造成目录构造,但它与索引节点不同的是,目录项是由内核保护的一个数据结构,不寄存于磁盘,而是缓存在内存。因为索引节点惟一标识一个文件,而目录项记录着文件的名,所以目录项和索引节点的关系是多对一,也就是说,一个文件能够有多个别字。比方,硬链接的实现就是多个目录项中的索引节点指向同一个文件。 留神,目录也是文件,也是用索引节点惟一标识,和一般文件不同的是,一般文件在磁盘外面保留的是文件数据,而目录文件在磁盘外面保留子目录或文件。 目录项和目录是一个货色吗?尽管名字很相近,然而它们不是一个货色,目录是个文件,长久化存储在磁盘,而目录项是内核一个数据结构,缓存在内存。 如果查问目录频繁从磁盘读,效率会很低,所以内核会把曾经读过的目录用目录项这个数据结构缓存在内存,下次再次读到雷同的目录时,只需从内存读就能够,大大提高了文件系统的效率。 留神,目录项这个数据结构不只是示意目录,也是能够示意文件的。 那文件数据是如何存储在磁盘的呢?磁盘读写的最小单位是扇区,扇区的大小只有 512B 大小,很显著,如果每次读写都以这么小为单位,那这读写的效率会非常低。 所以,文件系统把多个扇区组成了一个逻辑块,每次读写的最小单位就是逻辑块(数据块),Linux 中的逻辑块大小为 4KB,也就是一次性读写 8 个扇区,这将大大提高了磁盘的读写的效率。 以上就是索引节点、目录项以及文件数据的关系,上面这个图就很好的展现了它们之间的关系: .png) 索引节点是存储在硬盘上的数据,那么为了减速文件的拜访,通常会把索引节点加载到内存中。 另外,磁盘进行格式化的时候,会被分成三个存储区域,别离是超级块、索引节点区和数据块区。 超级块,用来存储文件系统的详细信息,比方块个数、块大小、闲暇块等等。索引节点区,用来存储索引节点;数据块区,用来存储文件或目录数据;咱们不可能把超级块和索引节点区全副加载到内存,这样内存必定撑不住,所以只有当须要应用的时候,才将其加载进内存,它们加载进内存的机会是不同的: 超级块:当文件系统挂载时进入内存;索引节点区:当文件被拜访时进入内存;虚构文件系统文件系统的品种泛滥,而操作系统心愿对用户提供一个对立的接口,于是在用户层与文件系统层引入了中间层,这个中间层就称为虚构文件系统(Virtual File System,VFS)。 VFS 定义了一组所有文件系统都反对的数据结构和标准接口,这样程序员不须要理解文件系统的工作原理,只须要理解 VFS 提供的对立接口即可。 在 Linux 文件系统中,用户空间、零碎调用、虚拟机文件系统、缓存、文件系统以及存储之间的关系如下图: Linux 反对的文件系统也不少,依据存储地位的不同,能够把文件系统分为三类: 磁盘的文件系统,它是间接把数据存储在磁盘中,比方 Ext 2/3/4、XFS 等都是这类文件系统。内存的文件系统,这类文件系统的数据不是存储在硬盘的,而是占用内存空间,咱们常常用到的 /proc 和 /sys 文件系统都属于这一类,读写这类文件,实际上是读写内核中相干的数据数据。网络的文件系统,用来拜访其余计算机主机数据的文件系统,比方 NFS、SMB 等等。文件系统首先要先挂载到某个目录才能够失常应用,比方 Linux 零碎在启动时,会把文件系统挂载到根目录。 文件的应用咱们从用户角度来看文件的话,就是咱们要怎么应用文件?首先,咱们得通过零碎调用来关上一个文件。 fd = open(name, flag); # 关上文件...write(fd,...); # 写数据...close(fd); # 敞开文件下面简略的代码是读取一个文件的过程: ...

August 13, 2020 · 3 min · jiezi

关于操作系统:操作系统汇总

1. 过程和线程的区别,多过程和多线程之间的区别2. 过程之间是如何切换的

July 31, 2020 · 1 min · jiezi

关于操作系统:操作系统汇总

1. 过程和线程的区别,多过程和多线程之间的区别2. 过程之间是如何切换的

July 31, 2020 · 1 min · jiezi

关于操作系统:多线程为了同个资源打起架来了该如何让他们安定

前言先来看看虚构的小故事曾经早晨 11 点了,程序员小明的双手还在键盘上飞舞着,眼神仍然凝视着的电脑屏幕。 没方法这段时间公司业绩增长中,需要天然也多了起来,加班天然也少不了。 天气变化莫测,这时窗外下起了蓬勃大雨,同时闪电轰鸣。 但这一丝都没有影响到小明,始料未及,忽然一道微小的雷一闪而过,办公楼就这么停电了,随后整栋楼都在回荡着的小明那一声撕心裂肺的「卧槽」。 此时,求小明的心里面积有多大? 等小明心里平复后,忽然肚子十分的痛,想上厕所,小明心想必定是早晨吃的某堡王有问题。 整栋楼都停了电,小明两眼一抹黑,啥都看不见,只能靠摸墙的办法,一步一步的来到了厕所门口。 到了厕所(共享资源),因为切实太急,小明间接冲入了厕所里,用手摸索着刚好第一个门没锁门,便夺门而入。 这就荒谬了,这个门外面正好小红在上着厕所,正好这个厕所门是坏了的,没方法锁门。 光明中,小红尽管看不见,但靠着声音,发现自己背后的这扇门有动静,感觉不对劲,于是铆足了力量,用她衣着高跟鞋脚,使劲地一脚踢了过来。 小明很侥幸,被踢中了「命根子」,撕心裂肺地喊出了一个字「痛」! 故事说完了,扯了那么多,实际上是为了阐明,对于共享资源,如果没有上锁,在多线程的环境里,那么就可能会产生翻车现场。 接下来,用 30+ 张图,带大家走进操作系统中防止多线程资源竞争的互斥、同步的办法。 注释竞争与合作在单核 CPU 零碎里,为了实现多个程序同时运行的假象,操作系统通常以工夫片调度的形式,让每个过程执行每次执行一个工夫片,工夫片用完了,就切换下一个过程运行,因为这个工夫片的工夫很短,于是就造成了「并发」的景象。 另外,操作系统也为每个过程创立微小、公有的虚拟内存的假象,这种地址空间的形象让每个程序如同领有本人的内存,而实际上操作系统在背地机密地让多个地址空间「复用」物理内存或者磁盘。 如果一个程序只有一个执行流程,也代表它是单线程的。当然一个程序能够有多个执行流程,也就是所谓的多线程程序,线程是调度的根本单位,过程则是资源分配的根本单位。 所以,线程之间是能够共享过程的资源,比方代码段、堆空间、数据段、关上的文件等资源,但每个线程都有本人独立的栈空间。 那么问题就来了,多个线程如果竞争共享资源,如果不采取有效的措施,则会造成共享数据的凌乱。 咱们做个小试验,创立两个线程,它们别离对共享变量 i 自增 1 执行 10000 次,如下代码(尽管说是 C++ 代码,然而没学过 C++ 的同学也是看到懂的): 按理来说,i 变量最初的值应该是 20000,但很可怜,并不是如此。咱们对下面的程序执行一下: 运行了两次,发现呈现了 i 值的后果是 15173,也会呈现 20000 的 i 值后果。 每次运行岂但会产生谬误,而且失去不同的后果。在计算机里是不能容忍的,尽管是小概率呈现的谬误,然而小概率事件它肯定是会产生的,「墨菲定律」大家都懂吧。 为什么会产生这种状况?为了了解为什么会产生这种状况,咱们必须理解编译器为更新计数器 i 变量生成的代码序列,也就是要理解汇编指令的执行程序。 在这个例子中,咱们只是想给 i 加上数字 1,那么它对应的汇编指令执行过程是这样的: 能够发现,只是单纯给 i 加上数字 1,在 CPU 运行的时候,实际上要执行 3 条指令。 ...

July 20, 2020 · 3 min · jiezi

进程线程基础知识全家桶30-张图一套带走

前言先来看看一则小故事咱们写好的一行行代码,为了让其工作起来,咱们还得把它送进城(过程)里,那既然进了城里,那必定不能胡作非为了。 城里人有城里人的规矩,城中有个专门管辖你们的城管(操作系统),人家让你劳动就劳动,让你工作就工作,毕竟摊位不多,每个人都要占这个摊位来工作,城里要工作的人多着去了。 所以城管为了偏心起见,它应用一种策略(调度)形式,给每个人一个固定的工作工夫(工夫片),工夫到了就会告诉你去劳动而换另外一个人上场工作。 另外,在劳动时候你也不能偷懒,要记住工作到哪了,不然下次到你工作了,你遗记工作到哪了,那还怎么持续? 有的人,可能还进入了县城(线程)工作,这里绝对轻松一些,在劳动的时候,要记住的货色绝对较少,而且还能共享城里的资源。 “哎哟,难道本文内容是过程和线程?”能够,聪慧的你猜出来了,也不枉费我瞎编乱造的故事了。 过程和线程对于写代码的咱们,真的天天见、日日见了,但见的多不代表你就相熟它们,比方简略问你一句,你晓得它们的工作原理和区别吗? 不晓得没关系,明天就要跟大家探讨操作系统的过程和线程。 注释过程咱们编写的代码只是一个存储在硬盘的动态文件,通过编译后就会生成二进制可执行文件,当咱们运行这个可执行文件后,它会被装载到内存中,接着 CPU 会执行程序中的每一条指令,那么这个运行中的程序,就被称为「过程」。 当初咱们思考有一个会读取硬盘文件数据的程序被执行了,那么当运行到读取文件的指令时,就会去从硬盘读取数据,然而硬盘的读写速度是十分慢的,那么在这个时候,如果 CPU 傻傻的等硬盘返回数据的话,那 CPU 的利用率是非常低的。 做个类比,你去煮开水时,你会傻傻的等水壶烧开吗?很显著,小孩也不会傻等。咱们能够在水壶烧开之前去做其余事件。当水壶烧开了,咱们天然就会听到“嘀嘀嘀”的声音,于是再把烧开的水倒入到水杯里就好了。 所以,当过程要从硬盘读取数据时,CPU 不须要阻塞期待数据的返回,而是去执行另外的过程。当硬盘数据返回时,CPU 会收到个中断,于是 CPU 再持续运行这个过程。 这种多个程序、交替执行的思维,就有 CPU 治理多个过程的初步想法。 对于一个反对多过程的零碎,CPU 会从一个过程疾速切换至另一个过程,其间每个过程各运行几十或几百个毫秒。 尽管单核的 CPU 在某一个霎时,只能运行一个过程。但在 1 秒钟期间,它可能会运行多个过程,这样就产生并行的错觉,实际上这是并发。 并发和并行有什么区别?一图胜千言。 过程与程序的关系的类比到了晚饭时间,一对小情侣肚子都咕咕叫了,于是男生见风使舵,就想给女生做晚饭,所以他就在网上找了辣子鸡的菜谱,接着买了一些鸡肉、辣椒、香料等资料,而后边看边学边做这道菜。 忽然,女生说她想喝可乐,那么男生只好把做菜的事件暂停一下,并在手机菜谱标记做到哪一个步骤,把状态信息记录了下来。 而后男生服从女生的指令,跑去下楼买了一瓶冰可乐后,又回到厨房持续做菜。 这体现了,CPU 能够从一个过程(做菜)切换到另外一个过程(买可乐),在切换前必须要记录以后过程中运行的状态信息,以备下次切换回来的时候能够复原执行。 所以,能够发现过程有着「运行 - 暂停 - 运行」的流动法则。 过程的状态在下面,咱们晓得了过程有着「运行 - 暂停 - 运行」的流动法则。一般说来,一个过程并不是从头至尾间断不停地运行的,它与并发执行中的其余过程的执行是互相制约的。 它有时处于运行状态,有时又因为某种原因而暂停运行处于期待状态,当使它暂停的起因隐没后,它又进入筹备运行状态。 所以,在一个过程的流动期间至多具备三种根本状态,即运行状态、就绪状态、阻塞状态。 上图中各个状态的意义: 运行状态(Runing):该时刻过程占用 CPU;就绪状态(Ready):可运行,但因为其余过程正在运行而暂停进行;阻塞状态(Blocked):该过程正在期待某一事件产生(如期待输出/输入操作的实现)而临时进行运行,这时,即便给它CPU控制权,它也无奈运行;当然,过程另外两个根本状态: 创立状态(new):过程正在被创立时的状态;完结状态(Exit):过程正在从零碎中隐没时的状态;于是,一个残缺的过程状态的变迁如下图: 再来具体阐明一下过程的状态变迁: NULL -> 创立状态:一个新过程被创立时的第一个状态;创立状态 -> 就绪状态:当过程被创立实现并初始化后,所有就绪筹备运行时,变为就绪状态,这个过程是很快的;就绪态 -> 运行状态:处于就绪状态的过程被操作系统的过程调度器选中后,就调配给 CPU 正式运行该过程;运行状态 -> 完结状态:当过程曾经运行实现或出错时,会被操作系统作完结状态解决;运行状态 -> 就绪状态:处于运行状态的过程在运行过程中,因为调配给它的运行工夫片用完,操作系统会把该过程变为就绪态,接着从就绪态选中另外一个过程运行;运行状态 -> 阻塞状态:当过程申请某个事件且必须期待时,例如申请 I/O 事件;阻塞状态 -> 就绪状态:当过程要期待的事件实现时,它从阻塞状态变到就绪状态;如果有大量处于阻塞状态的过程,过程可能会占用着物理内存空间,显然不是咱们所心愿的,毕竟物理内存空间是无限的,被阻塞状态的过程占用着物理内存就一种节约物理内存的行为。 ...

July 12, 2020 · 3 min · jiezi

56张图入门操作系统内功心法适合所有程序员

关注公众号“执鸢者”,回复“书籍”获取大量学习材料,回复“前端视频”获取大量教学视频,回复“操作系统”获取本节整体思维导图。 本文次要是操作系统相干内容,利用56张思维导图从OS概述、过程治理、内存治理、文件治理、I/O五局部零碎的理解了操作系统,通过对操作系统的理解,让咱们撸码时怎么做、为什么这样做、怎么做能够进步性能做到有理可依。 一、OS概述 1.1 概念 1.2 性能和指标 1.3 特色 1.4 操作系统的倒退与分类 1.5 操作系统的运行机制和体系结构 1.6 中断和异样 1.7 零碎调用 二、过程治理 2.1 过程的概念、组成、特色 2.2 过程的状态与转换 2.3 过程管制 2.4 过程通信 2.5 线程 2.6 处理机调度 2.7 过程调度(低级调度) 2.8 调度算法的评估指标 2.9 调度算法 2.10 进程同步、互斥 2.11 过程互斥的实现办法 2.12 信号量机制 2.13 管程 2.14 死锁 三、内存治理 3.1 内存的根底 3.2 内存空间的调配与回收 3.2.1 间断调配治理形式 3.2.2 根本分页存储管理 3.2.3 根本分段存储管理 3.2.4 段页式存储管理 3.3 内存空间的扩大(实现虚拟性) 3.3.1 虚拟存储技术 3.4地址转换 3.5 存储保护 ...

July 12, 2020 · 1 min · jiezi

计算机操作系统基础十七进程同步之Unix域套接字

引言本篇为第十七篇,进程同步之Unix域套接字。上一篇介绍了通过共享内存解决进程同步的问题,本文是实现进程同步的另一个办法---Unix域套接字 Unix域套接字域套接字是一种高级的过程间通信的办法Unix域套接字能够用于同一机器过程间通信套接字(socket)原是网络通信中应用的术语Unix零碎提供的域套接字提供了网络套接字相似的性能在前边理解到,共享内存须要额定的同步机制,来同步多个过程间的通信。Unix域套接字就不须要额定的机制来保障多个过程间通信的问题(其实咱们在部署Nginx的时候,就会应用到unix域套接字) Unix域套接字应用办法右边为服务端,左边为客户端 下边是代码示例,客户端和服务端通过域套接字进行连贯 服务端(server.cpp) #include<stdio.h>#include<sys/types.h>#include<sys/socket.h>#include<sys/un.h>#include<strings.h>#include<string.h>#include<netinet/in.h>#include<stdlib.h>#include<iostream>//定义域套接字门路//应用域套接字时,它会在文件系统中创立一个文件,客户端和服务端就是通过这个文件进行连贯 #define SOCKET_PATH "./domainsocket"//定义音讯最大长度#define MSG_SIZE 2048int main(){ int socket_fd,accept_fd; int ret = 0; socklen_t addr_len; char msg[MSG_SIZE]; struct sockaddr_un server_addr; //1.创立域套接字 socket_fd = socket(PF_UNIX, SOCK_STREAM, 0); if (-1 == socket_fd) { std::cout << "Socket create failed" << std::endl; return -1; } //移除已有域套接字门路 remove(SOCKET_PATH); //内存区域设置为0 bzero(&server_addr, sizeof(server_addr)); server_addr.sun_family = PF_UNIX; strcpy(server_addr.sun_path, SOCKET_PATH); //2.绑定域套接字 std::cout << "Binding socket..." << std::endl; ret = bind(socket_fd, (sockaddr *)&server_addr, sizeof(server_addr)); if (0 > ret) { std::cout << "Bind socket failed." << std::endl; return -1; } //3.监听套接字 std::cout << "Listening socket..." << std::endl; ret = listen(socket_fd, 10);//数字为监听连贯的大小 if(-1 == ret) { std::cout << "Listen failed" << std::endl; } std::cout << "Waiting new request." << std::endl; accept_fd = accept(socket_fd, NULL, NULL); bzero(msg, MSG_SIZE); while(true) { //4.接管&解决信息 recv(accept_fd, msg, MSG_SIZE, 0); std::cout << "Received message from remote: " << msg << std::endl; } close(accept_fd); close(socket_fd); return 0; }客户端(client.cpp) ...

July 11, 2020 · 2 min · jiezi

计算机操作系统基础十六进程同步之共享内存

引言本篇为第十六篇,进程同步之共享内存。前边介绍到的都是解决线程同步的办法,本文为解决进程同步的办法---共享内存 共享内存线程同步 每个过程可能会有一个或多个线程,线程是共享过程资源的,线程之间也须要通信,又或者说线程之间须要同步一些过程资源的状态,此时就须要线程之间的信息同步 进程同步 一个操作系统中可能有一个或多个过程,过程是共享计算机资源的(包含内存、磁盘等都是共享的),因而在过程之间也同样须要信息同步。在之前的文章中提到的生产者-消费者问题,以及哲学家进餐问题都是进程同步的起因 在学习本篇的共享内存之前,回顾一下之前说到的操作系统是怎么进行过程治理的。每一个过程都有本人的过程空间,并且他们的过程空间是通过页表通过段页式存储管理和理论的内存建设起映射的。过程之间他们的过程空间是互不烦扰的,互相独立的 因而: 在某种程度上,多过程是独特应用物理内存的(也就是说多过程是共享物理内存的)因为操作系统的过程治理,过程间的内存空间是独立的(也就是任意两个过程他们逻辑上的内存空间是齐全没有分割的),这样就保障了每一个过程它们独立运行的安全性(这也是过程治理的一个作用)过程默认是不能拜访过程空间之外的内存空间的(也就是一个过程不能拜访另外一个过程的内存空间) 然而,共享内存就能够突破这样的限度,通过共享内存,过程就能够通过页表来映射到同样的一片内存中去,这片内存既能够被过程1所应用,也能够被过程2所应用。也就是这片共享内存能够被过程1所读或者是写,同样也能够被过程2所读或者所写。因而,通过共享内存,过程1和过程2建设了分割。那么共享内存也是操作系统所提供的重要的进程同步的办法 共享内存介绍 共享存储容许不相干的过程拜访同一片物理内存(它实现的原理就是把一片雷同的物理内存映射到不同过程的页表中去,使得不同的过程通过页表能够拜访雷同的一块物理内存)共享内存是两个过程之间共享和传递数据最快的形式共享内存未提供同步机制,须要借助其它机制治理拜访,以防止并发拜访所带来的问题注:同步机制:在并发程序设计中,各过程对公共变量的拜访必须加以制约,这种制约称为同步 应用共享内存步骤 申请共享内存将共享内存连贯到过程空间(这样过程就能够通过页表拜访共享内存)应用共享内存脱离过程空间&删除 代码示例 例子中会有一个客户端和一个服务端,他们之间通过共享内存进行通信 common.h放的是一些公共信息 #ifndef __COMMON_H__#define __COMMON_H__//共享内存中的字符串最大长度#define TEXT_LEN 2048 //共享内存的数据结构//因为在默认状况下,内存里边存储的形式是没有数据结构的,当程序须要应用共享内存的话,就须要定义数据结构,把构造数据存储到共享内存struct ShmEntry{ //是否能够读取共享内存,用于过程间同步 bool can_read //共享内存信息 char msg[2048]};#endifserver.cpp #include "common.h"#include <sys/shm.h>#include <stdlib.h>#include <unistd.h>#include <stdio.h>#include <string.h>#include <iostream>int main(){ //定义共享内存结构图 struct ShmEntry *entry; //1.申请共享内存 int shmid = shmget((key_t)1111, sizeof(struct ShmEntry), 0666|IPC_CREAT); if(shmid == -1) { std::cout << "Create share memory error!" << std::endl; return -1; } //2.连贯到以后过程空间/应用共享内存 entry = (ShmEntry*)shmat(shmid, 0, 0); entry->can_read = 0; while(true){ if(entry->can_read == 1) { std::cout<< "Received message" << entry->msg << std::endl; entry->can_read = 0; } else { std::cout << "Entry can not read. Sleep 1s" << std::endl; sleep(1); } } //3.脱离过程空间 shmdt(entry); //4.删除共享内存 shmctl(shmid, IPC_RMID, 0); return 0;}client.cpp ...

July 9, 2020 · 1 min · jiezi

计算机操作系统基础十五使用fork系统调用创建进程

引言本文为第十五篇,使用fork系统调用创建进程。创建进程属于非常重要的内容,无论是哪种语言,底层在创建进程的时候都是使用fork函数,本文使用C语言来熟悉fork系统调用创建进程 使用fork系统调用创建进程fork系统调用是用于创建进程的fork创建的进程初始化状态是和父进程一样的(进程有进程空间、内存、内存态等)系统会为fork的进程分配新的资源(包括内存资源、CPU资源等)fork系统调用无参数fork会返回两次,分别返回子进程id和0(第一次是由父进程返回的,第二次由子进程所返回的,因此返回了两次)返回子进程id的是父进程,返回0的是子进程调用fork之后,我们就可以根据返回值是否为0来判断是父进程还是子进程返回的 代码示例: #include<iostream>#include<cstring>#include<stdio.h>#include<unistd.h>using namespace std;int main(){ pid_t pid; pid = fork(); if(pid == 0) { cout << "这是一个子进程" << endl; } else if(pid > 0) { cout << "这是一个父进程" << endl; cout << "子进程id:" << pid << endl; } else if(pid < 0 ){ cout << "创建进程失败" << endl; } return 0;}运行结果: 从运行结果可以看到,fork确实是返回了两次,两个if里边都走到了 在前边也说到,当fork创建一个子进程的时候,这个子进程的初始化内存状态是和父进程一样的,下边也用代码验证一下: #include<iostream>#include<cstring>#include<stdio.h>#include<unistd.h>using namespace std;int main(){ pid_t pid; int num = 888; pid = fork(); if(pid == 0) { cout << "这是一个子进程" << endl; cout << "num in son process:"<< num << endl; while(true) { num+=1; cout << "num in son process:"<< num << endl; sleep(1); } } else if(pid > 0) { cout << "这是一个父进程" << endl; cout << "子进程id:" << pid << endl; cout << "num in father process:"<< num << endl; while(true) { num-=1; cout << "num in father process:"<< num << endl; sleep(1); } } else if(pid < 0 ){ cout << "创建进程失败" << endl; } return 0;}运行结果: ...

July 8, 2020 · 1 min · jiezi

计算机操作系统基础十四线程同步之条件变量

引言本文为第十四篇,线程同步之条件变量,在上一篇文章是介绍了读写锁,读写锁在多读少写的情况下,性能要强于互斥量。本篇再介绍一种重要的处理线程同步的方法---条件变量 条件变量条件变量是一种相对复杂的线程同步方法条件变量允许线程睡眠,直到满足某种条件当满足条件时,可以向该线程发送信号,通知唤醒线程对于前边所介绍的生产者-消费者模型,其实是有一定的漏洞的 当缓冲区小于0时,不允许消费者消费,消费者必须等待当缓冲区满时,不允许生产者往缓冲区生产,生产者必须等待在前边的文章中并没有对这个进行约束,在本文学习条件变量时,将对这个进行更严谨的约束 假设此时缓冲区等于0,当生产者生产一个产品时,唤醒可能等待的消费者。假设缓冲区满时,当消费者消费一个产品时,唤醒可能等待的生产者 条件变量的示例(需要配合互斥量来使用) #include<iostream>#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<pthread.h>#include<vector>#include<queue>//临界资源int num=0;//缓冲区最大值int MAX_BUF=100;//定义条件变量pthread_cond_t cond=PTHREAD_COND_INITIALIZER;//定义互斥量pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;void* producer(void*){ while(true){ pthread_mutex_lock(&mutex); while(num>=MAX_BUF){ //等待 pthread_cond_wait(&cond, &mutex); printf("缓冲区满了,等待消费者消费\n"); } num+=1; printf("生产一个产品,当前产品数量为:%d", num); sleep(1);//生产产品所需时间 //通知可能等待的消费者 pthread_cond_signal(&cond); printf("通知消费者...\n"); //解锁 pthread_mutex_unlock(&mutex); sleep(1);生产产品的频率 }}void* consumer(void*){ while(true){ pthread_mutex_lock(&mutex); while(num<=0){ //等待 pthread_cond_wait(&cond, &mutex); printf("缓冲区空了,等待生产者生产\n"); } num-=1; printf("消费一个产品,当前产品数量为:%d", num); sleep(1); //通知可能等待的生产者 pthread_cond_signal(&cond); printf("通知生产者...\n"); //解锁 pthread_mutex_unlock(&mutex); }}int main(){ //定义两个线程 pthread_t thread1,thread2; //一个执行生产者逻辑,一个执行消费者逻辑 pthread_create(&thread1, NULL, &producer, NULL); pthread_create(&thread2, NULL, &consumer, NULL); pthread_join(&thread1, NULL); pthread_join(&thread2, NULL); return 0;}执行结果: ...

July 7, 2020 · 1 min · jiezi

计算机操作系统基础十三线程同步之读写锁

引言本文为第十三篇,线程同步之读写锁,读写锁也是解决线程同步的方法之一,在前边的两篇文章中国已经介绍了互斥量和自旋锁两种方法。读写锁的原理也和前边两种锁类似,但是读写锁做了一些改进 读写锁读写锁的改进是从以下几个点进行考量的,其中最重要的是对临界资源的考量。在复杂的开发环境中,很可能会出现对临界资源多读少写的情况。比如说一个数据库中的表,存储的主要是一些历史数据,对于这些历史数据,一般都是进行查询,很少进行修改,那么这个存储历史数据的表就属于多读少写的临界资源。对于读取的时候并不会改变临界资源的值,如果我们每一次去读或者写的时候都给它加锁,这样效率是很低的。那么这个时候就应该考虑是否有效率更高的方法?这个时候就产生了读写锁 读写锁介绍 读写锁是一种特殊的自旋锁允许多个读者同时访问资源,以提高读性能对于写操作是互斥的(不允许多个写操作同时访问同一资源)关于读写锁的模型 对于读写锁,它允许多个读者同时去读取临界资源,因此下图中的读线程1、2、3可以同时读取临界资源,但是在读取的同时,它不会允许写操作去访问临界资源。因为读取的时候并不会改变临界资源,而写操作可能会改变临界资源的值,因此在读写锁中读和写是互斥的,读和读之间是不互斥的 读写锁示例 #include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<pthread.h>#include<vector>//临界资源int num=0;//定义读写锁pthread_rwlock_t relock=PTHREAD_RWLOCK_INITIALIZER;void *reader(void*){ int times=10000000; while(times--){ //在读操作之前加读锁 pthread_rwlock_rdlock(&rdlock); if(times%1000==0){ usleep(10); } //释放读锁 pthread_rwlock_unlock(&rdlock); }}void *writer(void*){ int times=10000000; while(times--){ //加写锁 pthread_rwlock_wrlock(&rdlock); num+=1; pthread_rwlock_unlock(&rdlock); }}int main(){ printf("Start in main function."); //定义三个线程 pthread_t thread1,thread2, thread3; //两个执行读操作,一个执行写操作 pthread_create(&thread1, NULL, &reader, NULL); pthread_create(&thread2, NULL, &reader, NULL); pthread_create(&thread3, NULL, &writer, NULL); pthread_join(&thread1, NULL); pthread_join(&thread2, NULL); pthread_join(&thread3, NULL); //打印临界资源的值 printf("Print in main function: num = %d\n", num); return 0;}因为上边提到,读写锁对于多读少写的情况下,会有很明显的性能提升的,此时就可以验证一下,运行上边使用的读写锁的程序,查看运行时间如下: ...

July 6, 2020 · 1 min · jiezi