Memory Barrior, 内存屏障

“Memory Barrior, 内存屏障” 屏障技术 内存屏障技术是一种屏障指令,它可以让 CPU 或者编译器在执行内存相关操作时遵循特定的约束,目前多数的现代处理器都会乱序执行指令以最大化性能,但是该技术能够保证内存操作的顺序性,在内存屏障前执行的操作一定会先于内存屏障后执行的操作6。 https://draveness.me/golang/docs/part3-runtime/ch07-memory/golang-garbage-collector/ 内存屏障 Memory Barrior 内存屏障 (Memory barrier) 为什么会有内存屏障 每个CPU都会有自己的缓存 (有的甚至L1,L2,L3) ,缓存的目的就是为了提高性能,避免每次都要向内存取。但是这样的弊端也很明显: 不能实时的和内存发生信息交换,分在不同CPU执行的不同线程对同一个变量的缓存值不同。 用volatile关键字修饰变量可以解决上述问题,那么volatile是如何做到这一点的呢?那就是内存屏障,内存屏障是硬件层的概念,不同的硬件平台实现内存屏障的手段并java通过屏蔽这些差异,统一由jvm来生成内存屏障的指令。 内存屏障是什么 硬件层的内存屏障分为两种: Load Barrier 和 Store Barrier即读屏障和写屏障。 内存屏障有两个作用: 阻止屏障两侧的指令重排序; 强制把写缓冲区/高速缓存中的脏数据等写回主内存,让缓存中相应的数据失效。 对于Load Barrier来说,在指令前插入Load Barrier,可以让高速缓存中的数据失效,强制从新从主内存加载数据; 对于Store Barrier来说,在指令后插入Store Barrier,能让写入缓存中的最新数据更新写入主内存,让其他线程可见。 java内存屏障 java的内存屏障通常所谓的四种即LoadLoad,StoreStore,LoadStore,StoreLoad实际上也是上述两种的组合,完成一系列的屏障和数据同步功能。 LoadLoad屏障: 对于这样的语句Load1; LoadLoad; Load2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。 StoreStore屏障: 对于这样的语句Store1; StoreStore; Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。 LoadStore屏障: 对于这样的语句Load1; LoadStore; Store2,在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。 StoreLoad屏障: 对于这样的语句Store1; StoreLoad; Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。它的开销是四种屏障中最大的。 volatile语义中的内存屏障 volatile的内存屏障策略非常严格保守,非常悲观且毫无安全感的心态: 在每个volatile写操作前插入StoreStore屏障,在写操作后插入StoreLoad屏障; 在每个volatile读操作前插入LoadLoad屏障,在读操作后插入LoadStore屏障; 由于内存屏障的作用,避免了volatile变量和其它指令重排序、线程之间实现了通信,使得volatile表现出了锁的特性。 final语义中的内存屏障 对于final域,编译器和CPU会遵循两个排序规则: 新建对象过程中,构造体中对final域的初始化写入和这个对象赋值给其他引用变量,这两个操作不能重排序; (废话嘛) 初次读包含final域的对象引用和读取这个final域,这两个操作不能重排序; (晦涩,意思就是先赋值引用,再调用final值) 总之上面规则的意思可以这样理解,必需保证一个对象的所有final域被写入完毕后才能引用和读取。这也是内存屏障的起的作用: 写final域: 在编译器写final域完毕,构造体结束之前,会插入一个StoreStore屏障,保证前面的对final写入对其他线程/CPU可见,并阻止重排序。 读final域: 在上述规则2中,两步操作不能重排序的机理就是在读final域前插入了LoadLoad屏障。 X86处理器中,由于CPU不会对写-写操作进行重排序,所以StoreStore屏障会被省略;而X86也不会对逻辑上有先后依赖关系的操作进行重排序,所以LoadLoad也会变省略。 ...

2021-07-09 · 2 min · 248 words · -

28

“28” 0%的汽车狂人,制造了80%以上的交通事故;世界上不足20%的富人拥有80%以上的财富;企业中80%的销售额是由20%的产品或客户贡献的。 这些现象暗合帕累托法则,即在任何一组事物中,最重要的只占其中一小部分,约20%,其余80%尽管是多数,却是次要的,这一法则又被称为二八定律或20/80定律。 二八定律可以解决企业管理方面的许多问题: 如资源分配,核心产品,关键人才,核心利润,财富分配等。若公司80%的销售额由20%产品贡献,那么就应该将更多的资源投入到这少数的20%产品上。 二八定律不仅在经济学、企业管理领域广泛应用,它对我们的自身发展也具有重要的现实意义: 要避免将时间和精力花费在琐事上,学会抓主要矛盾。 一个人的时间和精力都是非常有限的,要想真正“做好每一件事情”几乎是不可能的,应该学会合理分配时间和精力。想面面俱到还不如重点突破,把80%的资源花在能出关键效益的20%的方面,这20%的方面又能带动其余80%的发展。 https://zhuanlan.zhihu.com/p/28480261

2021-07-04 · 1 min · 9 words · -

热数据, 温数据, 冷数据

“热数据, 温数据, 冷数据” 热数据 热数据是需要被计算节点频繁访问的在线类数据,比如可以是半年以内的数据,用户经常会查询它们,适合放在数据库中存储,比如MySQL、MongoDB和HBase。 温数据 温数据是非即时的状态和行为数据,也可以简单理解为把热数据和冷数据混在一起就成了温数据。如果整体数据量不大,也可以不区分温数据和热数据。 冷数据 冷数据是指离线类不经常访问的数据,用于灾难恢复的备份或者因为要遵守法律规定必须保留一段时间,比如企业备份数据、业务与操作日志数据、话单与统计数据。通常会存储在性能较低、价格较便宜的文件系统里,适用于离线分析,比如机器学习中的模型训练或者大数据分析。

