使用 Hugo + Typora + Github Action 完美博客

安装Hugo 在本地安装 Hugo, brew install hugo 安装成功后查看Hugo 版本 hugo version ➜ blog git:(main) hugo version hugo v0.91.0+extended darwin/amd64 BuildDate=unknown 创建一个site,site其实就是一个博客地址 : hugo new site test ➜ workspace hugo new site test Congratulations! Your new Hugo site is created in /Users/mac/workspace/test. Just a few more steps and you're ready to go: 1. Download a theme into the same-named folder. Choose a theme from https://themes.gohugo.io/ or create your own with the "hugo new theme <THEMENAME>" command....

February 1, 2021 · 2 min · walker

Epoll

epoll是在2.6内核中提出的,是之前的select和poll的增强版本。相对于select和poll来说,epoll更加灵活,没有描述符限制。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。 基本原理 epoll支持水平触发和边缘触发,最大的特点在于边缘触发,它只告诉进程哪些fd刚刚变为就绪态,并且只会通知一次。还有一个特点是,epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知。 epoll 优点 select缺点 单个进程可监视的fd数量被限制。 需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大。 3) 对fd进行扫描时是线性扫描。fd剧增后,IO效率较低,因为每次调用都对fd进行线性扫描遍历,所以随着fd的增加会造成遍历速度慢的性能问题 4)select() 函数的超时参数在返回时也是未定义的,考虑到可移植性,每次在超时之后在下一次进入到select之前都需要重新设置超时参数。 没有最大并发连接的限制,能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口)。而select的上限只有1024, 效率提升,不是轮询的方式,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数;即Epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,Epoll的效率就会远远高于select和poll。 内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap减少复制开销。而 select 和poll 在遍历fd集合都需要将集合从用户态拷贝到内核态, 水平触发和边缘触发 水平触发 对于读操作 : 只要缓冲内容不为空,LT模式返回读就绪。 对于写操作 : 只要缓冲区还不满,LT模式会返回写就绪。 边缘触发 对于读操作 : 当缓冲区由不可读变为可读的时候,即缓冲区由空变为不空的时候。 当有新数据到达时,即缓冲区中的待读数据变多的时候。 当缓冲区有数据可读,且应用进程对相应的描述符进行EPOLL_CTL_MOD 修改EPOLLIN事件时。 对于写操作 当缓冲区由不可写变为可写时 当有旧数据被发送走,即缓冲区中的内容变少时 当缓冲区有空间可写,且应用进程对相应的描述符进行EPOLL_CTL_MOD 修改EPOLLOUT事件时。 epoll的惊群效应 惊群现象就是多进程(多线程)在同时阻塞等待同一个事件的时候(休眠状态),如果等待的这个事件发生,那么他就会唤醒等待的所有进程(或者线程),但是最终却只可能有一个进程(线程)获得这个时间的“控制权”,对该事件进行处理,而其他进程(线程)获取“控制权”失败,只能重新进入休眠状态,这种现象和性能浪费就叫做惊群。 惊群效应到底消耗了什么 ? 系统对用户进程/线程频繁地做无效的调度,上下文切换系统性能大打折扣。 为了确保只有一个线程得到资源,用户必须对资源操作进行加锁保护,进一步加大了系统开销。 Golang 中epoll的应用 go 语言实现的网络库中就是通过epoll 模式实现的,在src/runtime/netpoll_*.go 可以看到其源码。网络上很多对这些源码的分析,不是重点。就知道为什么Golang网络并发为何如此优秀即可。这也是为何当您在 Go 中发送 HTTP 请求时,Go 运行时通常会创建两个 goroutine 来处理请求和响应。第一个 goroutine负责发送请求并等待响应。这个 goroutine 通常是在你发起 HTTP 请求时通过 http.Client.Do() 方法或者 http.NewRequest() 方法创建的。 参考 Go netpoller 原生网络模型之源码全面揭秘 网络轮询器

September 21, 2019 · 1 min · Walker

用户态和内核态

