diff --git a/C and C++.txt b/C and C++.txt index 7598f4d..6510f1c 100644 --- a/C and C++.txt +++ b/C and C++.txt @@ -152,6 +152,12 @@ (tips:是的,这里联想Unix环境高级编程中的示例,先fork, 然后子进程exec,主进程wait) +42、在使用localtime, inet_ntoa 等等函数需要小心,建议使用对应的多线程版本 + 例如:localtime_r, inet_ntop 等 + +43、字节序通常在单字节的数据上不用关心,但是在struct使用位域,则引入了位序, + 在不同硬件平台下,需要关注字节内部的字段定义。 + 具体可参考内核中 tcphdr 和 iphdr 的定义。 @@ -225,6 +231,22 @@ (tips:一种简单的实现是 在类中定义private 拷贝构造函数,但是这种方式不够清晰 更好的做法是 定义个 模板类,定义 pravate的拷贝构造函数,然后 子类 private 继承 Uncopyable 类) + +27、C++里面的匿名namespace中的成员,相当于添加了static属性,因为编译器实际上会为每一个匿名的namespace + 生成一个名称,且添加using声明,但是这个名称在其他模块是不可见的,这也就添加了internal属性。 + +28、unique_ptr不能进行拷贝操作,也就是不能直接进行传参和从函数中返回,取而代之的是调用move函数 + +29、shared_ptr的引用计数更新是线程安全的,但是指针的更新不是,需要注意,对于*直接使用指针的形式 + 也不是线程安全的。 + +30、delete NULL是安全的,delete的实现中直接调用了 free(p),而析构函数的调用是编译器插入的; + 如果delete void*,编译器会给出warning;各个malloc的实现中,free(NULL) 都是直接返回,没有啥直接效果。 + +31、虚函数为啥不能inline? + 其实,如果是由object来调用虚函数,那么函数可以inline; + 但是如果通过指针或者引用来访问,那么就走运行时多态特性了,即拿到类对象指针,访问虚函数表, + 解引用对应function pointer,整个过程中必须有call指令调用 // debug diff --git a/awesome_project.txt b/awesome_project.txt new file mode 100644 index 0000000..398ff65 --- /dev/null +++ b/awesome_project.txt @@ -0,0 +1,2 @@ +1、ext3grep + url: https://code.google.com/archive/p/ext3grep/source/default/source diff --git a/bfs.txt b/bfs.txt new file mode 100644 index 0000000..dbb7adb --- /dev/null +++ b/bfs.txt @@ -0,0 +1,17 @@ +// bfs related + +1、bfs SDK的接口整体上继承了Posix风格,理解起来比较容易。 + +2、bfs SDK中写入逻辑是先写到本地缓存中,大小为 256KB,满了后,后台异步的刷新到chunkserver中, + 所以当SDK接口返回时,数据并一定写入chunkserver,且chunkserver在open本地文件时, + 没有使用O_SYNC标志。 + +3、现在看,bfs没有将文件切分为 chunk,本地上传一个大文件,对应的chunkserver也有一个同样大小的文件 + (注:这个理解可能随着后面代码阅读的深入改变) + +4、bfs SDK写入支持链式写和星型写,但是没有相关的慢盘规避策略。 + +5、chunkserver中有统计load,数据源来自于pending状态的buffer数量。 + +6、CreateFile逻辑中,先Create成功,然后同步日志,如果在Create完成, + 但是在同步Log前,程序异常退出,是否会造成某些数据不一致。 diff --git a/books.txt b/books.txt index d1f6e7e..a14bf55 100644 --- a/books.txt +++ b/books.txt @@ -8,4 +8,6 @@ 4、《The Garbage Collection Handbook》 Author Richard Jones -5、《Virtual Machines》 Author James E. Smith \ No newline at end of file +5、《Virtual Machines》 Author James E. Smith + +6、《Technical Report on C++ Performance》 diff --git a/cmd_line.txt b/cmd_line.txt new file mode 100644 index 0000000..187c6f8 --- /dev/null +++ b/cmd_line.txt @@ -0,0 +1,33 @@ +// common cmd questions + +1、ls -l 显示的total表示很么意思? + (tips:表示当前目录下文件占用的底层磁盘block数量, + -l 显示的第二列表示当前文件的hard link数量) + +2、stat 命令显示的Blocks表示什么? + (tips:表示占用的底层磁盘的block数量,即使创建一个很小的文件, + block数量也为8,是因为文件系统层面和底层磁盘使用的单位不一样, + 文件系统层是 page,也就是4096,所以即使文件很小,也会分配8个block来存储) + +3、netstat -s 命令的输出有很多网络详细信息 + +4、watch -d -n1 'command'是个很有用的命令,可以动态演示一些命令的输出变化,例如丢包数量等。 + +5、top命令显示中,按 F,然后空格选中P,那么可以展示对应进程运行在哪个CPU上 + +6、file core_file 可以查看core文件是哪个binary产生的 + +7、ss -at '( sport = :80 or dport = :80 )' 可以通过端口来过滤链接, + 也就可以快速的确认是否有服务在监听某个端口 + +8、last reboot可以查看机器的重启信息 + (tips:last命令读取的/var/log/wtmp文件,可以查看对应用户的登录信息,例如: + last -n 10 user_name) + +9、groups命令可以查看当前登录的用户对应的用户组 + +10、python -m py_compile your_python_script.py可以完成python脚本的语法检测 + +11、命令前加 \ 可以跳过alias,运行original command + +12、pkill -9 -P 1,pkill -P参数可以根据父进程id来向目标进程发信号 diff --git a/distributed system.txt b/distributed system.txt index df17344..c53b039 100644 --- a/distributed system.txt +++ b/distributed system.txt @@ -6,10 +6,22 @@ 2、分布式系统需要面对的问题有哪些? 3、简单描述下CAP理论?举例说明CA对应什么系统,CP对应什么系统,AP对应什么系统? + CA不保证分区容忍性,对应的是本地数据库系统,本地文件系统等。但是C和A本身是 + 对立的两个概念,本地数据库已经不是分布式系统了。 + CP不保证可用性,常见的如分布式数据库,大多数的分布式一致性协议等 + AP则不保证数据的一致性,常见的如NoSQL数据库,DNS系统等 4、什么是2PC? + (tips:协调者发送canCommit,当执行者回复ack后,事务提交; + 问题在于,当第一步执行者回复ack后,执行者会blocking等待,如果这时协调者crash, + 那么整个系统将不可用) 5、什么是3PC? + (tips:3PC为了解决2PC的blocking问题,引入pre-commit阶段 + 1、在participant转为pre-commit状态前,coordinator crash,那么事务可以安全的abort + 2、在partitipant转为pre-commit状态后,coordinator crash,那么新的coordinator启动后, + 通过询问其他participant的状态,如果全部为pre-commit,那么事务可以安全提交;如果状态 + 不一致,那么需要abort transaction) 6、什么是leader 选举? @@ -33,3 +45,30 @@ 2、空间利用率低,和 cuckoo filter相比 3、不支持反响删除操作,基本用于只读场景下 4、不支持计数操作) + +14、分布式系统中一个需要解决的问题是将 partial-failure 转换为 fail-stop failure, + 因为在常见的心跳设计有时只考虑链接的稳定性和进程的存在性,而忽视了进程真正的服务能力, + 当遇到partial failure时,因为无法通过心跳检测到异常,所以整个服务处于亚健康状态。 + 这种情况下,直接让异常进程退出,通过其他机制(如,一致性协议)来恢复服务可能更好 + +15、分布式系统中可以使用mlock算法来保证请求的QoS + +16、分布式唯一ID生成算法,常用的有facebook开源的雪花算法,还有国内开源的mist薄雾算法。 + 雪花算法中使用了时间戳,在遇到时间回退时,有可能会产生相同ID,且整个ID的区间比较小, + 时间戳也只能保证使用70年。 + 薄雾算法没有使用时间戳,没有时间回退的问题。但是当程序重启后,再次运行,其基准计数有可能 + 相同,导致有可能ID重复。薄雾算法产生的ID空间更大,每天使用10亿,能使用300年。 + so,算法各有优劣,需要权衡使用。 + +17、raft 和 multi-paxos算法的区别 + (1)raft算法要求,所有请求都是串行的发送给leader节点,而multi-paxos则可以并发的发送到多个节点 + (2)raft的选主算法中,只有拥有最全的日志节点才能当选 + +18、简单的master-slave模型,在master故障后,slave提升为master有两个问题,一是异步同步算法下,有可能有数据还没同步完成, + 二是,新的master带来的单点问题;如果使用强同步模型,那么性能会降低;如果一个master对应多个slave,那么在master掉线后, + 又有多个slave的选举问题。 + raft等算法都是基于 Replicated State Machine 模型,在各个节点间同步log + +19、什么是脑裂? + (tips:即因为网络等其他原因,导致集群或者某个服务出现两个primary对外提供服务, + 常用的paxos等算法中,会通过选举+版本控制的方法来避免这一情况出现) diff --git a/fastdfs.txt b/fastdfs.txt new file mode 100644 index 0000000..3d39b07 --- /dev/null +++ b/fastdfs.txt @@ -0,0 +1 @@ +# fastdfs related questions diff --git a/file_system.txt b/file_system.txt index d29c93e..4039a9f 100644 --- a/file_system.txt +++ b/file_system.txt @@ -43,6 +43,9 @@ 21、DIRECT I/O 和 SYNC I/O 的区别是什么? 22、free 命令的显示中 buffer 和 cache 分别代表什么? + (tips:现在buffer 和 cache统一都由page cache实现,只不过buffer是比较特殊的直接操作设备文件产生的空间占用, + 例如,直接读取 /dev/ 设备 + cache 包括了page cache 和 slab中的可回收部分,其中cache的计算中已经减去了buffer 和 swap用到的page) 23、文件的覆盖写和追加写有什么区别? @@ -137,6 +140,7 @@ 63、SSD的预留空间对于文件系统可见吗? (tips:不可见,不同厂家的预留空间占总存储比例不同) + 预留空间被称为OP(Over-Provision),是为了应对写放大问题,如果OP空间大,那么适合于写密集型应用 64、机械硬盘和SSD的Cache采用的是? (tips:SRAM) @@ -213,7 +217,9 @@ 87、Ext4 extents的作用是? (tips:将同一文件的数据临近存放,减少之前间接块索引导致的多次读取元数据, - 同时增加预读效果,加快truncate操作) + 同时增加预读效果,加快truncate操作 + 单个extent最多包括32768个block,即128M的空间,如果当前文件系统开启了extent属性, + 那么在写入时就会按照extent的规则来写,同时 ioctl也支持从块指针的方式转为extent) 88、Ext4 multiblock allocation概念? (tips:之前的ext文件系统分配文件block时是一次分配一个block,性能较低) @@ -227,7 +233,19 @@ 91、SSD 中 wear leveling 作用是? (tips:SSD中每一个cell都有固定的寿命,如果频繁操作某一些cell,会加快这些cell的失效, - wear leveling 的作用就是尽可能的将读写操作分布于整个SSD中,而不是集中于某一些cell) + wear leveling 的作用就是尽可能的将读写操作分布于整个SSD中,而不是集中于某一些cell + 算法实现上有两种 + (1)动态WL + 在一个空白池子中选择待写入的块;但是有一个问题是,空白块往往是PE比较高的块, + 在某种程度上会加剧磨损不均衡 + (2)静态WL + 动态的记录某一个block的PE值,尽可能区分冷热数据,然后进行选择性写入 + 算法实现上的一些问题: + (1)记录的PE值需要动态的去收集,本身就有开销 + (2)底层去分析上层的IO pattern本身就有难度 + (3)注意,WL也可能占用硬件带宽 + 在某些实现中,可能倾向于反磨损均衡(某些固态盘阵列),预期就是这些盘一块块的坏掉替换 + 而不是在某个时刻批量的不可用) 92、SSD中,怎么回收处于stale状态的page? (tips:TODO) @@ -325,6 +343,7 @@ 而不是磁盘上,这样解决了Ext2上的预分配导致的Crash恢复时数据的不一致问题 124、Ext4 Extent 树高最高为 5 + (tips:实际上不大于2) 125、fsync在保证当前文件本身的数据落盘的同时,对于包含这个文件的文件夹的更新则没有保证 @@ -341,7 +360,10 @@ 129、I/O polling? (tips:接上一条,如果外部存储设备的延迟够低,那么可以不用现在的基于中断的模式,而是polling,等待直到I/O完成, 减少中断次数;如果采用这种技术,那么应用层应该尽可能的少用Non-blocking,因为这又引入了异步模式,需要额外的操作来 - 确保命令已经执行完) + 确保命令已经执行完, + IO polling 有一个问题就是CPU消耗,试想如果一个IO需要10us完成,在submit_bio之后就立即polling,那么 + 刚开始的几us相当于在做无用功,所以内核引入了 hybrid poll机制,即,在任务提交后,sleep一段时间,然后再 + polling设备,以降低CPU消耗) 130、接上一条,有的SSD本身具有中断聚合功能,即尽可能少的中断CPU执行,批量提交I/O完成信息 @@ -406,7 +428,9 @@ 再写入,这一步也会有开销,已3+1 模式为例,会有4倍的数据流量) 147、EC编码和RAID技术的比较? - (tips:RAID技术不适用于大容量存储中,RAID5 5个9 RAID6 8个9的数据安全性) + (tips:RAID技术不适用于大容量存储中,RAID5 5个9 RAID6 8个9的数据安全性 + RAID5 采用 n+1 的编码模式,即允许坏一块盘,且校验码打散在各个盘上 + RAID6 采用 n+2 的编码模式,允许坏两块盘,校验码打散在各个盘上) 148、磁盘静默错误怎么解决? (tips:使用 DIX/DIF在每一个扇区后追加校验字段来解决, @@ -449,7 +473,15 @@ 155、iostat 命令中 avgrq-sz 和 avgqu-sz表示什么意思? (tips:avgrq-sz 表示平均 request大小,单位为 sector - avgqu-sz表示发送给设备的requests队列长度) + avgqu-sz表示发送给设备的requests队列长度, + 这里有个问题就是iostat的输出和dd或fio工具命令行中的bs大小不一致, + 从上面的描述中可以看到,avgrq-sz表示的是 request 结构所表示的io请求大小, + 所以在用户层面的写入后,(1)在 writeback page_cache时; + (2)在block层的 plug机制 (3)io 调度器的merge机制 会尝试合并请求, + 所以会看到avgrq-sz比较大; + avgrq-sz的最大值为 /sys/block/sdx/queue/max_sectors_kb ,单位为kb, + 如果取值 256,则avgrq-sz的最大值为 512, + 且需要注意,这里统计的是读请求和写请求的和) 156、EAGAIN 和 EWOULDBLOCK 两个错误码有什么区别? (tips:在Linux上,这两个错误码的定义数值是相同的, @@ -465,12 +497,16 @@ 158、Ext4 默认开启barrier特性,且 Journal中有校验 checksum,如果乱序写入,且transaction中断, 那么后续的校验将不通过,本次操作会abort;在高端存储磁盘有 battery-backend时,可以考虑关闭 barrier以提高性能 + (barrier之前是通过drain request_queue 然后再发送一个flush请求实现的,这样虽然可以达到预期效果, + 但是会影响性能) 159、fsync 函数返回EIO错误时,如果直接进行重试,fsync会 clear之前有出错标志的page, 这会潜在的导致数据丢失,Pg数据库目前的做法是fsync返回EIO时,直接panic 160、iostat 命令中的 rKB/s 或 wKB/s表示? (tips:表示每秒 读取 或 写入的数据量) + r/s 和 w/s 分别表示什么 + (tips:表示完成merge后的读写请求request数量,对应内核中的request,注意,是merge后的) 161、产品宣传时提到 IOPS 的数据时,也要同样考虑 IO 的 latency 数据, 这两个同步提高了,才能说明从硬件到软件都有优化 @@ -530,6 +566,7 @@ TBW(Total Bytes Writen) TBW = Total Capacity * 擦写次数/写放大率 + 关于TBW,网上还有另一种单位,TeraBytes Written,与之对应的还有 PBW,即SSD可以写入的总PB数据量 175、查看磁盘空间时,需要明确文件系统有预留空间,inode table 也会占用空间 需要使用 tune2fs -l /dev/sda 查看下 super block的信息,确认空间占用 @@ -539,3 +576,239 @@ lower layer的文件会被隐藏。 它的实现是在内核中加入了重定向功能,即对overlayfs目录下文件的操作在内核中会重定向到实际的文件。 这样系统的page-cache 也只需要保留一份。 + +177、JBOD一种为各个磁盘单独控制,且结合RAID技术;另一种则是简单的将多个磁盘抽象为一个大空间, + iops等和一个磁盘相同,且操作系统或文件系统的元数据在第一个盘上, + 如果第一个盘损坏,则整个数据将不可读。 + +178、du 和 df 展示的结果不同,数据对不上,这是因为du基于stat系统调用,看到的是一个文件的数据; + df 使用的是 statvfs 系统调用,是展示整个系统或分区的角度来获取数据;du可能还要考虑follow + symlink或 hard link的重复计算,而 df 可能还需要考虑系统预留空间,inode的inline data, + journal 的预留空间等 + +179、ext系文件系统为什么要有块组的概念? + (tips:如果没有块组,那么所有的磁盘数据块只存在于一个大的块组中, + 对应的,block bitmap也比较大,查询空闲数据块的耗时将增加,解决这个问题的办法就是把磁盘空间 + 再划分为对应的块组,拿到inode number或者block number,只需要进行一个除法运算就可以快速确认 + 属于哪个块组) + +180、每一个块组中inode bitmap占一个block,一个block按 4096 bytes计算,则,一个块组中可以包括 + 4096*8 = 32768 个 inode。 + 但是用 mke2fs等工具格式化磁盘时,整个分区的indoe数量 按照 每 16KB 一个indoe的方式来计算,目的是为了 + 节省空间,因为一个inode需要占用 256 bytes空间(super block中存储了一个块组中有几个inode)。 + 使用 mke2fs -N 可以扩大inode数量,但是仍然没有突破 inode bitmap占一个block,只不过inode table数量变大。 + +181、ext4文件系统中,如果当前的Journal模式为JOURNAL_DATA_MODE,即数据也要写入Journal, + 那么不支持DirectIO;如果当前文件有inline data,那么也会切回buffer IO模式。 + +182、在进行DirectIO写入后,需要invalid对应的page cache中的page,这样才能保证在下次buffer I/O操作中 + 拿到最新的数据 + +183、iostat的数据源来自于/proc/diskstats文件,里面记录了raw data,iostat读取这些数据, + 然后展示出来。 + (avgrq-sz是用 读写成功的 bytes/读写请求次数,前者和一个request相关,后者则和这个request里面的bio + 数量一一对应,虽然在scheduler层会对bio进行merge(merge时需要位置严格相邻),将bio链接 + 到request的bio_tail链表中,整体上bio num不变。 + 在使用 dd if of bs=4K count=1000 oflag=direct 进行测试时,发现 avgrq-sz 为8,刚好为4K, + 实现为 direct 绕开了 page cache,就不会在生成bio之前对 请求进行merge,所以提交到 request 链表中的 + 都是 4K 的bio,这样在 plug 或 io scheduler merge之后,bio数量没变,所以用 total_bytes/bio_num = 8*512 + 即8个 sector) + +184、free命令会显示cache/buffer,其实现在的buffer也是通过page cache实现的,而buffer在内核中的表示是 + buffer_head 结构,这个struct之前用来表示block_device中的block在内存中的数据,也是老的系统中文件系统 + 访问底层的结构,不过现在被bio结构取代,但是在 直接 mmap /dev/下的设备时,仍然有使用,这也就对应了 + 有的文章说 现在的buffer只是对应了裸设备的数据映射。 + 同样的,之前也有文章说 直接 mmap /dev/ 然后再通过其他普通文件系统 read 同一份数据,page cache中有两份 + 这是因为 两者使用了不同的文件系统,对应了不同的inode,也就对应了不同的address_space,这样数据就有了两份, + 如果修改其中一份数据,另一份不会直接同步。 + +185、BIO结构完成了从page cache内存地址到dev中sector地址的转换,每一个bio中代表一段连续的地址空间, + 里面有可能包括多个bio_vec,一个bio_vec表示一个file system block。 + 以16KB连续数据为例,需要一个bio结构,里面包括了4个bio_vec;如果数据不连续,则需要多个bio结构 + +186、使用 echo '' > file 这一步,其实是在open中传入了O_TRUNC标志,进而在打开文件时完成文件长度修改。 + (tips:O_TRUNC的实现中,首先对inode->i_mutex加锁,然后修改inode->i_size,再invalid page cache, + 这时有一个特殊的点,就是如果这个page mmap到用户态,则需要修改页表,用户的下次访问需要走page fault的流程, + 重新读取文件内容 + 这次还验证了一个场景,一个进程读文件,另一个后台进程覆盖文件内容,那么第一个线程会读取到新内容, + 所以说,文件系统并没有保证一致性) + +187、在 /etc/mke2fs.conf 文件中有创建文件系统时的默认配置,通过这些配置可以熟悉了解文件系统都实现了哪些功能, + 有哪些常用feature + +188、debugfs 命令还有个交互模式,里面有一些很有用的命令,可以用于查看磁盘上的数据 + +189、网友测试中遇到了一个场景,用 dd if=/dev/zero oflag=direct 写入nvme SSD,速度比默认使用buffer快很多, + 自己找了测试环境验证了下,确实是这样。 + 使用 direct 写入sata HDD,速度和使用 sync 模式差不多,但是nvme快很多。 + 看了对应的代码有如下可能原因: + (1)direct IO 直接使用了 用户态buffer 对应的 page,没有数据的拷贝,这在写入场景下同样适用 + (2)direct IO 直接将request提交到了对应device的 request queue,HDD通常是单队列, + 但是,nvme SSD队列数可以非常多,处理的速度也比较快 + (3)nvme SSD 和 HDD设备侧使用的调度算法不同,nvme由于处理速度快,更多的使用noop, + HDD常使用CFQ + +190、direct I/O 有什么好处? + direct I/O 有如下几个好处: + (1) 减少CPU利用率,因为如果使用buffered I/O,那么需要将用户空间的数据拷贝到page cache中, + 相当于执行memcpy + (2) 使用buffered I/O 会增加系统内存压力,有可能导致其他cache 命中率较高的文件提前刷回, + 在下次访问时,又需要读回,即减少page cache整体的抖动。很多数据库产品都是在用户态实现文件的内容缓存, + 写入数据时采用direct。 + 但是,在采用direct时,需要注意,read/write调用的执行时间可能会增加,这是由direct的执行特点决定的, + 具体需要考虑direct是将请求一直要发送到通用块层才返回的。而buffered I/O只需要到page cache层就结束了。 + +191、如果文件之前已经通过buffered I/O访问过,那么direct I/O 的写请求会同步更新page cache。 + +192、怎么增加磁盘的队列深度? + (tips: + 1、使用异步IO + 2、多线程并发提交IO任务 + 3、每次写入大块数据,块层会自动按照内核的配置将一个任务切分为多个子任务 + + 在压测磁盘时,需要增大磁盘的队列深度,也就是说有积压的任务在等待处理,从而提交dist util) + +193、对于SSD设备,因为可以并行处理IO请求,所以iostat的工具输出中 util% 和 svctm% 不能表达出真实的情况 + 这一点与磁盘设备不同。 + +194、rename这个函数,新的文件路径和旧的文件路径必须位于同一文件系统下,否则会报错。 + 从其实现上讲,rename其实就是修改inode指向,同时inode里包含了文件操作相关的operation指针, + 文件系统不同,这些函数实现不同,所以不能直接修改。 + +195、现在的磁盘都采用的ZBR(Zoned Bit Recording),将磁盘划分为zone,zone内部扇区数量相同,zone间, + 扇区数可以不一样。这样,当磁盘转速一定,那么外圈的磁头单位时间的线速度将更高,能读取的扇区数更大。 + 以前的磁盘,内圈和外圈的扇区数一样,这样磁盘的空间小,利用率不高。 + +196、在使用df看磁盘的使用率时,会遇到磁盘空间满,但是du 得到的占用和df结果不匹配的情况。 + 这种情况下,可以使用lsof | grep deleted,看下是否存在文件被删除,但是进程仍然持有 + file handle的情况,这时删除文件的空间还没有被操作系统回收,所以df显示磁盘满。 + +197、DAX(direct access)访问文件时,绕过了page cache,直接访问硬件上的数据,在NVMDIMM等设备上使用较多。 + 与O_DIRECT的软件实现不同,DAX是一种硬件特性,且内部实现了一整套的访问(内核代码中的各种operation实现), + 包括page fault的处理,硬件驱动的支持等。其本身实现的基础就是高端存储硬件提供直接的访问支持。 + 现在支持DAX的文件系统有XFS和EXT4 + +198、NVMDIMM是一种结合了DRAM和FLASH的硬件,它本身提供了电容,保证了掉电时DRAM中的数据可以写回FLASH, + 电力恢复后,再将FLASH中的数据加载到系统内存中。 + +199、AEP(Apache Pass)是intel Optane产品的代称,是一种以3D XPoint介质为基础实现的非易失性存储介质, + 它提供的数据访问带宽比内存低,但是比FLASH高,同时具有持久化功能,可以用来实现更快速的缓存系统 + +200、lsof的输出中,FD列中的显示中,mem表示mmap的文件,mmap表示内存映射的设备,txt表示二进制可执行文件, + 数字后面跟 r w u,其中,数字表示进程中fd number,r w u分别表示读 写 混合读写 + +201、在调查磁盘读写高时,除了考虑常用的 read/write 调用,其他还有 mmap 需要考虑, + 还有一个就是libaio的使用,这在需要trace系统调用的场景下尤其需要注意 + (tips:libaio并没有增加新的读写接口,数据还是要通过file_operation中的接口来完成读取, + 所以在某些 trace 工具中,通过hook generic_read/generate_write来统计除mmap以外的读写 + 是可行的) + +202、binary的text section加载进内存后,page属性会被标记为 executable,且禁止写入,那么在内存紧张时, + 这部分内存会被回收,带来的问题就是可以看到进程在不停的读磁盘文件,其实是加载binary的文件内容到 + 内存中,这个现象和swap有点像,但还不一样,因为clean page是可以直接被回收的,没开启swap时, + 也不同执行swap out + +203、ext3/ext4 文件系统在文件删除后,会清除inode中的size 和 block pointer字段。如果需要恢复被删除的文件, + 则需要读取文件系统 journal 中记录的日志来undo对应的操作。 + 文件系统的journal并不是一个普通的文件,对用户不可见,但是文件系统可以对它进行读写。debugfs 工具的logdump + 可以看到文件内容。 + 网上说 ext2 在文件被删除后只是将inode标记为 deleted,没有清楚对应的block pointer等字段,但是在1.12的代码中 + 看到是会清楚 block pointer,所以这里待进一步确定! + +204、内部ext4 挂载时都用了 nodelalloc选项来关闭delay alloc。delay alloc的作用是在buffer I/O中,数据写入 + page cache时不立即进行磁盘快的分配,而是等到上层调用fsync或定时写回时才分配。 + 但是在文件写入时(不管是写入page cache,还是写入磁盘),要持有一把锁,一般情况下,写入很快完成;但是 + delay alloc 因为将块的分配推后,等到真正需要分配时,需要的块有可能很多,导致延迟增加,进一步影响 + 应用的响应。 + delay alloc的优点有减少分配块次数,单次的写入延迟降低;且批量分配能将文件内容尽可能存放在连续的磁盘块上, + 有利于后续的I/O; + 缺点就是:有可能某一次的写入延迟增加,且存在page cache内容准备刷回磁盘发现磁盘没有空间这种情况。 + +205、lsof的输出 或者 ls -l /proc/$PID/fd/ 结果中有 anon_inode,这是内核中的anon_inodefs伪文件系统下面的 + inode,一般多用于eventfd,signalfd,timerfd等,这类文件描述符并没有真正对应inode,但是为了保持语义一致, + 内核会为对应的fd分配inode。 + /proc/$PID/fd/ 输出格式一般为 type:[inode number],如果type为socket,那么通过inode number可以在 + /proc/net/ 目录下的 tcp/udp/unix 等文件中找到详细信息 + +206、当磁盘开启了write cache时,数据写入磁盘上的DRAM后,立即向上层返回成功,保证了顺序写和随机写性能, + 但是存在数据掉电丢失的问题。如果关闭write cache,那么可以保证数据安全。 + 现在有PWC技术,可以保证在掉电时,仍然能写入少量数据(M级别) + 使用 hdparm -W /dev/sda 命令可以查看磁盘的write cache是否打开 + +207、/proc/$PID/io 文件里面的rchar表示read或pread系统调用中传入的参数之和,对应的wchar表示write或pwrite + 传入的参数之和;read_bytes 表示真正从后端设备读取的数据量,统计发生在submit_io这一层,write_bytes则 + 表示page-cache写回后端设备的数据量 + iotop命令中,total 列统计的就是各个process的rchar和wchar + 而actual 列统计的则是/proc/vmstat中的pgpgin 和 pgpgout,单位为KB + 对于读操作,pgpgin 和 read_bytes 保持同步更新,但是写操作在submit_bio层更新pgpgout,在__set_page_dirty调用 + 中更新write_bytes + +208、磁盘控制器频繁的下发SMART指令,也会对性能产生影响,一般都是分钟级的采样。 + 这也就带来一个问题,上层怎么快速的发现磁盘的问题,这就要用一些写入测试来辅助, + 例如现在在用的 write_test 方法等 + +209、磁盘的低级格式化主要是将盘片划分出磁道,柱面,扇区,且通常由磁盘厂商完成。 + 磁盘的高级格式化主要是文件系统相关的元数据的从新划分,例如,重新初始化空闲块位图, + inode位图等。 + (tips:磁盘寻址使用CHS地址,即C 柱面,Head 磁头,S 扇区 + CHS寻址方式受限于地址存储位长,当前的大容量磁盘采用LBA方式寻址, + CHS只留给磁盘驱动来处理) + +210、磁盘读写操作的最小单位是扇区,写入时如果不按照扇区大小(512字节)对齐,那么下次的写入只能 + 另外找空闲扇区来写。 + 普通的read/write是buffer写的,数据先写入page cache,由于内存的随机读写特性,数据可以以任意单位写入,随后由 + 内核线程写回磁盘。 + 另外,由于direct模式的写入对buffer地址,file offset,写入大小都要求以512对齐,所以,就避免了扇区浪费。 + (之前还想着写个程序,每次以direct模式追加写入1字节,这样就可以快速的占满磁盘,但是有了上述的限制, + 就没法实现了) + +211、/etc/fstab 文件中记录了在机器启动时需要挂载的磁盘信息; + /etc/mtab 文件中记录了当前机器上已经挂载的磁盘信息 + +212、遇到 Disk quota exceed 报错时,先 df -hi 查看对应磁盘的inode使用情况; + 如果没有超;那么查看/etc/mtab文件查看对应挂载点的挂载属性中是否有 usrquota/grpquota, + 如果有,那么可以使用 edquota -u $USER 或 edquota -u $GROUP 来查看当前为用户/组设置的quota + 设置quota可以使用 setquota 命令。 + edquota命令查看不到设置的quota时,可以使用 repquota -g or repquota -u mount_point来确认 + 对应目录是否有设置quota + +213、getfattr -d -m '-' /some/file 命令可以获取文件的所有扩展属性,EXT系列文件支持这一属性。 + 大小限制可以 man 5 attr 查看 + +214、什么是 last write win? + (tips:last write win是一种在并发写入场景下的数据版本决策策略,服务端以时间序,选择最后一写入的 + 数据来作为最新版本 + 为什么不是客户端看到的顺序,因为在分布式系统下,很难做到客户端和服务端时钟的一致性(例如,客户端网络卡顿), + 要做到,可能得启用更重的分布式一致性协议,但这又会导致整体QPS低。 + 以分布式对象存储为例,就选择应用lww 原则) + +215、看文章有人遇到访问/proc下文件慢,先用strace发现耗时长的调用,进而使用公司内部的分析工具确认 + 耗时调用栈。这里有个问题,外部如何确认调用栈(pstack? SystemTap?) + 最后问题怀疑点是 /procy 文件系统实现时有一把全局的mutex,怀疑大并发访问导致锁争用 + sysak 貌似开源了 + +// block layer +1、io_submit调用理论上是非阻塞的,但是如果执行sync写入,或者有meta data I/O,或者,设备中能提供的 + request数量有限,如果超过这个限制,也只能阻塞进入等待。 + +2、怎么确保文件系统下发的写入操作返回时,数据真正落盘呢? + (tips:使用SCSI中的FUA命令,可以将磁盘的缓存模式调整为write through,这样当磁盘驱动器向操作系统返回写入成功信号时, + 数据就真正落盘了) + +3、Linux系统中,文件系统下发的bio可以带 REQ_PREFLUSH 和 REQ_FUA两个flag,前者保证在处理bio之前将磁盘cache写回, + 后者的作用如2所述。block layer不用关注这两个flag的底层实现逻辑,磁盘驱动需要实现这两个功能, + 如果磁盘没有non-volatile buffer,REQ_PREFLUSH不作任何操作。 + 如果物理磁盘不支持FUA,那么可以将FUA操作模拟为一个写入+FLUSH + +// trace +1、通过iostat看到某个磁盘util高,怎么确认是哪个进程在进行I/O? + (tips:首先通过df查看磁盘挂载在哪个目录,然后通过lsof输出对应目录下所有打开的文件,参考文件打开模式, + 然后过滤进程,利用 pidstat -d -p $PID,查看具体某个进程的IO量,同时,也可以通过strace查看read和mmap + 系统调用的使用情况,进一步确定是在读写哪个文件,有时存在一种情况,使用iostat或iotop可以看到读写流量, + 但是trace并没有发现有read/write,那么有一种情况就是进程在频繁的调用fsync,需要使用其他工具来trace sync + 类的调用) + +2、怎么确认某块硬盘状态是正常的? + (tips:现在的硬盘,包括SSD都支持SMART特性,所以可以使用smartctl --all 或 smartctl -H dev 命令来查看对应盘的信息 + 如果确认磁盘发生了错误,可以使用badblocks命令来扫盘,以确认具体哪个block损坏; + 执行检查时,可能需要辅助使用 fdisk -l 或 dumpe2fs -h dev 命令来确认一些硬件或文件系统元数据) diff --git a/gcc_gdb.txt b/gcc_gdb.txt new file mode 100644 index 0000000..38badca --- /dev/null +++ b/gcc_gdb.txt @@ -0,0 +1,84 @@ +// gcc & gdb related + +1、在查看coredump文件时,如果遇到调用栈栈顶附近的函数为 ??,则可能是 + 栈被破坏了,调查的一个方向是 f 0 ,p $rsp 查看第一层函数栈顶指针, + 再 f $top , p $rsp 查看调用栈栈顶的栈指针,两者相减,看是否是调用栈 + 过深导致。 + +2、上述过程中,不能使用rbp寄存器,因为使用 -fomit-frame-pointer 选项编译时, + 会去掉rbp寄存器的使用 + +3、gcc 4.x及以下的版本的 string实现是基于引用计数和 COW,gcc 5.x 替换了另一种短字符优化sso的方案, + 其实现就是string分配16字节的预留空间,和allocated_capacity保存在一个union中。 + +4、gcc的-l选项只在编译时起作用,如果是静态库,那么因为已经打包进入binary,运行时无需特殊指定库路径, + 如果是动态库,那么gcc会在默认路径下找库,和编译时库路径无关。 + LD_LIBRARY_PATH环境变量就是指定运行时库路径的。 + 编译时可以通过 -W,--rpath 来指定运行时的动态库路径 + +5、gcc在使用静态库时,不会把整个库都打包进去,而是会分析调用链,按需加载使用,减小binary大小 + +6、想查看gcc默认的搜索头文件路径,可以在编译时加上 --verbose选项 + +7、/lib64/ld-linux-x86-64.so.2 这个文件是linux默认的 dynamic-linker,在命令行执行 chmod 等程序时, + 系统其实执行的是 /lib64/ld-linux-x86-64.so.2 chmod,它的作用是完成动态库的加载,然后执行ELF程序 + 所以可以测试一个binary 没有 可执行权限,然后由 /lib64/ld-linux-x86-64.so.2 binary 也是可以启动的 + +8、为什么编译时不需要显示的指定 glibc的库 + glibc库的路径已经固化到gcc中了,不用再显示指定,除非要用自己的glibc库 + +9、gcc编译出来的C库,不能直接在g++编译的C++程序中使用,如果使用,需要在对应的header文件中添加 extern "C" 标识, + 但是C中没有extern "C" 的写法,所以,需要在程序中使用预编译宏来判断,例如 #ifdef __cplusplus + +10、在实现用户态的spin_lock时,建议使用CPU的 pause 指令,这可以提高在 spin wait场景下的性能 + 具体可以参考网上文章的说明和Nginx的spin lock实现 + +11、在使用glibc等库中的函数时,如果不确定,一定要先man一下,看看应该引用哪些头文件, + 如果没用正确的引用头文件,会引入奇怪的问题,例如调用 strerror core, + lseek 函数偏移量异常等。 + strerror core的原因是,编译器将函数返回值转为int,而64位环境下指针为8bytes,int为4bytes,访存异常。 + lseek这个是因为,如果第二个参数显示的写为字面值,编译器传参为 int,但是函数中用的是 8bytes的数据, + 会引入一些非预期数据,导致seek结果非预期。 + +12、高版本中的gdb可以直接查看vector中的数据,在较低版本中,需要 *(vector.M_impl.M_start)@vector.size() + 这种方法来看 vector 全部内容 + +13、gcc -M test.c 选项可以看到编译时所有include的头文件 + gcc -MM选项是过滤了系统头文件之后的输出 + +14、在用GDB跟踪某些binary时,step比较慢,可以在GDB的命令行中 set env LD_BIND_NOW=true来解决 + +15、在用GDB attach 进程时,可以使用 info sharedlibrary 命令来查看当前加载动态链接库的情况, + 同时也可以使用sharedlibrary 命令来手动加载某些库 + +16、GDB调试C++程序时,如果定位到this指针的位置,那么可以使用 x /10a 命令来查看虚表的内容 + 方便定位问题 + +17、gcc可以编译.cpp文件,但是会遇到 undefined reference 的报错,这是因为没有链接C++的 + stdc++ 库,只要在编译cpp文件时,加上 -lstdc++ 选项,则编译可以正常进行。 + 同时,编译分为如下几步 + cc1 / cc1plus (编译) --> as (汇编) --> collect2 () --> ld (链接) + +18、gcc工具编译链接时(具体是ld工具)查找符号的顺序是从左到右,且.a 或 .o 文件只会被处理一次; + 所以被依赖的库需要放在右边,而项目.o文件需要放在靠左边的位置; + 或者同一库文件,多写几次 -l; + 动态链接库就没有这个问题 + +19、使用 LD_DEBUG 环境变量可以查看可执行文件的链接库/符号查找细节。常用命令如下: + LD_DEBUG=symbols,files $COMMAND + LD_DEBUG环境变量本身支持的取值可以通过 LD_DEBUG=help cat 查看。 + +20、可以通过 cat /proc/${pid}/maps | grep so 的方式来查看进程运行时都加载了哪些so + +21、可以通过如下命令获取进程中各个线程栈 + gdb -c -ex "thread apply all bt" -ex "quit" > output.log + 如果运行的进程是个nostripped版本,也可以尝试使用pstack来抓栈 + +22、jump *0x1234 可以做到调到地址为 0x1234地址的指令处; + 这个指令会忽略中间跳过的代码 + +23、在x86架构下进程core 原因为 illegal instruction时,不能忽略,可以gdb disass 看下当前执行到 + 哪条指令报的错,再然后可以去查对应cpu型号是否支持这个指令 + 这两天就遇到一些较老型号的CPU不支持vinsert128 这个指令导致UT core;vinsert128 是avx2指令集中的一个 + 可以 lscpu | grep -i avx2 看对应cpu是否支持 + 谨记,不可大意,还是可以尝试去定位下 diff --git a/kernel.txt b/kernel.txt index 1bac6e9..e7ccefa 100644 --- a/kernel.txt +++ b/kernel.txt @@ -15,6 +15,7 @@ 6、pthread_mutex 默认情况下 对同一个锁 重复加锁两次 会有什么效果,为什么? 7、pthread_mutex 怎么实现? 使用了哪个系统调用? + (tips:futex) 8、可以在线程中调用fork吗?为什么? @@ -70,6 +71,10 @@    tips:PTRACE_TRACEME & SIGTRAP handler & /proc/self/status 30、简单解释下 vsyscall & vdso ? + (tips:vsyscall是为了解决常用的time,gettimeofday等函数频繁调用时上下文切换过多的问题, + 它提供了一种映射机制,将内核中的时间数据映射到用户态中。 + vdso则更进一步,将这种映射封装为动态链接库,可以利用ADSR机制来对数据进行保护, + 在/proc/$pid/smap中,可以通过dd来将vdso导出得到一个elf格式的库文件) 31、/proc/kcore文件有什么用? @@ -272,13 +277,45 @@ 92、系统级的load数据从哪里来? (tips:uptime等工具可以查看当前系统的load,这个数值正比于 active process 和 处于 uninterruptable状态的进程, - load = old * e + (1-e)*processes,即也会参考前一次的load数据) + load = old * e + (1-e)*processes,即也会参考前一次的load数据 + 所以说,如果通过命令看到系统负载高,不一定是CPU繁忙,也有可能是 + 系统I/O繁忙,更多的进程在等待着从磁盘等设备读取数据, + 系统的负载有两种,一种是CPU,一种是IO,uninterruptable状态的进程就是反馈系统 + 的IO情况) + +93、操作系统中并没有为cgroup添加专用的system call,而是通过 cgroup fs来完成 + 进程的资源隔离操作 + +94、内核会为每个cpu启动一个优先级很高的实时内核线程watchdog/x,尝试每5s调度一次,更新时间戳, + 如果对应线程长时间没有被调度执行,则打印soft lockup告警 + +95、同样内核会为每个CPU启动一个高精度的timer来检测中断的执行是否被长时间阻塞,这个timer默认每4s触发一次来更新时间戳 + +96、kill -0 $pid 可以用来检测给定的进程是否存在(不发送任何实际的signal),对于一些有pid文件的服务来讲, + 可以通过判断 kill -0 `cat /var/xxx.pid` 命令的返回值来确认对应的进程是否存在 + +97、lazy FPU context switch + (tips:Intel CPU 支持lazy FPU context switch的特性,在没有使用FPU的进程进行切换时, + 不用保存涉及浮点数运算的寄存器 + 当进程确实用到了浮点数运算时,会触发一个异常,然后下一次的context switch会保存浮点数寄存器 + 不过这个功能好像有安全风险) + +98、应用层使用spinlock需要注意什么? + (tips:应用层的spinlock在lock时,不关中断,这是与内核实现最大的不同;而这也导致用户态的 + 程序在拿到lock后,可能因为外部中断的原因而delay,这时另一个cpu上的程序就陷入了空转,浪费CPU资源, + 解决办法就是拿锁失败后sleep,而sleep又会有上下文切换的开销,与spinlock的高效不符,当前系统mutex基于 + CAS已实现的足够高效) // 内存管理 mm 1、malloc的实现 + (tips:malloc申请空间时,直觉上进程不会触发sleep逻辑,因为申请的是虚拟内存, + 但是需要注意,内核有可能申请vma_area结构,在申请时用的GFP_KERNEL标志, + 这有可能sleep,所以整体上 malloc 有可能sleep;C++的 new 因为是在malloc之后在对应内存写入数据, + 触发缺页中断,这就有可能导致当前进程sleep,甚至OOM) 2、简单介绍下 tcmalloc 或 jemalloc 或 ptmalloc + (tips:tcmalloc在申请内存失败时,直接abort了当前进程) 3、简单介绍下 buddy system @@ -305,7 +342,8 @@ 位置靠后的CPU访存延迟增高) 13、使用哪个命令来查看NUMA信息? - (tips:numactl命令) + (tips:numactl命令 + numactl --hardware or numactl --show) 14、/proc/interrupts 文件内容解释? @@ -332,6 +370,13 @@ 23、Linux的页表访问分为几个层级?具体实现方式? 24、什么是swap,有什么作用? + (tips:将内存物理页面写回backup storage,减轻内存压力,写回时, + 需要修改页表内容,通过PTE表项可以拿到对应虚拟地址对应的在swap中的 + 物理页面内容。 + 2.6 内核针对多个连续页面内容新增了 extent 属性,可以向文件系统那样 + 操作多个连续物理页面 + 线上有可能不开swap,因为swap会带来额外的磁盘IO,会引起应用进程延迟增加, + 同时,swap的读回又会带来随机IO) 25、什么是内存overcommit? (tips:操作系统向外提供的内存总量比 RAM+Swap 多,这里有一个假设就是, @@ -339,7 +384,10 @@ 还是要申请物理内存,并且要考虑OOM机制的影响) 26、系统可配置的处理overcommit策略都有哪些? - (tips:vm.overcommit_memory和vm.overcommit_ratio proc变量的取值) + (tips:vm.overcommit_memory和vm.overcommit_ratio proc变量的取值 + 具体修改的是/proc/sys/vm/overcommit 和 /proc/sys/vm/overcommit_ration 这两个文件中的内容 + 其中overcommit=0,mmap传入MAP_NORESERVE则不进行参数检查;如果为1,表示允许overcommit; + 如果为2,可以使用的最大虚存为Swap+RAM*overcommit_ration/100,且不允许超过这个限制) 27、怎样判断进程有overcommit行为? @@ -622,8 +670,11 @@ 108、cgroup可以对用户态map的页面进行管理,也可以针对内核使用的内存进行管理, 常见的为tcp,当应用触发limit时,cgroup启动reclaim,如果失败, 则启动OOM(OOM可以配置为不启动,那么进程会hang或者sleep);memory.limit_in_bytes为 - hard limit,对应的有soft limit,超过后cgroup会尽力去回收空间。memsw表示 - memory+swap的空间使用量,在内存空间紧张时,用户态的匿名映射页会swap out。 + hard limit;memory.soft_limit_in_bytes在系统整体内存不紧张时不生效, + 但当系统整体内存紧张后,soft_limit_in_bytes会生效,超额的内存申请会失败sleep。 + memsw表示memory+swap的空间使用量,在内存空间紧张时,用户态的匿名映射页会swap out。 + 在调大memory.limit_in_bytes 之前,需要先把memory.memorysw.limit_in_bytes调大 + 且需要逐级调整,保证整个parent->child链路上的限制都符合预期。 109、内核使用的页面不会swap out到二级存储 (tips:这里还需要明确下具体是哪些页面,例如tcp使用的skb?shm使用的?等, @@ -633,7 +684,10 @@ (tips:因为如果应用没有针对NUMA进行优化,那么有可能父进程在Node0运行, 启动时预分配了运行时需要的的总内存,如果后续spawn的子进程迁移到Node1,这时, 访问原来的内存就需要经过QPI总线,走point-to-point link,导致数据访问延时增加, - QPI总线的数据传输速度低于CPU访问内存的) + QPI总线的数据传输速度低于CPU访问内存的 + 所以在使用NUMA时,对进程绑核是必须操作,减少跨node访存;当某一个numa node内存紧张时 + 早期的实现会回收本node的page cache,造成上层应用延迟升高;当时2014年之后的内核代码已经修复 + 了这个问题,修改为尝试申请其他node的内存) 111、OOM计算的是物理内存的使用,malloc返回非NULL,只是表示分配了虚拟内存, 后续访问时,有可能触发缺页中断,进而引发OOM,一个可取的方法就是malloc之后, @@ -656,6 +710,129 @@ 114、实现atomic指令时的lock前缀,提供了一种保证,后续对相关的数据访问独占进行, 如果之前的指令有修改这个数据,那么在lock指令执行前也会执行写回 +115、prctl可以设置运行进程的名,其实现细节应该是设置task_struct里面的cmd等结构 + +116、free命令显示的 -/+ buffer/cache +表示系统还有多少可用,-表示系统当前已用的, + 通过向 /proc/sys/vm/drop_cache 写入不同的数值,可以强制刷回cache,但是需要注意, + 当前的 共享内存和mmap shared page,都是基于tmpfs实现,且 tmpfs不可以刷回,所以, + 不是所有cache都可以回收。 + +117、使用多线程程序观察到虚拟内存很高,为什么? + (tips: glibc从2.10版本开始引入了一个MALLOC_ARENA_MAX参数来申请thread_local的arena空间, + arena的数量在64位系统下默认为2*cpu cores,对应的内存空间大小为 2*cpu cores * arena size, + arena size为 64M; + 可以通过设置MALLOC_ARENA_MAX环境变量或者通过mallopt函数来控制这一行为) + +118、在/proc/$PID/task目录下可以看到当前进程下各个线程的详细信息 + +119、SIGSEGV信号如果是由进程自己触发,则信号信息中的pid为0 + +120、NUMA与SMP相比,个人认为最大的区别在于拓扑结构上的差异,这些差异带来了 + 跨node内存访问延迟(QPI访问),cache一致性等等的问题。 + (tips:从UMA到NUMA,有三个问题需要解决 + 1、内存的非一致性访问组织 + 2、CPU点对点互连(在SMP架构下,CPU之前不需要互连,大家访问的是公共的内存池,现在 + 有的架构CPU内部各个core以 full mesh结构互连,以避免数据访问时的多跳问题) + 3、可扩展的cache一致性协议,NUMA架构下,每个socket下CPU内部的core + 都有自己独有的L3级cache,L3级cache也是oncore的) + + +121、vfork新建进程时,不拷贝父进程的页表,所以创建进程的速度比较快,且父进程阻塞,直到 + 子进程调用了execve或者 _exit(),注意在vfork创建的子进程退出时,不能使用exit,而要 + 使用_exit(),之所以只能使用_exit,是因为glibc库在调用exit时,会进行一些清理工作, + 会访问到父进程的内存空间,而vfork创建的进程,不能直接访问父进程的数据。 + +122、hugepage的使用有两种方式,一种是使用系统预留的hugepage,数量由 + /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages 文件内容控制, + 使用前需要在上述文件中写入期望系统预留的hugepage数量,然后在mmap等调用中显示的传入MAP_HUGETLB参数。 + 这种预留的内存空间常驻内存不会被swap,常用于和设备的DMA操作,公司里面实现的用户态文件系统和NVMe设备交互时也有采用。 + +123、内核会针对每一个page统计MCE(Machine Check Exception),如果超过阈值,那么会进行 + soft_offline_page,即将出错的页面内容拷贝到其他物理页面,然后将出错的页面下线, + 这一操作对应用透明。 + MCE的log配置文件位于 /etc/mcelog/mcelog.conf + +124、x64程序中,cpu的fs寄存器用于存储stack canary + +125、内核回收内存空间时,可能会导致sys cpu利用率升高,对应的解决办法是查看 /proc/buddyinfo 确认是 + 哪一阶的内存比较紧张。然后可以通过调低vm.min_free_kbytes系统参数来以更高的频率主动触发内核的回收行为。 + 内核管理内存是划分Zone的,具体信息保存在 /proc/zoneinfo 。每一个Zone中都有 high,low,min三个watermark, + 当某个zone中的内存水位低于low时,kswapd会被唤醒,直到水位回到high以上, + 当水位低于min时,大块的内存申请可能会触发direct reclaim或者OOM。 + vm.min_free_kbytes 调整的过低,那么系统回收内存的速度将会非常慢,在运行的过程中,当需要回收内存时,可能会导致死锁。 + vm.min_free_kbytes 调整的过高,那么会导致OOM过于频繁,应用会异常退出。 + 当我们设置了 vm.min_free_kbytes后,内核会根据各个Zone的大小分别计算出min数值。 + 在使用free命令时,我们看到的可用内存空间不一定就可以用来申请,/proc/sys/vm/lowmem_reserve_ratio这个文件 + 里面定义了各个zone中保留的页面比例,当高端内存向低端内存申请内存时,需要判断 free_pages 和 min + reserved_page + 的大小关系,如果小于,那么申请失败(参考mm/page_alloc.c __zone_watermark_ok函数) + 应用可用内存需要减去这个数值。所以在malloc或者mmap失败时,可以查看这个参数,确认下是否配置正确。 + 在线上系统中,可以适当的调整这个数值来适当的增加内核主动回收频率,减少direct reclaim + +126、free命令的各项数据来自于/proc/meminfo这个文件(代码在fs/proc/meminfo.c),而这个文件的内容又是来自于procfs, + 其中buffer来自于sysinfo中的bufferram字段(代码在mm/page_alloc.c),这个字段又是来自于nr_blockdev_pages,即 + 统计devfs中inode结构中i_mapping字段映射的页面数量,一般直接读取设备时,会分配这一数据, + 这也就和之前看的buffer表示块设备文件数据占用内存的说法一致。 + 这部分内存和普通内存的用法一样,如果写入数据,也需要刷回磁盘,如果只读,那么对应内存会被回收(干净页) + +127、/var/log/messages 这个文件记录了系统中很多信息,包括系统启动,关闭,OOM,shell的操作日志等。 + Ubuntu等发行版使用的是 /var/log/syslog 来记录这些信息 + +128、dmesg 或者上面 /var/log/messages有记录进程发生segment fault时的信息,常见的是 segfault at xxx ip xxx sp xxx error xxx + 其中 ip 指向的是当前正在执行的指令,加上 at 后面的偏移量可以得到调用栈中的的caller(栈地址是从大到小,栈顶的地址小),这里记录的 + 地址都可以使用addr2line工具得到对应的代码行信息 + error 是一个位图,记录具体的错误信息 + page fault error code bits: + * + * bit 0 == 0: no page found 1: protection fault + * bit 1 == 0: read access 1: write access + * bit 2 == 0: kernel-mode access 1: user-mode access + * bit 3 == 1: use of reserved bit detected + * bit 4 == 1: fault was an instruction fetch + +129、通常在不可睡眠,或不可调度的上下文中使用GFP_ATOMIC标志调用kmalloc申请内存 + 如果当前zone的内存水位低于min,那么GFP_ATOMIC可以使用到 min/4的量。对应的,如果使用GFP_KERNEL标志, + 则可能会触发direct reclaim + PF_MEMALLOC则是kswapd在回收内存空间时申请空间,所以带这个标志的内存申请必须得到满足 + 可以使用对应的预留空间。 + +130、某个问题中,程序在异常时有一段退出逻辑,但是刚好磁盘hang,导致对应代码段没有load上来 + 程序没有按照预定的逻辑退出,出现问题。 + 这个问题有如下几个启示 + (1)怎么确认代码里面的某个函数,或某几个函数在磁盘文件的哪个block + (2)内核从磁盘读取文件异常时,通常会打印带sector的日志,那么怎么和第一点的信息对应上来, + 即通过sector找到对应block,在匹配到操作的文件 + (3)怎么解决代码段不能load的问题(可以通过mlock来避免对应页面被换出) + +131、NUMA中node是一种物理划分,表示哪些CPU core是一组,可以用numactl --hardware 看到对应的local内存的大小 + 而CPU Sockets表示的是插槽的数量,所以,CPU Sockets和NUMA node数量没有关系,两者可能相等,也可能不相等 + +132、sched_setaffinity在设置时, dest_cpu = cpumask_first_and(cpu_active_mask, new_mask),并尝试将对应的 + thread迁移到目标cpu的runqueue中. + +132、在使用mmap加载磁盘文件时,如果出现越界访问(例如:超过文件长度的访问),或者访问时,其他程序对文件内容 + 进行了截断,那么会报SIGBUS的错误。 + 同时,如果对READ_ONLY的区域,进行写入,则会产生SIGSEGV错误;这与常见的段错误不同;应用层程序尝试写 + 内核区域的内存时,也会产生SIGSEGV + +133、RSS 和 PSS 和 USS + (RSS可以直观的理解为物理内存使用量,PSS在大多数场景和RSS的含义一样,但是在共享库场景,这两个 + 数值不一样,例如一个共享库20K,被20个进程共享,那么PSS的显示就是 1K + USS Unique Set Size则表示进程独享的内存大小) + +134、通过/proc/${pid}/cgroup 可以查看对应进程归属的cgroup组,在对应的cgroup目录下可以查看memory.stat文件 + 确认各项数据分别使用了多少;cgroup限制的内存包括了两部分,RSS和Cache; + 而操作系统的Cache包括了(page cache 、dev buffers、slab,具体数值可以通过/proc/meminfo查看); + 但是需要注意cgroup统计时并不统计slab的使用量 + +135、cgroup内部的回收逻辑在4.19内核之前都是直接回收;但是高版本内核使用了异步回收功能 + 如果回收后,仍然不能满足要求,那么就会触发OOM逻辑 + (在对应进程的memory cgroup目录下查看是否有 wmark* 文件来确认是否支持异步回收功能 + 可以搜搜 memcg异步回收相关文档) + +136、在/var/log/mcelog中可以观察到最近的MCE事件 + +137、'Corrected patrol scrub error' + (tips:Demand scrubbing is the ability to write corrected data back to the memory once a correctable error is detected on a read transaction. Patrol scrubbing proactively searches the system memory, repairing correctable errors. It prevents accumulation of single-bit errors) // 其他 @@ -698,21 +875,23 @@ // shell 1、怎么样使alias永久生效? -2、source 命令有什么作用? - -3、top 命令中CPU使用率高于100,是什么原因? - (tips:机器有多个core,显示的是各个CPU利用率之和) - 4、怎样确定一个shell 命令是builtin的? (tips:使用type命令查看) 5、怎样让系统生成core 文件? - (tips:ulimit -c unlimited) + (tips:ulimit -c unlimited,同时还要关注/proc/sys/kernel/core_pattern是否为空) 6、怎样定制core文件命名规则? (tips:/proc/sys/kernel/core_pattern) 7、Ubuntu系统不再使用/var/log/messages来记录系统事件,现在使用/var/log/syslog来替代 -test -test2 +8、在core文件中一般不会记录memory-mapped I/O pages,有时coredump file size会比VIRT值还大 + ,有可能是内存中的数据swap到了磁盘 + +9、core file在生成时,具体dump哪些内容受到 /proc/${pid}/coredump_filter 影响, + 这个文件的内容是 16 进制的,具体每一bit的涵义参考man 5 core + +10、/proc/${pid}/task/${thread_id}/stack 可以看到一个进程下的各个线程的内核栈 + +11、/proc/${pid}/smaps 可以看到进程使用的内存区域的详细信息,排查问题时有用 diff --git a/mysql.txt b/mysql.txt index 29be194..15a061d 100644 --- a/mysql.txt +++ b/mysql.txt @@ -223,3 +223,6 @@ (tips:依赖于libaio,但不是所有的IO操作都依靠异步,一般只用于read-ahead和write data file pages场景) 100、MySQL show profile, show profile cpu for query 1 查看具体SQL性能数据 + +101、sharding 和 partition的区别,sharding是为了解决单机存储空间不够而对数据进行物理划分, + 而partition是一种逻辑划分,partition中可以有sharding,sharding中也可以有partition diff --git a/network.txt b/network.txt index f46e2f4..fea0676 100644 --- a/network.txt +++ b/network.txt @@ -1,6 +1,20 @@ --- some popular network interview questions,mostly about TCP/IP --- --- TCP/IP 常见网络面试 笔试题 --- +// MAC +1、MAC 层的frame format中有一个CRC32字段,网卡会对源、目的地址、length和DATA计算CRC + 但是,IP/TCP/UDP header中的CRC只计算header本身的数据 + +2、netstat -s 的输出中有 InCsumErrors,这个计数来自于两个文件 /proc/net/netstat /proc/net/snmp, + 内核代码中对于 InCsumErrors 有 ip,tcp,udp的统计 + "InCsumErrors", IPSTATS_MIB_CSUMERRORS + "InCsumErrors", TCP_MIB_CSUMERRORS + "InCsumErrors", UDP_MIB_CSUMERRORS + +3、链路层的crc error可以cat /sys/class/net/eth0/statistics/rx_crc_errors 拿到 + 数据来自于 struct rtnl_link_stats64 + 日常可以使用 ip -s -s link show dev eth0 命令来快速查看 + // IP 1、路由器可以隔离广播域吗? 交换机可以隔离广播域吗? @@ -8,7 +22,7 @@ 2、什么是VLAN? (tips:虚拟局域网) - + 3、什么是VxLAN? (tips:扩展版的VLAN,具体作用后面再补充) @@ -76,6 +90,8 @@ 但像下面nginx的说明一样,在高并发环境下,惊群并不显著) 11、time_wait 状态作用是什么? + (tips: 1、避免老的链接上的数据再次发给新建立的连接,导致数据一致性问题 + 2、如果回复对端FIN的Ack丢失,可以进行重传) 12、怎样避免出现 time_wait @@ -113,7 +129,11 @@ (tips:RPS表示Recieve Packet Steering, RFS 表示 Recieve Flow Steering, RPS依据hash计算数据包软中断处理CPU, RFS在RPS基础上,根据流表来选择用户态收包的CPU - 两个策略都尝试最大化的利用Cache,减少因为上下文切换带来的Cache命中率的下降) + 两个策略都尝试最大化的利用Cache,减少因为上下文切换带来的Cache命中率的下降, + 注意,RPS的实现中需要注意,开启后,并不会减少原来CPU的软中断数量, + 它的工作原理是通过IPI来告诉其他CPU进行软中断处理来收包,对应的CPU的 %soft 会增加, + 留给应用层的CPU时间减少 + RSS 表示 Receive Side Steering 即常说的 网卡多队列,可以通过查看 /proc/interrupts 来确认对应网卡是否有开启) 28、accept 调用返回时,三次握手是否结束? @@ -131,8 +151,17 @@ (tips:TCP_NODELAY用来关闭nagle算法) 35、SO_REUSEADDR 标志有什么作用? + (tips:作用有如下几个 + 1、默认情况下,如果有两个socket,socketA=ipA:portA,socketB=ipB:portB, + 只要ipA 和 ipB不冲突,就可以bind成功, + 如果有一个socket绑定了 0.0.0.0:21,如果不打开SO_REUSEADDR,那么其他socket + 就不能再bind 192.168.0.1:21,同样绑定其他端口也不行,因为地址冲突了。 + 总结而言就是 SO_REUSEADDR可以让0.0.0.0地址和其他地址共同使用) + 2、还有一点就是当socket处于 TIME_WAIT状态时,如果不指定SO_REUSEADDR,那么 + 后续如果有socket想绑定相同的addr:port,就会失败,这里不考虑SO_REUSEPORT) 36、SO_REUSEPORT 标志有什么作用? + (tips:SO_REUSEPORT则条件更为宽松,ip+port都相等也可以,由内核来分配对应的链接sock) 37、SO_REUSEADDR 和 SO_REUSEPORT有什么区别?使用中分别要注意什么? @@ -144,8 +173,12 @@ 40、什么是慢启动? 41、什么是快重传? + (tips:发送端手动按连续三个重复的ack时,认为有数据丢失,立即重传丢失的数据包, + 而不等待RTO,同时,慢启动门限取值为 1/2 的 cwnd,cwnd取值为 慢启动门限+3*数据包大小) 42、什么是快恢复? + (tips:快恢复在上面快重传中其实已经描述了,就是ssthresh取值为 1/2 的cwnd, + 但是,如果遇到RTO,那么cwnd取值为1,sstresh 为 1/2 的遇到RTO时的cwnd) 43、Tahoe 和 Reno 算法中,判断出现丢包的依据是什么? (tips:三次重复的ack) @@ -260,7 +293,8 @@ 89、TCP_DEFER_ACCEPT 标志有什么作用? (tips:设置了这个option的listen socket 只有在连接上真正收到了数据时,listener才会被唤醒, - 这样会减少listener的wake up次数,提高效率) + 这样会减少listener的wake up次数,提高效率 + 客户端socket同样可以设置这个标志,这样会减少一次客户端向服务器发送的ack) 90、sysctl -a | grep "tcp" | grep "re" 中各种重试次数与时间的设置都有哪些作用? @@ -338,8 +372,10 @@ 108、ss 命令看到 处于 listen 状态的 socket 的 Recv-Q 和 Send-Q表示什么意思? (tips:listen状态的Recv-Q表示当前accept队列中等待accept的连接数量, 最大数量受 题目106中所述变量控制,Send-Q表示当前socket的accept backlog取值, - 为 max(listen_backlog, /proc/net/core/somaxconn), - 当命令输出中这两个参数相等时,表示当前accept队列满,考虑调大somaxconn数值) + 为 min(listen_backlog, /proc/sys/net/core/somaxconn), + 当命令输出中这两个参数相等时,表示当前accept队列满,考虑调大somaxconn数值; + 如果是已经建立好的链接,Recv-Q表示内核已经收到但是应用还没有读取的数据量, + Send-Q表示还在SendBuf中,没有ack的数据量,单位是字节) 109、当半连接队列满时,新来的连接的处理方式为? (tips:丢弃新来的连接请求,客户端的connect会超时) @@ -360,8 +396,10 @@ 服务端发送SYN包,服务端回复ACK里面带有seq数据,这样就可以拿到正确的seqid,然后手动构造RST包给服务端) 113、TCP头中的PSH 标志什么意思? - (tips:当接收端收到带有PSH标志的包时,不会继续等待数据,而是直接唤醒上层的socket接收数据, - 所以,PSH一般用在 发送端 已经发送完数据,在最后一个包上带上PSH标志 + (tips: + 接收端收到带有PSH标志的包时,不会继续等待数据,而是直接唤醒上层的socket接收数据 + 发送端看到有PSH标志的包时,不会继续等待后续数据,而是立即将对应数据包发送出去, + PSH一般用在 发送端 已经发送完数据,在最后一个包上带上PSH标志 应用层无法控制TCP头中的PSH) 114、TCP头中的URG 标志什么意思? @@ -429,6 +467,123 @@ 内网环境带宽高,时延低,建议继续使用CUBIC 外网访问可以继续使用BBR +127、当客户端和服务端开启keep-alive特性,且客户端向服务端回复了zero-window, + 那么,带来的效果就是在 服务端一个链接会长时间的存在 + 可以认为这是慢速攻击的一种 + +128、当client回复给server zero window后,server会用 persistence timer来向client发送 + 特殊的keep-alive报文,以确保及时知道client可以正常接收数据 + +129、tcp_max_tw_buckets 这个参数的意思是规定了系统中time_wait状态的socket的数量上限,如果 + 超过这个限制,则time_wait状态的socket会立即关闭,不再等待2MSL。 + +130、TCP关闭连接时使用四次握手,什么时候会变成三次握手呢? + (tips:被动关闭方启用了延时ack机制,收到主动方的 FIN时,没有立即回复ack, + 但应用层同时也没有数据要发送,所以ack 和 fin 一起发送,四次握手变成三次) + +131、epoll触发EPOLLIN 或 EPOLLOUT事件有什么条件吗? + (tips: EPOLLIN事件需要满足sk队列中的 rcv_next - copied_seq >= target,即tcp队列收到的数据量, + 减去已经拷贝到用户态的数据量,大于SO_RECVLOWAT值,这个值默认为1,所以说EPOLLIN事件基本每次都会触发; + 但是上面也只是考虑了tcp sock,还有EPOLL自己的逻辑需要考虑; + EPOLLOUT事件,即socket可写需要满足wmem 空间大于 总量的1/3,这通常用来计算客户端的最小接收速度, + 因为通常情况下,服务端会设置send timeout) + +132、TCP的wmem,不是说一发送数据,就有空间释放出来,因为TCP是可靠协议,需要ack来确保对端收到, + 所以,wmem的释放需要对方ack了数据后才释放。 + +133、socket在多进程分发有两种实现方式 + (1)父进程创建socket,然后fork,子进程拷贝父进程的打开文件表,此时 + socket 结构只有一个,多个进程共享这个socket。这里就存在之前的惊群问题, + 即数据来了后,同时唤醒了多个进程 + (2)通过SOCK_REUSEPORT标志,多个socket可以监听同一个port,这时listen的fd只需要处理 + 新建连接请求,而数据请求由五元组来分发。 + +134、netstat -s 的输出中关于重传的有如下几个重要指标 + segments retransmitted 这个计数统计了所有的重传事件,包括RTO,包括快速重传等等 + 在TcpExt 部分,有一个TCPLostRestransmit 这个表示丢失的重传包数量,这个更能表示网络质量 + 还有一个 fast retransmit,具体可以参考快重传的解释 + +135、在建立连接时,SYN包中的Sequence Number就是Initial Sequence Number, + 这个数字在两端可以不一样 + 这个数字在每次建立连接时,都不一样,这样可以避免前后两次建立连接带来的混淆 + +136、Path MTU怎么确定? + (tips:发送一个大的ip数据包,然后添加IP_DONTFRAG标志,如果超时没有收到ack,那么减少包的大小 + 直到收到ack + 这个和traceroute的原理很像) + +137、TCP允许同时打开一个socket,那允许同时关闭吗?状态流转是怎样的? + (tips: 允许同时关闭,两边的socket都会完整的经历 ESTAB->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT 状态流转) + +138、TCP的接收端怎么确认包丢失? + (tips: 如果收到的包的Sequence Number出现跳变或缺失,则可以判断有数据包丢失) + +139、ssthresh 取值为 INFINITE,在后面的传输过程中,依靠拥塞控制算法,ssthresh会逐渐的靠近准确值 + +140、tcp慢启动过程中的cwnd怎么取值呢,首先尝试从路由信息中获取cwnd,如果没有,那么取系统定义的 + TCP_INIT_CWND,同时还要参考接收方的接收窗口。 + 对于发送小数据量来说,如果链路RTT比较高,那么传输时间所占比例较小,大部分时间花费在了数据 + 传输路径上,这时一个调优选项就是调高 TCP_INIT_CWND值。 + 同时,建议关闭 tcp_slow_start_after_idle。 + 但是关闭这个flag后,有一个问题,就是会带来burst流量,例如,链接空闲一段时间后,突然要 + 发送数据,复用了之前的窗口值,这时数据包在中间路由器被drop的可能性增加(因为比较大)。 + +141、tcp incast是指多对一的数据流向拓扑,即多个源向一个目的写入数据,会导致目的端交换机buffer满, + 出现丢包,反馈到源端就是出现数据重传。 + 解决方案一般是升级到更高buffer容量的交换机,另一方面尝试源和目的就近部署, + 减少网络跳数,还需要考虑数据打散 + +142、socket的REUSEPORT属性在服务端构造了一个多队列多服务模型,与之对应的是早期的fork版本构建了一个 + 单队列多服务模型。 + 单队列模型如果服务阻塞,那么前提是所有服务端都阻塞;多队列如果服务阻塞,那么有可能只是一个服务阻塞, + 所以多队列模型的平均访问延迟分布的更广,有可能个别客户端的延迟非常高。 + (from dog250) + +143、网络模块中的连接表是全局的,在recv中,计算四元组hash,然后在连接表中找到sock, + 完成后续的通信过程 + +144、tcp用到的mem参数有如下几个: + ( + net.core.rmem_default 所有socket的读取buffer默认值 + net.core.rmem_max 所有socket的读取buffer最大值,可以由setsockopt SO_RECVBUF 修改,单位字节 + net.core.wmem_default 所有socket的发送buffer默认值 + net.core.wmem_max 所有socket的发送buffer最大值,可以由setsockopt SO_SNDBUF 修改,单位字节 + net.ipv4.tcp_mem tcp socket 能用到的mem量,分 min pressure max,以page为单位 + net.ipv4.tcp_rmem 单tcp socket 能用到的接收buffer量,分 min pressure max,调用setsockopt修改rmem后,内核的自适应调节功能关闭 + net.ipv4.tcp_wmem 单tcp socket 能用到的发送buffer量,分 min pressure max,调用setsockopt修改wmem后,内核的自适应调节功能关闭 + ) + +145、tcp各个状态简述 + ( + FIN_WAIT_1,主动调用close的一方在发送了FIN包之后的状态,当收到对方的ack后,状态流转到FIN_WAIT_2;如果没有收到ack,那么重新发送FIN包 + FIN_WAIT_2,处于FIN_WAIT_1状态的socket收到FIN的ack后,状态转为FIN_WAI_2,socket在这个状态的超时时间为tcp_fin_timeout,如果系统中有较多的 + 此状态的socket,那么表示对端可能异常,没有正确的调用close + time_wait,主动关闭的一方在收到对方发送的FIN后,回复ACK,状态转为time_wait,在2 msl 时间后,socket 关闭 + close_wait,被动关闭的一方收到对端的FIN包后,状态转为close_wait,注意,这个状态没有超时时间,需要后端配合来释放socket链接 + last_ack,被动关闭的一方发送FIN后,状态转为last_ack,如果没有收到对方的ack,需要重传,如果收到ack,那么socket状态为closed + ) + +146、什么是orphan sockets + (tips:orphan socket可以理解为没有和fd关联的socket,对应TCP中socket的状态为 FIN_WAIT_1,LAST_ACK,TCP_CLOSING, + 其中TCP_CLOSING用于表示同时关闭的场景,即在FIN_WAIT_1状态下同时受到FIN包 + CLOSE_WAIT 不计入 orphan socket + TIME_WAIT 不计入 orphan socket + FIN_WAIT_2 根据 fin_timeout 不同时长,有可能计入 + 如果系统上orphan socket 过多,那么需要统计处于哪个状态的socket较多,再结合[145]来分析) + +147、TCP的SYN报文可以携带数据吗? + (tips:虽然在发送SYN阶段,双方的序列号还没有协商好,但是实现中可以携带数据) + +148、TCP是全双工通信的吗? + (tips:虽然各种文档中都说TCP是全双工的,但是实现上,有如下几点限制了TCP的全双工通信 + 1、接收ACK以及触发后续的数据发送的处理逻辑跑在同一个CPU上 + 2、接收数据以及触发后续的ACK发送的逻辑跑在同一个CPU上 + 3、socket的读写都需要lock) + +149、TCP协议本身的实现就有队头拥塞问题,也可以认为TCP无法提供柔性的降级有损服务 + 而如果想让TCP实现"不可靠传输",则需要在协议层进行修改,但需要考虑整个链路中间节点 + 兼容性情况 + // http 1、什么是CNAME? @@ -467,6 +622,21 @@ 但是,多个HTTP请求必须串行发送,即一个request等待对应response返回后才能继续发送下一个请求, 在HTTP2.0中引入了 Multiplexing机制,这样多个request可以并行发送) +15、标准http header中的字段都是大小写不敏感的,浏览器已经对这个进行了处理, + 在做服务端开发,或者客户端解析时需要注意 + +16、http协议中的Connection header定义了当前Trans完成后,服务端怎么处理当前这个连接。 + 默认为 close,即常见的短链接 + 如果为 Keep-Alive,那么链接将会维持 + http Header中同样有Keep-Alive字段,可以定义timeout 和 max 字段来控制当前链接的超时时间 + 和发送的request数量 + +17、系统本地默认没有DNS cache缓存,即每次使用域名进行访问时,都要进行域名解析;但是libcurl库的 + 实现中有DNS cache,每个handler中默认保存60s + +18、https SNI (Server Name Indacation)机制在TLS客户端握手信息中填入domain,这样在多站点主机中, + 服务端可以正确的下发预期的证书 + // 其他相关 1、iptables实现原理,使用了哪些算法? @@ -496,8 +666,18 @@ 10、netfilter都有哪些hook点? 11、tcpdump工作原理? + (tips:调用socket(AF_PACKET, SOCK_RAW, ETH_P_ALL) 创建一个RAW Socket来接收所有的包) 12、什么是bonding技术?常用的工作模式有哪些? + (bond0 的配置选项可以参考 /etc/sysconfig/network-script/ifcfg-bond0 文件, + 参数中 mode=4 表示采用 802.3ad标准来组成聚合链路来发送和接收数据包,这个需要 + 上游交换机支持;miimon 参数表示 mii 监控的时间间隔,用来监听底层硬件是否正常; + xmit_hash_policy表示采用的hash规则,常用的有 layer3+4 表示利用ip层的源&目的地址和 + TCP层的源&目的端口 + mode=0 是rr策略,有容灾能力 + mode=1 是active-backup策略,主备模式,备网卡对外不可见,有容灾能力 + mode=2 是hash策略,具体由xmit_hash_policy决定 + mode=3 是广播模式,每一个包都发给所有网卡,有容灾能力) 13、网络设备中 什么叫 “MAN Core”? @@ -547,9 +727,9 @@ (tips:上文中提到的TSO只是针对 TCP协议的优化,而GSO这是针对所有上层协议做的一种优化) 31、网络收敛比的概念? - (tips:有两方面的理解,首先是交换机不支持线速转发, - 例如,12个网口,每个口输入1Gbps,但是出口只有8gbps,这时网络收敛比为 3:2。 - 还有一种来自于架构层面,核心交换机万兆,后接12个千兆,则网络收敛比为 10:12,即 1:1.2) + (tips: 硬件方面,交换机不支持线速转发, + 架构层面,核心交换机万兆,后接12个千兆,则网络收敛比为 10:12,其实就是北向和南向能提供的带宽比值, + 理想情况下收敛比应该是1:1,但是这样会增加网络硬件的成本投入) 32、网络数据发送前的操作都有哪些? (tips:1、首先根据ip 和 路由表决定数据从哪个网卡发出 @@ -594,3 +774,20 @@ 这了强调 非阻塞 是因为,如果采用阻塞 system call,则整个server都会block, 无法执行其他逻辑,效率较低,还涉及睡眠唤醒等逻辑, 常见的Nginx Redis Node都采用的 Reactor,分发器则为系统提供的 epoll 等) + +38、ifconfig命令的显示中,frame栏表示未对齐的frame长度(默认按8对齐),而被网卡丢弃; + errors表示CRC校验错误,太短,或者太长的frame; + overruns表示超过网卡处理速度而被丢弃,通常在网络繁忙时出现; + droped则表示访问了未配置的功能,例如,在没有配置ipv6的情况下,收到ipv6报文 + +39、在配置了bond0的机器上用tcpdump 抓包时,不能使用 -i any ,而要指定bond的网卡 + +40、一个进程在运行时,可以确定的知道使用了哪些socket,但是反过来,知道socket却无法明确的得到对应的进程, + 因为socket有可能被多个进程使用。 + 这两者是通过进程的打开文件表关联起来的。 + netstat 命令是通过列举 /proc/net/tcp中的inode,然后和 /proc/$task/fd 下面列举的 socket inode进行比对, + 如果相等,则判断对应进程打开了某一个socket。 + /proc/$task/fd/ 下面的link文件格式有时是 socket:$number 或 pipe:$number,这个数字表示对应socket或pipe的inode number + 即,上面和进程关联时使用 + ss 命令同样可以查看网络socket的信息,但是ss使用的是 netlink,一种更适合于传输大量数据的内核-用户态通信方式, + 但是这两个命令都会 lock sock list diff --git a/nginx.txt b/nginx.txt new file mode 100644 index 0000000..44e24da --- /dev/null +++ b/nginx.txt @@ -0,0 +1,18 @@ +// question collection about awesome nginx + +1、keepalive_timeout + (tips:keepalive连接的超时时间,超过这个时间而没有请求过来,连接会断掉) + +2、keepalive_requests + (tips:一个连接可以发送的请求数量,超过这个数量后,会断开连接) + +3、Nginx的平滑升级通过 execve 实现,过程中不改变进程的open file table, + old master将listen fd写入环境变量,new master进程启动后会尝试读取listen fd, + 然后加入自己的epoll list中,中间还有其他一些信号操作。 + 整体上,平滑升级的关键是listen fd的迁移,其他通信用的fd则采用lazy模式,等待通信完成自然关闭 + +4、Nginx自身没有带日志切分功能,但是可以通过管道的方式将日志输出到cronolog中,日志文件定义中有 + 格式化的字符串,cronolog自动切换文件进行写入,也就完成了日志切割。 + +5、nginx自己定义了一个http 499的错误码,表示在服务端处理超时,未及时返回http resp的情况下, + 客户端主动断开了链接。http标准中并没有定义这个http status diff --git a/nosql.txt b/nosql.txt index 9d110af..23bd1f4 100644 --- a/nosql.txt +++ b/nosql.txt @@ -1,12 +1,52 @@ // NoSql 1、NoSQL 可以水平扩展的原因有? (tips:NoSQL数据模型简单,没有事务支持,没有Join操作) + +2、数据库的索引数据结构在选型时,面临三方面的问题,即RUM(Read Optimization / Update Optimization / Memory space), + 这三个方面分别对应着不同的索引算法实现。 + (1)读优化的数据结构有,hash,trie等 + (2)写优化的数据结构有,lsm,partitioned-B-Tree等 + (3)空间优化的数据结构有,bloom filter等 + 不同的应用应该根据自己的特点进行选择 + 上述细节可以参考RUM的论文 + // Redis 1、Redis中渐进式rehash实现? (tips:避免了集中式rehash导致系统繁忙,将整个rehash过程均摊到每一次的GET PUT 操作中,一步步完成) +2、Redis实现分布式lock的问题在于,如果某个持有锁的client因为GC或者IO操作suspend, + 在恢复后,没办法确认当前的锁状态,当然,在恢复执行后可以再次执行加锁操作,以 + 确认是否持有锁,但是这没有完全避免GC和sleep的可能,还是会有可能持有一个过期的 + 锁,然后写入非预期的数据。 + 一种解决方案就是锁中带有一个单调递增的token,server只允许比当前记录的token大的client的 + 写操作,之前因为sleep而过期的锁会被拒绝写入数据。 + 当前redlock算法依赖于集群中的大多数返回加锁成功,但是当出现网络分区等异常场景时, + 算法没法保证加锁的正确性。 + +3、redis中如果某个key出现热点,怎么解决呢?思路可能有如下几点 + (1)QoS限流,当前也是这么做的 + (2)本地缓存,减少访问redis量 + (3)热点key添加其他信息,如timestamp,这样人为区分开访问节点 + +4、Redis基于LRU实现了一套VM管理机制,原因有如下几个点 + (tips: + 1、Redis管理的数据结构比较复杂,不适合直接用操作系统的page管理 + 2、操作系统的page是4KB大小固定的,但是像Redis中的list结构,有可能包含了很多的node, + 需要由Redis自己来将整个结构进行写回 + 3、Redis如果依赖操作系统的page,由于本身实现是单线程的,前一个请求访问到了在磁盘中的key, + 那么会阻塞后续的所有client访问 + 为了绕过page cache,很多数据库的实现都是自己管理VM,自己实现flush,然后以 O_DIRECT 的模式 + 将数据写回,避免数据的二次拷贝 + ) + +// LevelDB +1、BloomFilter有什么缺点? + (tips: + 1、如果判断一个key不在,那就真的不在,但是如果判断在,那有可能不在 + 2、BloomFilter结构不支持delete操作,不过这个在LevelDB中还好,因为levelDB在SSTable中 + 使用BloomFilter,且SSTable结构是不变的,避开了这一问题,有其他结构提出了改进的操作) // Memcached diff --git a/storage.txt b/storage.txt new file mode 100644 index 0000000..d43e868 --- /dev/null +++ b/storage.txt @@ -0,0 +1,98 @@ +# 存储硬件 or 存储产品相关 +# storage hardware or production related + +1、 NoF(NVMe over Fabrics)的实现中和本地NVMe访问有哪些区别 + (tips: 从如下几点来分析 + (1) 设备的识别,本地盘通过总线系统和完成,而NoF则需要NVMe Qualified Name即NQN来识别 + (2) 设备的发现,本地盘通过总线通知,而NoF需要一种协议模式来发现和连接,有点像TCP + (3) 存储命令的排队模型,本地盘直接使用基于内存的Queue,但是NoF则需要一个Message-based的排队系统 + (4) 数据传输方面,本地盘直接使用PCIe协议中的Physical Region Page,而NoF需要Scatter-Gather List的支持) + +2、SATA3.0 的最大传输速度为6G,但是SAS3.0支持12G,如果直接在SAS口上链接SATA盘,会有带宽浪费的问题 + 所以在SCSI协议层支持多路复用,最大化带宽利用 + +3、当前的深度归档实现的思想在20年前就有了,即MAID(Massive Arrays of idle disk),实现的难点在于 + 调度 + +4、怎么应对磁盘盘符漂移 + (tips:使用e2label程序为某个盘写入label,然后在挂载时,按照label挂载;如果要 + 自动挂载,则将这些信息写入/etc/fstab文件中; + 另,同样可以使用by UUID的方式来) + +5、ext2 ext3 ext4的super block中有字段 s_volume_name 用来保存设置的label,最长16个字节 + +6、当前的FaultTolerant部署模式为啥不采用Active-Active的模式? + +7、SSD的Read Disturb问题 + (tips:SSD由于其自身电器属性,在读取一个paeg时,会影响其他page上的数据,有可能造成bit 翻转等错误 + 解决方法是从硬件层面引入类似RAID方法来在错误出现时纠错 + 另一方变是引入统计信息,统计某一个page或者block的读次数,到达上限后,进行回收) + +8、https://ma.ttias.be/change-reserved-blocks-ext3-ext4-filesystem-linux 根据这里的描述,对于 + 当前越来越大的磁盘来讲,5%的预留空间太大了,可以考虑调小 + 可以继续看下动态调整的影响面 + +9、机械硬盘的磁头旋转角速度固定,单位时间内,可以读取的外道长度较长,读取速度更快; + 早期生产商将外道的磁密度降低来平衡这种差异。 + 但是上述做法的一个缺陷是浪费了磁盘track空间(外圈的sector数量和内圈保持一致)。 + ZBR(Zone Bit Recording)将单位角度内,具有相同数量sector的track组成一个zone,这样外圈的磁密度和内圈保持一致, + 这样做的效果就是数据存储在外圈访问速度高。 + +10、磁盘中可能有多个碟片,但是有一个碟片的一个面是不能保存数据的,生产商用这一面来存储一些硬件信息。 + 例如有两个碟片的磁盘,那么可以用作数据存储只有三个面。 + +11、从2010年起,IDEMA组织完成了 Advanced Format标准的制定,磁盘的physical sector size值扩大到4096 bytes,即4K + 用 hdparm -I /dev/sda 也能查看这一信息 + 使用了上述标准的磁盘,数据读写的最小单位为4K,即小于4K的,写入时,需要考虑Read-Modify-Write带来的放大 + fdisk -l /dev/sda 可以看到一个logical/phsical sector size 的输出,这些内容实际上也是读取的磁盘上的数据 + 在scsi驱动代码中可以看到 blk_queue_logical_block_size -> read_capacity_16 进而发送SAI_READ_CAPACITY_16的 + scsi命令给磁盘,获取 logical_block_size 和 physical_block_size,这两个值怎么理解呢,logical表示磁盘当前可以 + 寻址的最小单位,而physical则表示真实的可以操作数据的最小单位,其他导出的信息还有minimum_io_size + 业界之所以选择4KB sector主要有以下几点考虑 + (1)大容量磁盘背景下,sector越多元数据越多,管理越复杂 + (2)上层业务使用的文件size也变得越来越大 + (3)4KB的sector可以应用更加高效的数据检测与恢复算法 + +12、hdparm -I /dev/sda 的输出中 IORDY 表示一种硬件层面的流控策略,如果不使用,那么在 fast PIO模式下,可能会有 + 数据损坏。 + +13、可以通过命令 dd if=/dev/sda of=/root/mymbr bs=512 count=1 来获取磁盘中第一个sector的数据, + 里面保存有MBR信息,使用 file /root/mymbr 命令可以查看 + +14、MBR 格式的partition table占用64字节,每个partition table entry 占用16字节,所以一个磁盘只能划分出4个parimary partition + 如果要划分多余4个分区,则需要将一个parimary partition格式化为extended partition,然后在这个扩展分区中再划分出 + 多个逻辑分区,且只有逻辑分区可以读写文件 + +15、因为MBR格式中,partition table entry中记录sector数量的是一个4bytes整数,即32bits,那么一个partition最大 + 512 * 2^32 = 512GB,如果有4个partition,那么磁盘最大容量为2TB。 + 所以在当前大容量磁盘广泛使用的今天,MBR格式不再使用,取而代之的是GPT(GUID Partition table) + +16、关于块大小的选择,业界有个1%的阈值,即块寻址的时间占整个写入时间的1%;如果块切分的比较小,那么对应的meta数据多, + 查询空闲块的时间消耗大,如果块很大,则又有内碎片问题 + 当前磁盘的寻址时间普遍在 10ms左右,那么以100MB的速度写入的话,可能的最优块大小即为100M,再考虑2的次方对齐,即 + 128MB + +17、hot spare disk(热备盘)在RAID组中个别盘不可服务时,可以快速替换出问题的盘,保证业务连续性 + +18、PMR 和 CMR含义相同,都表示传统的垂直记录磁盘,特点是磁道与磁道独自;但是磁盘写入时需要的磁场强度高于读取,所以 + 一般情况下写磁头的宽度要大于读磁头,而SMR就刚好利用了这一特点,将相隔的两个磁道进行物理上的重叠, + 这样在相同磁盘尺寸的情况下,可以存储更多的数据 + +19、在数据中心成本计算中,设备(包括存储设置)占用的机架空间也需要考虑 + +20、在文章《Flash Reliability in Production: The Expected and the Unexpected》中,作者提出了如下几个观点 + (1)SLC的可靠性并不比MLC高 + (2)RBER比UBER能更好的预测错误 + (3)RBER和UE(uncorrectable errors)的数量会随着PE的增长而增长 + RBER(Raw Bit Error Rate) + UBER(Uncorrectable Bit Error Rate) + +21、影响SSD可靠性的因素有如下几个 + (1)耐久性,随着SSD PE(program/erase)次数的增加,整个盘的持久性会降低 + (2)Data Retention,即数据保持能力,如果发生电荷泄漏,那么就会导致Read Error + (3)Data disturb,目标cell读取多次后,再次读取时,需要对临近的cell同步施加一个电压, + 但这会造成电荷泄漏,导致Read Fail + +22、在使用 smartctl 查询不到对应nvme设备的 smart-log 后,可以尝试使用 sudo nvme smart-log /dev/nvme 查看 + +23、当前3D TLC是企业级固态存储的主要介质选择 diff --git a/tools.txt b/tools.txt index 97ff2c4..524ebe7 100644 --- a/tools.txt +++ b/tools.txt @@ -8,3 +8,53 @@ this doc record some useful tools to trace problem or optimize performance #No.3 bloom filter caculator 网上有计算 bloom filter 占用空间的公式 + +#No.4 OpenSSL 开源tls库 +检查证书中的域名: +openssl x509 -in certificate.crt -text -noout + +生成自签名证书: +openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout privateKey.key -out certificate.crt + +校验私钥文件正确性 +openssl rsa -check -noout -in key.private + +校验证书文件中的公钥和私钥是否匹配(适用于自签场景) +ssh-keygen -f oss_test.pem -y > key.pub 注意可能需要输入passphrase +openssl x509 -modulus -noout -in key.pub 注意输出 +openssl rsa -modulus -noout -in key.private 查看私钥的module(要和上一条命令输出一致) + +#No.5 rpm 解包 +rpm2cpio ./xxx.x86_64.rpm | cpio -idmv +rpm -ql [package_name] | head -n 100 可以查看对应包按照目录 +rpm -e [package_name] 删除已安装的包 + +#No.6 tcpdump +抓特定来源的HTTP包 tcpdump -A -s 0 'src example.com and tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)' +抓所有HTTP包 tcpdump -A -s 0 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)' + +#No.7 ssh +<1>如果使用ssh登录服务器出现长时间的delay,可以查看/etc/ssh/sshd_config文件中UseDNS是否打开 +如果打开,可以考虑关闭 + +#No.8 +在安装了python的机器上,可以使用如下命令行来检查某个json文件格式 +python -m json.tool your_json_file + +#No.9 curl +curl --local-port 12345 可以指定发出去的数据包的源端口,如果是TCP协议,此时会调用bind来完成地址绑定 + + +#No.10 yum +yum whatprovides */c++config.h -b current 可以查询哪个包提供了对应头文件,便于确定需要安装的包 + +#No.11 make +make -n 只打印make要执行的命令,但不真正执行 + +#No.12 configure +开源软件中的configure脚本依赖 config.guess 和 config.sub;这两个文件需要及时更新,尤其是国产化平台 +需要对应的更新 + +#No.13 gtest +gtest测试框架会catch exception,导致core的堆栈和实际调用栈不匹配,可以在UT binary后加 --gtest_catch_exceptions=0 +参数来禁用exception catch来观察实际问题调用栈,方便定位问题