2021-07-04 · 1 min · 9 words · -

race condition, 竞态条件

race condition 数据争用 (data race) 和竞态条件 (race condition) 在有关多线程编程的话题中,数据争用 (data race) 和竞态条件 (race condition) 是两个经常被提及的名词,它们两个有着相似的名字,也是我们在并行编程中极力避免出现的。但在处理实际问题时,我们应该能明确区分它们两个。 数据争用 (data race) 定义: 多个线程对于同一个变量、同时地、进行读/写操作的现象并且至少有一个线程进行写操作。 (也就是说,如果所有线程都是只进行读操作,那么将不构成数据争用) 后果: 如果发生了数据争用,读取该变量时得到的值将变得不可知,使得该多线程程序的运行结果将完全不可预测,可能直接崩溃。 如何防止: 对于有可能被多个线程同时访问的变量使用排他访问控制,具体方法包括使用mutex (互斥量) 和monitor (监视器) ,或者使用atomic变量。 竞态条件 (race condition) 竞态条件(race conditions),多个线程以非一致性的顺序同时访问数据资源 相对于数据争用,竞态条件(race condition) 指的是更加高层次的更加复杂的现象,一般需要在设计并行程序时进行细致入微的分析,才能确定。 (也就是隐藏得更深) 定义: 受各线程上代码执行的顺序和时机的影响,程序的运行结果产生 (预料之外) 的变化。 后果: 如果存在竞态条件(race condition),多次运行程序对于同一个输入将会有不同的结果,但结果并非完全不可预测,它将由输入数据和各线程的执行顺序共同决定。 如何预防: 竞态条件产生的原因很多是对于同一个资源的一系列连续操作并不是原子性的,也就是说有可能在执行的中途被其他线程抢占,同时这个“其他线程”刚好也要访问这个资源。解决方法通常是: 将这一系列操作作为一个 critical section (临界区) 。 代码示例 下面以C++实现的一个银行存款转账操作为例,说明数据争用(data race) 和竞态条件(race condition)的区别。 该系统的不変性条件: 存款余额≥0,不允许借款。 3.1.数据争用的例子 int my_account = 0; //我的账户余额 int your_account = 100; //你的账户余额 // 转账操作: 存在数据争用(data race)! bool racy_transfer(int& src, int& dst, int m) { if (m <= src) { //操作结果不可预测 src -= m; //操作结果不可预测 dst += m; //操作结果不可预测 return true; } else { return false; } } // 将下面两个函数在两个线程分别运行 racy_transfer(your_account, my_account, 50); racy_transfer(your_account, my_account, 80); 运行上面的的代码后,不光我们双方账号的余额不可预测,甚至整个系统会发生什么事情都无法保证。 ...

2021-07-01 · 2 min · 275 words · -

dpdk

“dpdk” dpdk Intel DPDK全称Intel Data Plane Development Kit,是intel提供的数据平面开发工具集,为Intel architecture (IA) 处理器架构下用户空间高效的数据包处理提供库函数和驱动的支持,它不同于Linux系统以通用性设计为目的,而是专注于网络应用中数据包的高性能处理。DPDK应用程序是运行在用户空间上利用自身提供的数据平面库来收发数据包,绕过了Linux内核协议栈对数据包处理过程。Linux内核将DPDK应用程序看作是一个普通的用户态进程,包括它的编译、连接和加载方式和普通程序没有什么两样。DPDK程序启动后只能有一个主线程,然后创建一些子线程并绑定到指定CPU核心上运行。 https://cloud.tencent.com/developer/article/1198333 https://tonydeng.github.io/sdn-handbook/dpdk/ https://github.com/google/cpu_features

2021-06-30 · 1 min · 14 words · -

win11

“win11” https://github.com/rcmaehl/WhyNotWin11

2021-06-29 · 1 min · 2 words · -

UDP

“UDP” UDP UDP 协议全称是用户数据报协议,在网络中它与 TCP 协议一样用于处理数据包,是一种无连接的协议.在 OSI 中,第四层传输层,处于 IP 协议的上一层 UDP 有不提供数据包分组,组装和不能对数据包进行排序的缺点,也就是说,当报文发送后,无法监控其是否完整安全到达的,就想一个坏掉了的水龙头,你不论怎么让他停止他都只会输出,也不管你的桶满没满,就像爱一样 文章相关视频讲解: C/C++ Linux 服务器开发高级架构师学习视频点击: C/C++Linux服务器开发高级架构/Linux后台架构师-学习视频 底层原理到徒手实现 TCP/IP网络协议栈 tcpip协议栈与网络API的关联,udp的并发性比tcp强? 2|1 特点 面向无连接: 首先 UDP 是不需要和 TCP 一样在发送数据前进行三次握手建立连接的,想发数据就可以开始发送了.并且也只是主句报文的搬运工,不会对数据报文进行任何拆分和拼接操作 具体来说: 在发送端,应用层将数据传递给 UDP 协议,UDP 只会给数据增加一个 UDP 头标识下 UDP,然后就传递给网络层了 在接收端,网络层将数据传递给传输层,UDP 只去除 IP 报头就传递给应用层,不会任何拼接操作 有单播,多播,广播的功能 UDP 不止支持一对一的传输方式,同样支持一对多,多对多,多对一的方式,也就是说 UDP 提供了单播,多播和广播的功能. UDP 是面向报文的 发送方的 UDP 读应用程序交下来的报文,在添加首部后就向下交付 IP 层.UDP 对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界.因此,应用程序必须选择合适大小的报文 不可靠性 首先不可靠性体现在无连接上,通信不需要建立连接,想发就发,这样的情况肯定不可靠 并且受到什么数据就传递什么数据,并且也不会备份数据,发送数据也不会关心对方是否已经正确接收到数据了 再者网络环境时好时坏,但是 UDP 因为没有拥塞控制,一直会以恒定的速度发送数据,即使网络条件不好,也不会对发送数据进行调整.这样实现的弊端就是在网络条件不好的情况下会导致丢包,但是有点也很明显,比如电话会议等等最好就是 UDP 头部开销小,传输数据报文是很高效的. 因此 UDP 的头部开销小,只有八字节,相比 TCP 的至少二十字节要少得多,在传输数据报文时是很高效的 Linux网络编程-UDP和TCP协议详解 原文链接: https://xie.infoq.cn/article/760f379a3e3f2694b5e994ffd?utm_source=rss&utm_medium=article