定义 内核态:cpu可以访问内存的所有数据,包括外围设备,例如硬盘,网卡,cpu也可以将自己从一个程序切换到另一个程序。 用户态:只能受限的访问内存,且不允许访问外围设备,占用cpu的能力被剥夺,cpu资源可以被其他程序获取。 为什么要有用户态和内核态? 由于需要限制不同的程序之间的访问能力, 防止他们获取别的程序的内存数据, 或者获取外围设备的数据, 并发送到网络, CPU划分出两个权限等级 – 用户态和内核态。 用户态和内核态的切换 所有用户程序都是运行在用户态的, 但是有时候程序确实需要做一些内核态的事情, 例如从硬盘读取数据, 或者从键盘获取输入等. 而唯一可以做这些事情的就是操作系统, 所以此时程序就需要先操作系统请求以程序的名义来执行这些操作. 这时需要一个这样的机制: 用户态程序切换到内核态, 但是不能控制在内核态中执行的指令 这种机制叫系统调用, 在CPU中的实现称之为陷阱指令(Trap Instruction) 并且用户模式和系统模式之间的转换是由操作系统通过系统调用或中断来控制的。 他们的工作流程如下: 用户态程序将一些数据值放在寄存器中, 或者使用参数创建一个堆栈(stack frame), 以此表明需要操作系统提供的服务. 用户态程序执行陷阱指令 CPU切换到内核态, 并跳到位于内存指定位置的指令, 这些指令是操作系统的一部分, 他们具有内存保护, 不可被用户态程序访问 这些指令称之为陷阱(trap)或者系统调用处理器(system call handler). 他们会读取程序放入内存的数据参数, 并执行程序请求的服务 系统调用完成后, 操作系统会重置CPU为用户态并返回系统调用的结果 当一个任务(进程)执行系统调用而陷入内核代码中执行时,我们就称进程处于内核运行态(或简称为内核态)。 当进程处于内核态时,执行的内核代码会使用当前进程的内核栈。每个进程都有自己的内核栈。当进程在执行用户自己的代码时,则称其处于用户运行态(用户态)。 系统调用开销大 平时说的系统调用开销大,主要是相对于函数调用来说的。 一般的,进程是不能访问内核的。它不能访问内核所占内存空间也不能调用内核函数。CPU硬件决定了这些(这就是为什么它被称作**“保护模式”**) 对于一个函数调用,汇编层面上就是一个CALL或者JMP,这种指令在硬件层面上虽然首次是会打乱流水线的,但如果是十分有规律的情况下,大多数CPU都能很好的处理。 锁开销和上下文切换开销 互斥锁的开销主要在内核态与用户态的切换 申请锁时,从用户态进入内核态,申请到后从内核态返回用户态(两次切换);没有申请到时阻塞睡眠在内核态。使用完资源后释放锁,从用户态进入内核态,唤醒阻塞等待锁的进程,返回用户态(又两次切换);被唤醒进程在内核态申请到锁,返回用户态(可能其他申请锁的进程又要阻塞)。所以,使用一次锁,包括申请,持有到释放,当前进程要进行四次用户态与内核态的切换。同时,其他竞争锁的进程在这个过程中也要进行一次切换。 进程上下文切换的直接消耗包括CPU寄存器保存和加载,需要调度时有内核调度代码的执行。 上下文切换开销 进程切换 进行进程切换就是从正在运行的进程中收回处理器,然后再使待运行进程来占用处理器。 这里所说的从某个进程收回处理器,实质上就是把进程存放在处理器 的寄存器中的中间数据找个地方存起来,从而把处理器的寄存器腾出来让其他进程使用。那么被中止运行进程的中间数据存在何处好呢?当然这个地方应该是进程的 私有堆栈。 让进程来占用处理器,实质上是把某个进程存放在私有堆栈中寄存器的数据(前一次本进程被中止时的中间数据)再恢复到处理器的寄存器中去,并把待运行进程的断点送入处理器的程序指针PC,于是待运行进程就开始被处理器运行了,也就是这个进程已经占有处理器的使用权了。 调度器进程切换的代码应有如下功能: ●保存处理器PC寄存器的值到被中止进程的私有堆栈; ●保存处理器PSW寄存器的值到被中止进程的私有堆栈; ●保存处理器SP寄存器的值到被中止进程的进程控制块; ●保存处理器其他寄存器的值到被中止进程的私有堆栈; ●自待运行进程的进程控制块取SP值并存入处理器的寄存器SP; ●自待运行进程的私有堆栈恢复处理器各寄存器的值; ●自待运行进程的私有堆栈中弹出PSW值并送入处理器的PSW; ●自待运行进程的私有堆栈中弹出PC值并送入处理器的PC。

February 21, 2019 · 1 min · Walker

0 min · Walker