2021-06-22 · 1 min · 69 words · W10N

Ruckus R310, 优科

“Ruckus R310, 优科” 默认用户名密码 super/sp-admin 软件升级 [https://support.ruckuswireless.com/software?filter=88#sort=relevancy&f:@source=Software%20Downloads]&f:@commonproducts=[R310] https://support.ruckuswireless.com/software/2791-ruckus-solo-access-point-110-0-0-0-2014-ga-refresh2-software-release-r310 Ruckus Solo Access Point 110.0.0.0.2014 (GA Refresh2) Software Release (R310) 操作前的准备 认识硬件 LED状态灯 我们拿一台 Ruckus 2942 AP 来讲解。这个型号已经停产,而且是单频 2.4GHz AP,但是 LED 灯的状态和含义都是相同的。真正的原因是我不想重新做图,懒。 OPT- 没用,永远不亮。有些新的型号已经取消了OPT灯 WLAN- 现在基本上都是双频AP,所以不再有WLAN灯,取而代之的是2.4G和5G灯。有以下几种状态 (胖AP只会出现前3种) : 灭- 表示WLAN服务没有启用 (默认情况下WLAN服务是关闭的) 橙色长亮- 表示WLAN服务已经开启,但是没有客户端连接到Radio 绿色长亮- 表示WLAN服务已经开启,并且已经有客户端连接到Radio 绿色慢闪- 表示AP是Mesh AP,WLAN服务已经开启,但是没有客户端连接到Radio 绿色快闪- 表示AP是Mesh AP,WLAN服务已经开启,并且已经有客户端连接到Radio DIR- 控制器管理状态灯,有以下几种状态: 灭- AP是胖AP,没有被控制器管理 绿色长亮- AP是瘦AP,并且已经和控制器同步 绿色快闪 AP是瘦AP,正在和控制器同步,或者正在更新固件 绿色慢闪 AP是瘦AP,正在寻找控制器 (如果一直处于这种状态, 就要检查网络连通性) AIR- Mesh上行链路状态灯,有些型号的双频AP没有AIR灯,用5G灯来充当AIR灯的功能: 灭- AP是Root AP,或者Mesh被禁用 绿色长亮- Mesh上行链路良好 绿色快闪- Mesh上行链路不佳 (此时需要检查干扰情况,或者调整AP的位置和间距) 绿色慢闪- Mesh上行链路不存在,AP没有找到Root AP 红色 AP硬件损坏 NOTE1: Mesh技术介绍 http://baike.baidu.com/view/1215700.htm NOTE2: AP都会有一个Reset按键,是一个凹陷的孔。先把AP加电,2-3秒钟之后,用曲别针之类的东西按住Reset键 6 秒钟再松手,AP就可以恢复出厂设置。为了避免Reset失败,最好按8秒再松手。 NOTE3: 有些型号的AP还会有一个soft reset,作用是重启AP。 ...

2021-06-20 · 2 min · 335 words · -

Redis Replication, sentinel

“Redis Replication, sentinel” Redis 主从 Replication 的配置 beanlam 发布于 2015-04-20 本专栏与Redis相关的文章 Redis Sentinel机制与用法 (一) Redis Sentinel机制与用法 (二) Jedis的JedisSentinelPool源代码分析 Jedis的Sharded源代码分析 Redis 主从 Replication 的配置 详解Redis SORT命令 JedisCommand接口说明 本文参考翻译自《Redis Replication documentation》 概述 Redis的replication机制允许slave从master那里通过网络传输拷贝到完整的数据备份。具有以下特点: 异步复制。从2.8版本开始,slave能不时地从master那里获取到数据。 允许单个master配置多个slave slave允许其它slave连接到自己。一个slave除了可以连接master外,它还可以连接其它的slave。形成一个图状的架构。 master在进行replication时是非阻塞的,这意味着在replication期间,master依然能够处理客户端的请求。 slave在replication期间也是非阻塞的,也可以接受来自客户端的请求,但是它用的是之前的旧数据。可以通过配置来决定slave是否在进行replication时用旧数据响应客户端的请求,如果配置为否,那么slave将会返回一个错误消息给客户端。不过当新的数据接收完全后,必须将新数据与旧数据替换,即删除旧数据,在替换数据的这个时间窗口内,slave将会拒绝客户端的请求和连接。 一般使用replication来可以实现扩展性,例如说,可以将多个slave配置为“只读”,或者是纯粹的数据冗余备份。 能够通过replication来避免master每次持久化时都将整个数据集持久化到硬盘中。只需把master配置为不进行save操作(把配置文件中save相关的配置项注释掉即可),然后连接上一个slave,这个slave则被配置为不时地进行save操作的。不过需要注意的是,在这个用例中,必须确保master不会自动启动。更多详情请继续往下读。 Master持久化功能关闭时Replication的安全性 当有需要使用到replication机制时,一般都会强烈建议把master的持久化开关打开。即使为了避免持久化带来的延迟影响,不把持久化开关打开,那么也应该把master配置为不会自动启动的。 为了更好地理解当一个不进行持久化的master如果允许自动启动所带来的危险性。可以看看下面这种失败情形: 假设我们有一个redis节点A,设置为master,并且关闭持久化功能,另外两个节点B和C是它的slave,并从A复制数据。 如果A节点崩溃了导致所有的数据都丢失了,它会有重启系统来重启进程。但是由于持久化功能被关闭了,所以即使它重启了,它的数据集是空的。 而B和C依然会通过replication机制从A复制数据,所以B和C会从A那里复制到一份空的数据集,并用这份空的数据集将自己本身的非空的数据集替换掉。于是就相当于丢失了所有的数据。 即使使用一些HA工具,比如说sentinel来监控master-slaves集群,也会发生上述的情形,因为master可能崩溃后迅速恢复。速度太快而导致sentinel无法察觉到一个failure的发生。 当数据的安全很重要、持久化开关被关闭并且有replication发生的时候,那么应该禁止实例的自启动。 replication工作原理 如果你为master配置了一个slave,不管这个slave是否是第一次连接上Master,它都会发送一个SYNC命令给master请求复制数据。 master收到SYNC命令后,会在后台进行数据持久化,持久化期间,master会继续接收客户端的请求,它会把这些可能修改数据集的请求缓存在内存中。当持久化进行完毕以后,master会把这份数据集发送给slave,slave会把接收到的数据进行持久化,然后再加载到内存中。然后,master再将之前缓存在内存中的命令发送给slave。 当master与slave之间的连接由于某些原因而断开时,slave能够自动重连Master,如果master收到了多个slave并发连接请求,它只会进行一次持久化,而不是一个连接一次,然后再把这一份持久化的数据发送给多个并发连接的slave。 当master和slave断开重连后,一般都会对整份数据进行复制。但从redis2.8版本开始,支持部分复制。 数据部分复制 从2.8版本开始,slave与master能够在网络连接断开重连后只进行部分数据复制。 master会在其内存中创建一个复制流的等待队列,master和它所有的slave都维护了复制的数据下标和master的进程id,因此,当网络连接断开后,slave会请求master继续进行未完成的复制,从所记录的数据下标开始。如果进程id变化了,或者数据下标不可用,那么将会进行一次全部数据的复制。 支持部分数据复制的命令是PSYNC 不需硬盘参与的Replication 一般情况下,一次复制需要将内存的数据写到硬盘中,再将数据从硬盘读进内存,再发送给slave。 对于速度比较慢的硬盘,这个操作会给master带来性能上的损失。Redis2.8版本开始,实验性地加上了无硬盘复制的功能。这个功能能将数据从内存中直接发送到slave,而不用经过硬盘的存储。 不过这个功能目前处于实验阶段,还未正式发布。 相关配置 与replication相关的配置比较简单,只需要把下面一行加到slave的配置文件中: slaveof 192.168.1.1 6379 你只需要把ip地址和端口号改一下。当然,你也可以通过客户端发送SLAVEOF命令给slave。 部分数据复制有一些可调的配置参数,请参考redis.conf文件。 无硬盘复制功能可以通过repl-diskless-sync来配置,另外一个配置项repl-diskless-sync-delay用来配置当收到第一个请求时,等待多个slave一起来请求之间的间隔时间。 只读的slave 从redis2.6版本开始,slave支持只读模式,而且是默认的。可以通过配置项slave-read-only来进行配置,并且支持客户端使用CONFIG SET命令来动态修改配置。 ...

2021-06-18 · 1 min · 111 words · -

macos apps

macos apps CleanMyMac X Bob, 词典 Monosnap, 截图 Stats, https://github.com/exelban/stats, 监控 Itsycal, 在任务栏显示日历周 RealVNC® Viewer golang iterm2, another terminal wechat brew brew 是 Mac 下的一个包管理工具, 作用类似于 centos 下的 yum # brew install package_0 nodejs obsidian go fping other Tunnelblick, openvpn gui client Sketch, 矢量绘图

2021-06-16 · 1 min · 44 words · -

valgrind

“valgrind” Valgrind是用于构建动态分析工具的探测框架。它包括一个工具集,每个工具执行某种类型的调试、分析或类似的任务,以帮助完善你的程序。Valgrind的架构是模块化的,所以可以容易地创建新的工具而又不会扰乱现有的结构。 Valgrind中许多有用的工具被作为标准而提供。 Memcheck是一个内存错误检测器。它有助于使你的程序,尤其是那些用C和C++写的程序,更加准确。 Cachegrind是一个缓存和分支预测分析器。它有助于使你的程序运行更快。 Callgrind是一个调用图缓存生成分析器。它与Cachegrind的功能有重叠,但也收集Cachegrind不收集的一些信息。 Helgrind是一个线程错误检测器。它有助于使你的多线程程序更加准确。 DRD也是一个线程错误检测器。它和Helgrind相似,但使用不同的分析技术,所以可能找到不同的问题。 Massif是一个堆分析器。它有助于使你的程序使用更少的内存。 DHAT是另一种不同的堆分析器。它有助于理解块的生命期、块的使用和布局的低效等问题。 SGcheck是一个实验工具,用来检测堆和全局数组的溢出。它的功能和Memcheck互补: SGcheck找到Memcheck无法找到的问题,反之亦然。 BBV是个实验性质的SimPoint基本块矢量生成器。它对于进行计算机架构的研究和开发很有用处。 也有一些对大多数用户没有用的小工具: Lackey是演示仪器基础的示例工具;Nulgrind是一个最小化的Valgrind工具,不做分析或者操作,仅用于测试目的。 https://yuanfentiank789.github.io/2018/11/01/%E7%94%A8Valgrind%E6%A3%80%E6%B5%8B%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F/

2021-06-15 · 1 min · 16 words · -

进程状态

“进程状态” 进程的状态 linux (本文使用linux4.8.4) 下,进程状态大致有7种。 进程状态 说明 TASK_RUNNING 可运行状态。未必正在使用CPU,也许是在等待调度 TASK_INTERRUPTIBLE 可中断的睡眠状态。正在等待某个条件满足 TASK_UNINTERRUPTIBLE 不可中断的睡眠状态。不会被信号中断 __TASK_STOPPED 暂停状态。收到某种信号,运行被停止 __TASK_TRACED 被跟踪状态。进程停止,被另一个进程跟踪 EXIT_ZOMBIE 僵尸状态。进程已经退出,但尚未被父进程或者init进程收尸 EXIT_DEAD 真正的死亡状态 在include/linux/sched.h中,进程状态的定义并没有那么少: /* Task state bitmask. NOTE! These bits are also encoded in fs/proc/array.c: get_task_state(). We have two separate sets of flags: task->state is about runnability, while task->exit_state are about the task exiting. Confusing, but this way modifying one set can’t modify the other one by mistake. / #define TASK_RUNNING 0 #define TASK_INTERRUPTIBLE 1 #define TASK_UNINTERRUPTIBLE 2 #define __TASK_STOPPED 4 #define __TASK_TRACED 8 / in tsk->exit_state / #define EXIT_DEAD 16 #define EXIT_ZOMBIE 32 #define EXIT_TRACE (EXIT_ZOMBIE | EXIT_DEAD) / in tsk->state again */ #define TASK_DEAD 64 #define TASK_WAKEKILL 128 #define TASK_WAKING 256 #define TASK_PARKED 512 #define TASK_NOLOAD 1024 #define TASK_NEW 2048 #define TASK_STATE_MAX 4096 #define TASK_STATE_TO_CHAR_STR “RSDTtXZxKWPNn” ...

2021-06-14 · 5 min · 880 words · -

chrome 为什么多进程而不是多线程

chrome 为什么多进程而不是多线程 https://www.zhihu.com/question/368712837 多进程有四点好处。1,chromium项目创建初期,webkit不属于谷歌。他们对苹果的东西不信任,而且各种页面渲染时候的崩溃也很大。那时候webkit在chromium里的地位就是个小小第三方库。所以需要把渲染放到另外个进程防止崩溃了影响主进程。2,同样的,webkit那时候很多内存泄露。多进程能很大程度避免。一个进程关了,所有内存就回收了。当时谷歌还写文章鄙视了下那些说多进程占用内存多的人。3,多进程安全性更好。如果blink被发现什么提权漏洞,例如写一段js就能控制整个chromium进程做任何事情,显然多进程可以把损失限制在渲染线程。渲染线程拿不到主进程的各种私密信息,例如别的域名下的密码4,另外有个点大家没说的地方就是,webkit内部很多全局变量。如果要做到一个页面一个线程,理论上很难搞。谷歌其实考虑过想搞一个单进程多线程模式,后来发现不好搞就放弃了。。这个模式在移动平台还是有优势的。以前的手机性能和内存还很差。多进程很消耗内存。chromium刚移植到安卓上时,还是30几版本。性能和稳定性远不如webkit单进程。那时候安卓版chromium就是单进程模式。 作者: 龙泉寺扫地僧 链接: https://www.zhihu.com/question/368712837/answer/994040540 来源: 知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 Chromium里有三种进程——浏览器、渲染器和插件。浏览器进程只有一个,管理窗口和tab,也处理所有的与磁盘,网络,用户输入和显示的工作。这就是我们看到的“Chrome界面”。渲染器开多个。每个渲染器负责处理HTML、CSS、js、图片等,将其转换成用户可见的数据。当时Chrome使用开源的webkit实现这个功能。顺便说一句,webkit是由Apple开发的,当时有很多坑,也被长期吐槽;现在Chrome已经转成使用自家的Blink引擎了。插件会开很多。每个类型的插件在第一次使用时会启动一个相应的进程。 作者: 大宽宽 链接: https://www.zhihu.com/question/368712837/answer/999401453 来源: 知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 Blink不再是WebKit: http://www.chromium.org/blink BLINK内核就是谷歌公司,针对WEBKIT内核,做的修订和精简。 去掉了几十万行的没用的复杂代码,让效率更高。然后针对未来的网页格式,做了进一步优化,和效率提升的处理。 所以BLINK内核可以看成是WEBKIT的精简高效强化版。 https://blog.chromium.org/2008/09/multi-process-architecture.html

2021-06-09 · 1 min · 25 words · -

寄存器, Register

“寄存器, Register” 寄存器 自1946年冯·诺伊曼领导下诞生的世界上第一台通用电子计算机ENIAC至今,计算机技术已经发展了七十多载。 从当初专用于数学计算的庞然大物,到后来大型机服务器时代,从个人微机技术蓬勃发展,到互联网浪潮席卷全球,再到移动互联网、云计算日新月异的当下,计算机变的形态各异,无处不在。 这七十多年中,出现了数不清的编程语言,通过这些编程语言,又开发了无数的应用程序。 可无论什么样的应用程序,什么样的编程语言,最终的程序逻辑都是要交付给CPU去执行实现的 (当然这里有些不严谨,除了CPU,还有协处理器、GPU等等) 。所以了解和学习CPU的原理都是对计算机基础知识的夯实大有裨益。 在七十多年的漫长历程中,也涌现了不少架构的CPU。 MIPS PowerPC x86/x64 IA64 ARM 这篇文章就以市场应用最为广泛的x86-x64架构为目标,通过学习了解它内部的100个寄存器功能作用,来串联阐述CPU底层工作原理。 通过这篇文章,你将了解到: CPU指令执行原理 内存寻址技术 软件调试技术原理 中断与异常处理 系统调用 CPU多任务技术 什么是寄存器 寄存器是CPU内部用来存放数据的一些小型存储区域,用来暂时存放参与运算的数据和运算结果以及一些CPU运行需要的信息。 x86架构CPU走的是复杂指令集 (CISC) 路线,提供了丰富的指令来实现强大的功能,与此同时也提供了大量寄存器来辅助功能实现。这篇文章将覆盖下面这些寄存器: 通用寄存器 标志寄存器 指令寄存器 段寄存器 控制寄存器 调试寄存器 描述符寄存器 任务寄存器 MSR寄存器 通用寄存器 首当其冲的是通用寄存器,这些的寄存器是程序执行代码最最常用,也最最基础的寄存器,程序执行过程中,绝大部分时间都是在操作这些寄存器来实现指令功能。 所谓通用,即这些寄存器CPU没有特殊的用途,交给应用程序"随意"使用。注意,这个随意,我打了引号,对于有些寄存器,CPU有一些潜规则,用的时候要注意。 eax: 通常用来执行加法,函数调用的返回值一般也放在这里面 ebx: 数据存取 ecx: 通常用来作为计数器,比如for循环 edx: 读写I/O端口时,edx用来存放端口号 esp: 栈顶指针,指向栈的顶部, Stack Pointer, SP, 堆栈指针 ebp: 栈底指针,指向栈的底部,通常用ebp+偏移量的形式来定位函数存放在栈中的局部变量 esi: 字符串操作时,用于存放数据源的地址 edi: 字符串操作时,用于存放目的地址的,和esi两个经常搭配一起使用,执行字符串的复制等操作 在x64架构中,上面的通用寄存器都扩展成为64位版本,名字也进行了升级。当然,为了兼容32位模式程序,使用上面的名字仍然是可以访问的,相当于访问64位寄存器的低32位。 rax rbx rcx rdx rsp rbp rsi rdi 除了扩展原来存在的通用寄存器,x64架构还引入了8个新的通用寄存器: ...

2021-05-08 · 2 min · 282 words · -

fork vfork clone pthread_create

“fork vfork clone pthread_create” Linux通过clone系统调用实现fork.调用通过一系列的参数标志来指明父、子进程需要共享的资源。fork、vfork、和__clone的库函数都根据各自需要的参数标志去调用clone,然后由clone()去调用do_fork()。 arch(X86)架构的是: fork、vfork、和__clone的库函数最终调用的都是clone系统调用。 至于其它的架构的,可能是通过fork和vfork系统调用。这和本身的实现有关。当然在现在的大多数Linux内核中,就算调用的是fork,在底层基本上传递给do_fork的参数都带有能实现写时复制的一些标志。 fork和 pthread_create,然后利用strace跟踪两者的调用过程, 都是调用的clone。 在Linux中主要提供了fork、vfork、clone三个进程创建方法。 在linux源码中这三个调用的执行过程是执行fork(),vfork(),clone()时,通过一个系统调用表映射到sys_fork(),sys_vfork(),sys_clone(),再在这三个函数中去调用do_fork()去做具体的创建进程工作。 fork fork创建一个进程时,子进程只是完全复制父进程的资源,复制出来的子进程有自己的task_struct结构和pid,但却复制父进程其它所有的资源。例如,要是父进程打开了五个文件,那么子进程也有五个打开的文件,而且这些文件的当前读写指针也停在相同的地方。所以,这一步所做的是复制。这样得到的子进程独立于父进程, 具有良好的并发性,但是二者之间的通讯需要通过专门的通讯机制,如: pipe,共享内存等机制, 另外通过fork创建子进程,需要将上面描述的每种资源都复制一个副本。这样看来,fork是一个开销十分大的系统调用,这些开销并不是所有的情况下都是必须的,比如某进程fork出一个子进程后,其子进程仅仅是为了调用exec执行另一个可执行文件,那么在fork过程中对于虚存空间的复制将是一个多余的过程。但由于现在Linux中是采取了copy-on-write(COW写时复制)技术,为了降低开销,fork最初并不会真的产生两个不同的拷贝,因为在那个时候,大量的数据其实完全是一样的。写时复制是在推迟真正的数据拷贝。若后来确实发生了写入,那意味着parent和child的数据不一致了,于是产生复制动作,每个进程拿到属于自己的那一份,这样就可以降低系统调用的开销。所以有了写时复制后呢,vfork其实现意义就不大了。 fork()调用执行一次返回两个值,对于父进程,fork函数返回子程序的进程号,而对于子程序,fork函数则返回零,这就是一个函数返回两次的本质。 在fork之后,子进程和父进程都会继续执行fork调用之后的指令。子进程是父进程的副本。它将获得父进程的数据空间,堆和栈的副本,这些都是副本,父子进程并不共享这部分的内存。也就是说,子进程对父进程中的同名变量进行修改并不会影响其在父进程中的值。但是父子进程又共享一些东西,简单说来就是程序的正文段。正文段存放着由cpu执行的机器指令,通常是read-only的。下面是一个验证的例子: //例1: fork.c #include<stdio.h> #include<sys/types.h> #include<unistd.h> #include<errno.h> int main() { int a = 5; int b = 2; pid_t pid; pid = fork(); if(pid == 0) { a = a-4; printf("I'm a child process with PID [%d],the value of a: %d,the value of b:%d.\n",pid,a,b); }else if(pid < 0) { perror("fork"); }else { printf("I'm a parent process, with PID [%d], the value of a: %d, the value of b:%d.\n", pid, a, b); } return 0; } #gcc –o fork fork.c #./fork //运行结果: I’m a child process with PID[0],the value of a:1,the value of b:2. I’m a parent process with PID[19824],the value of a:5,the value of b:2. //例1: fork.c #include<stdio.h> #include<sys/types.h> #include<unistd.h> #include<errno.h> int main() { int a = 5; int b = 2; pid_t pid; pid = fork(); if(pid == 0) { a = a-4; printf("I'm a child process with PID [%d],the value of a: %d,the value of b:%d.\n",pid,a,b); }else if(pid < 0) { perror("fork"); }else { printf("I'm a parent process, with PID [%d], the value of a: %d, the value of b:%d.\n", pid, a, b); } return 0; } #gcc –o fork fork.c #./fork ...

2021-05-06 · 12 min · 2459 words · -

workqueue

“workqueue” 转自: http://bgutech.blog.163.com/blog/static/18261124320116181119889/ 什么是workqueue Linux中的Workqueue机制就是为了简化内核线程的创建。通过调用workqueue的接口就能创建内核线程。并且可以根据当前系统CPU的个 数创建线程的数量,使得线程处理的事务能够并行化。workqueue是内核中实现简单而有效的机制,他显然简化了内核daemon的创建,方便了用户的 编程. 工作队列 (workqueue) 是另外一种将工作推后执行的形式.工作队列可以把工作推后,交由一个内核线程去执行,也就是说,这个下半部分可以在进程上下文中执行。最重要的就是工作队列允许被重新调度甚至是睡眠。 那么,什么情况下使用工作队列,什么情况下使用tasklet。如果推后执行的任务需要睡眠,那么就选择工作队列。如果推后执行的任务不需要睡眠,那么就 选择tasklet。另外,如果需要用一个可以重新调度的实体来执行你的下半部处理,也应该使用工作队列。它是唯一能在进程上下文运行的下半部实现的机 制,也只有它才可以睡眠。这意味着在需要获得大量的内存时、在需要获取信号量时,在需要执行阻塞式的I/O操作时,它都会非常有用。如果不需要用一个内核 线程来推后执行工作,那么就考虑使用tasklet。

2021-05-05 · 1 min · 14 words · -

上下文切换

“上下文切换” 操作系统上线程的切换也不是免费的,线程切换其实会带来额外的开销,其中包括: 保存线程 1 的执行上下文; 加载线程 2 的执行上下文; 频繁的对线程的上下文进行切换可能还会导致性能地急剧下降,这可能会导致我们不仅没有提升请求处理的平均速度,反而进行了负优化,所以这也是为什么 Redis 对于使用多线程技术非常谨慎。 我们都知道,Linux 是一个多任务操作系统,它支持远大于 CPU 数量的任务同时运行。当然,这些任务实际上并不是真的在同时运行,而是因为系统在很短的时间内,将 CPU 轮流分配给它们,造成多任务同时运行的错觉。 而在每个任务运行前,CPU 都需要知道任务从哪里加载、又从哪里开始运行,也就是说,需要系统事先帮它设置好CPU 寄存器和程序计数器 什么是 CPU 上下文 CPU 寄存器和程序计数器就是 CPU 上下文,因为它们都是 CPU 在运行任何任务前,必须的依赖环境。 CPU 寄存器是 CPU 内置的容量小、但速度极快的内存。 程序计数器则是用来存储 CPU 正在执行的指令位置、或者即将执行的下一条指令位置。 什么是 CPU 上下文切换 就是先把前一个任务的 CPU 上下文 (也就是 CPU 寄存器和程序计数器) 保存起来,然后加载新任务的上下文到这些寄存器和程序计数器,最后再跳转到程序计数器所指的新位置,运行新任务。 而这些保存下来的上下文,会存储在系统内核中,并在任务重新调度执行时再次加载进来。这样就能保证任务原来的状态不受影响,让任务看起来还是连续运行。 CPU 上下文切换的类型 根据任务的不同,可以分为以下三种类型 - 进程上下文切换 - 线程上下文切换 - 中断上下文切换 进程上下文切换 Linux 按照特权等级,把进程的运行空间分为内核空间和用户空间,分别对应着下图中, CPU 特权等级的 Ring 0 和 Ring 3。 内核空间 (Ring 0) 具有最高权限,可以直接访问所有资源; 用户空间 (Ring 3) 只能访问受限资源,不能直接访问内存等硬件设备,必须通过系统调用陷入到内核中,才能访问这些特权资源。 ...

2021-05-05 · 1 min · 178 words · -

neofetch, linux logo ascii

“neofetch, linux logo ascii” neofetch, linux logo ascii neofetch 是一个跨平台的易于使用的 系统信息显示命令行脚本,它收集你的系统信息,并在终端中和图像一起显示出来,这个图像可能是你的发行版的 logo 也可能是你选择的一幅 ascii 艺术字。 Neofetch 和 ScreenFetch 或者 Linux_Logo 很像,但是它可以高度定制,并且还有一些额外的我们要在下面讨论的特点。 它的主要特点有: 运行速度快,可以显示全色图像 —— 用 ASCII 字符显示的发行版 logo ,旁边显示系统信息,可以高度定制,可以随时随地显示系统信息,并且在脚本结束的时候还可以通过一个特殊的参数来启用桌面截图。 系统要求: Bash 3.0+ 带 ncurses 支持。 w3m-img (有时候会打包成 w3m) 或者 iTerm2 或者 Terminology,用于显示图像。 imagemagick,用于创建缩略图。 支持 [\033[14t 的 Linux 终端模拟器 或者 xdotool 或者 xwininfo + xprop 或者 xwininfo + xdpyinfo 。 Linux 系统中还需要 feh、nitrogen 或者 gsettings 来提供对墙纸的支持。 注意: 你可以从 Neofetch 的 Github 页面了解更多关于可选依赖的信息,以检查你的 Linux 终端模拟器 是不是真的支持 \033[14t 或者是否需要一些额外的依赖来使这个脚本在你的发行版上工作得更好。 ...

2021-04-30 · 1 min · 166 words · -

usbip

“usbip” 下载 usbip-win https://github.com/cezanne/usbip-win/releases 解压到一个目录 D:\workspace\apps\usbip-win-0.3.4 安装证书 右键usbip_test.pfx -> 安装PFX,选择"本地计算机",而不是"当前用户",证书密码 usbip,存储位置选择 “受信任的证书颁发机构” 开启驱动测试签名 bcdedit.exe /set TESTSIGNING ON 重启系统 找到要使用的USB设备 .\usbip.exe list -l 安装USB驱动 .\usbip.exe install 启动服务端 .\usbipd.exe -d -4 编译内核 WSL2 客户端 安装工具包 sudo apt install build-essential flex bison libssl-dev libelf-dev 下载内核 https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/ https://yadom.in/archives/usb-passthrough-hyper-v-and-wsl2.html https://snowstar.org/2020/06/14/wsl2-usb-via-usbip/ https://github.com/microsoft/WSL2-Linux-Kernel

2021-04-29 · 1 min · 46 words · -

调度策略

“调度策略” 为什么会发生调度? 因为cpu是有限的,而操作系统上的进程很多,所以操作系统需要平衡各个进程的运行时间 比如说有的进程运行时间已经很长了,已经占用了cpu很长时间了,这个时候操作系统要公平 就会换下一个需要运行的进程。 调度策略 抢占 vs 协作 从调度机制上来讲,调度可以分为抢占式和协作式。 非抢占, 协作 非抢占方式是指一旦将调度资源 (如 CPU) 分配给某任务后,便让该任务一直执行,直到该任务完成或阻塞或主动让出CPU控制权,非抢占调度又称为协作式调度,它实现简单,并且对共享资源的访问也更安全(如允许使用不可重入函数)。非抢占算法常见的如 FIFO,STCF(Short time to complete first)等。 抢占 抢占方式则允许调度程序根据某种规则,剥夺当前进程的调度资源,将其分配给其它进程。常见的抢占策略有: 基于时间片 基于时间片: 给每个进程分配时间片,当时间片用完后,则停止该进程重新调度下一个进程,这种均分CPU 的算法,又叫轮转调度算法 基于优先级 基于优先级: 给每个进程分配一个优先级,一旦出现一个优先级更高的进程,则停止当前进程并切换到该高优先级进程,这种调度算法又叫优先级抢占 轮转调度和优先级抢占结合: 即相同优先级的进程使用轮转调度,如果遇到更高优先级的进程,则可抢占CPU。现代 OS 如 Linux 通常都使用这种混合调度策略。 PS: 基于优先级抢占容易出现优先级反转的问题: 优先级低的任务持有一个被优先级高的任务所需要的共享资源,这种情况下,优先级低的任务有资源而得不到CPU,优先级高的资源有CPU而得不到资源,从而阻塞(导致其它中优先级的任务获得执行)或者忙等(可能永远无法获得资源)。 解决优先级反转的方案: 给临界区一个高优先级,所有进入该临界区的任务将获得该高优先级,避免其被随意抢占 当高优先级任务在等待低优先级进程持有资源时,低优先级进程将暂时获得高优先级进程的优先级,VxWorks采用的方式。 禁止中断,也就是在临界区不可被抢占,Linux 采用的就是这种方式,在 thread_info.preeempt_count 记录每个进程当前持锁计数。 另外,调度器是不能在进程指令流的任意一点执行打断的,因为进程可能此时正在做任何事情,如系统调用,死循环,锁操作等,要实现任意状态的可抢占性代价是很大的,需要 OS 和 App 的通力配合,特别是在涉及在内核态的时候,目前所有OS都不能在进程执行的任意点进行抢占。只是说不断让抢占区更大,抢占点尽可能地密集。 实时 vs 非实时 从系统需求或用户角度而言,调度系统可以分为实习系统和非实时系统。 在实时操作系统中,系统必须在特定的时间内完成指定的应用。实时通常分为软实时(soft real-time)和硬实时(hard real-time),硬实时是指系统要有确定的最坏情况下的服务时间,即对于事件的响应时间截止期限无论如何都必须得到满足,通常应用在军工航天领域。而软实时只提供统计意义上的实时,比如应用要求在95%的情况下都会确保在规定的时间内执行某个任务,而不一定要求100%。实时系统通常采用抢占调度,实现一个硬实时系统的代价是很高的,要做到进程可以在任意时刻被抢占,在现代OS上来讲,基本是不可能的,因为OS的很多系统调用都是不可被打断的,并且很多操作具备时间的随机性,比如 CPU Cache Miss,Page Fault,CPU Branch Predictor 等等。 BTW, 为什么现代OS不尽可能地去掉这些不稳定性(如虚拟内存,CPU多级Cache,分支预测等),从而为成为实时系统打好基础呢?对桌面操作系统而言,对实时性的要求没有那么高,应用切换偶尔卡一卡并无大碍,桌面系统更关注的一方面是对内核的统一抽象,屏蔽硬件差异化,接口丰富易用,让上层应用易于开发,比如虚拟内存,文件描述符等。另一方面,桌面系统在选择牺牲部分的实时性来提高吞吐量,比如多级Cache,分支预测。因此尽管如 Linux 这类桌面系统支持实时优先级和多种调度机制,但仍然最多只能实现软实时,这是设计目标决定的。 而非实时系统,则没有对最低任务处理时延的要求,比如简单的非抢占调度模型。 ...

2021-04-27 · 3 min · 591 words · -