unix/linux
文章平均质量分 73
theanarkh
这个作者很懒,什么都没留下…
展开
-
端口复用之 SO_REUSEADDR
前言:端口复用是网络编程里的经典问题,同时这里面的知识点又非常繁琐,本文通过代码简单介绍一下 SO_REUSEADDR,但不会涉及到 SO_REUSEPORT。长期以来,我们都有一个认知,就是不能监听同一个端口。比如以下代码。server1.listen(8080);server2.listen(8080);我们就会看到 Address already in use 的错误。但是真的不能绑定到同一个端口吗?不一定。#include <stdio.h>#include <std原创 2022-02-04 05:40:34 · 609 阅读 · 0 评论 -
使用 ebpf 监控 Node.js 事件循环的耗时
前言:强大的 ebpf 使用越来越广,能做的事情也越来越多,尤其是无侵入的优雅方式更加是技术选型的好选择。本文介绍如何使用 ebpf 来监控 Node.js 的耗时,从而了解 Node.js 事件循环的执行情况。不过这只是粗粒度的监控,想要精细地了解 Node.js 的运行情况,需要做的事情还很多。在 Node.js 里,我们可以通过 V8 Inspector 的 cpuprofile 来了解 JS 的执行耗时,但是 cpuprofile 无法看到 C、C++ 代码的执行耗时,通常我们可以使用 perf原创 2021-12-18 00:25:53 · 1083 阅读 · 0 评论 -
Linux ptrace 的实现
前言:ptrace 是 Linux 内核提供的非常强大的系统调用,通过 ptrace 可以实现进程的单步调试和收集系统调用情况。比如 strace 和 gdb 都是基于 ptrace 实现的,strace 可以显示进程调用了哪些系统调用,gdb 可以实现对进程的调试。本文介绍这些工具的底层 ptrace 是如何实现的。这里选用了 1.2.13 的早期版本,原理是类似的,新版内核代码过多,没必要陷入过多细节中。1 单步调试ptrace 系统调用的实现中包含了很多功能,首先来看一下单步调试的实现。通过 pt原创 2021-12-04 21:49:46 · 844 阅读 · 0 评论 -
探索 ebpf 在 Node.js 中的应用
前言:ebpf 是现代 Linux 内核提供的非常复杂和强大的技术,它使得 Linux 内核变得可编程,不再是完全的黑盒子。随着 ebpf 的发展和成熟,其应用也越来越广泛,本文介绍如何使用 ebpf 来追踪 Node.js 底层的代码。介绍ebpf 的设计思想虽然很简单,但是实现和使用上非常复杂。ebpf 本质上内核实现了一个虚拟机,用户可以把自己编写的 c 代码加载进内核中执行,从而参与内核的逻辑处理。这听起来很简单,但是整个技术其实非常复杂,从实现来说,内核需要对加载的代码进行非常多而复杂的校验,原创 2021-11-30 23:55:13 · 318 阅读 · 0 评论 -
Linux 内核动态追踪技术的实现
前言:之前的文章介绍了基于 tracepoint 静态追踪技术的实现,本文再介绍基于 kprobe 的动态追踪即使的实现。同样,动态追踪也是排查问题的利器。kprobe 是内核提供的动态追踪技术机制,它允许动态安装内核模块的方式安装系统钩子,非常强大。下面先看一个内核中的例子。#include <linux/kernel.h>#include <linux/module.h>#include <linux/kprobes.h>#define MAX_SYMBO原创 2021-11-15 00:22:06 · 1371 阅读 · 0 评论 -
Linux 内核静态追踪技术的实现
前言:最近在探索 Node.js 调试和诊断方向的内容,因为 Node.js 提供的能力有时候可能无法解决问题,比如堆内存没有变化,但是 rss,一直上涨。所以需要深入一点去了解更多的排查问题方式。而这些方向往往都涉及到底层的东西,所以就自然需要去了解内核提供的一些技术,内核提供的能力,经过多年的发展,可谓是百花齐放,而且非常复杂。本文简单分享一下内核的静态追踪技术的实现。追踪,其实就是收集代码在执行时的一些信息,以便协助排查问题。1 TracepointTracepoints 是一种静态插桩的技术,实原创 2021-11-14 00:50:54 · 1825 阅读 · 0 评论 -
No.js---基于V8和io_uring的JS运行时
前言:阅读Node.js的源码已经有一段时间了,最近也看了一下新的JS运行时Just的一些实现,就产生了自己写一个JS运行时的想法,虽然几个月前就基于V8写了一个简单的JS运行时,但功能比较简单,这次废弃了之前的代码,重新写了一遍,写这个JS运行时的目的最主要是为了学习,事实也证明,写一个JS运行时的确可以学到很多东西。本文介绍运行时No.js的一些设计和实现,取名No.js一来是受Node.js的影响,二来是为了说明不仅仅是JS,也就是利用V8拓展了JS的功能,同时,前端开发者要学习的知识也不仅仅是JS了原创 2021-09-05 17:34:47 · 224 阅读 · 0 评论 -
在Node.js中使用SO_RESUEPORT
前言:今天下载了Node.js最新版代码,并为Node.js增加了SO_RESUEPORT的能力,本文介绍具体的实现,关于SO_RESUEPORT的知识可以参考之前的文章或者网上文章。1 LibuvSO_RESUEPORT是操作系统内核提供的能力,所以第一步首先修改Libuv。考虑到操作系统兼容性的问题,目前只支持Linux系统,旧版Mac OS也支持相关shuxing...原创 2021-07-23 02:43:54 · 287 阅读 · 3 评论 -
编写自己的js运行时第二篇
前言:第一版基于V8实现了一个朴素版的服务器,第二版支持了多进程架构,并且支持了SO_REUSEPORT。本文介绍一下第二版的一些实现,设计上还是比较随意的,目前主要关注功能。首先我们看看第二版怎么使用。1 通过fork共享端口const TCPServer = TCP();const tcpServer = new TCPServer('127.0.0.1', 8989);tcpServer.socket();tcpServer.setReusePort(1);tcpServer.bind(原创 2021-07-10 06:14:01 · 166 阅读 · 2 评论 -
从内核看io_uring的实现---第一篇(基于5.9.9)
前言:本文介绍一下Linux下高性能异步IO框架io_uring在内核的实现,因为io_uring实现代码量大,逻辑复杂,所以只能慢慢分析。这一篇介绍io_uring初始化接口io_uring_setup的实现。io_uring_setup的声明非常简单,但是实现的细节却非常复杂。static long io_uring_setup(u32 entries, struct io_uring_params __user *params){ struct io_uring_params p; int原创 2021-07-07 22:58:46 · 594 阅读 · 2 评论 -
在Libuv中使用io_uring
本文介绍如果在Libuv中使用io_uring。逻辑:1 申请一个io_uring对应的fd。2 初始化一个poll handle,封装1中的fd。3 注册到Libuv的epoll中。4 读取文件列表,给io_uring提交请求5 io_uring完成,1中的fd可读,从而epoll返回。6 Libuv的poll io阶段执行回调。7 回调里获取io_uring的任务完成列表,拿到每个任务关联的请求,执行回调。代码如下(参考https://github.com/shuveb/io_uring原创 2021-07-03 05:56:32 · 466 阅读 · 0 评论 -
io_uring和Node.js
前言:io_uring是大神Jens Axboe开发的异步IO框架,在Linux内核5.1引入。本文介绍什么是异步框架和io_uring的一些基础内容,最后介绍Node.js(Libuv)中,之前有人提但至今还没有合并的一个关于io_uring的pr。1 io_uring介绍在io_uring之前,Linux没有成熟的异步IO能力,什么是异步IO呢?回想我们读取资源的过程,我们可以以阻塞或非阻塞的模式调用read、readv,也可以通过epoll监听文件描述符和事件的方式,在回调里调用read系列函数进原创 2021-07-03 02:17:20 · 283 阅读 · 1 评论 -
提升Node.js性能之SO_REUSEPORT的探讨
前言:多个进程不能同时绑定同一个IP和端口,这是早期Linux内核的一个限制,这个限制给服务器带来了很多不便之处,因为服务器的架构通常不是单进程的,尤其在多核的时代,但是3.9的内核带来了新的特征SO_REUSEPORT。不仅使得服务器的代码逻辑变得简单,对服务器的性能也提升了不少。SO_REUSEPORT的意义是支持同用户下的多个进程同时监听一个IP和端口,本文介绍在Node.js中支持SO_REUSEPORT,以提升Node.js的性能。目前,Node.js的TCP模块还没有支持SO_REUSEPOR原创 2021-06-27 06:10:03 · 242 阅读 · 2 评论 -
从内核看epoll的实现(基于5.9.9)
1 epoll_createSYSCALL_DEFINE1(epoll_create1, int, flags){ return do_epoll_create(flags);}SYSCALL_DEFINE1(epoll_create, int, size){ if (size <= 0) return -EINVAL; return do_epoll_create(0);}原创 2021-06-26 01:50:50 · 340 阅读 · 1 评论 -
从内核看eventfd的实现(基于5.9.9)
eventfd是一种进程/线程通信的机制,我们先看个例子感受一下。#include <sys/eventfd.h>#include <unistd.h>#include <inttypes.h> #include <stdlib.h>#include <stdio.h>#include <stdint.h> int main(int argc, char *argv[]){原创 2021-06-23 23:00:27 · 297 阅读 · 1 评论 -
从内核看socketpair的实现(基于5.9.9)
前言:本文介绍socketpair的实现和通信。Unix域支持服务器、客户端的模式,这种模式的好处就是任意进程都可以和服务器进程通信,这种模式通常需要一个文件路径作为地址,使得任意进程都能通过该文件路径标识找到服务器地址。而socketpair虽然也类似,但它不需要地址的概念,因为它用于有继承关系的进程间通信,通常是主进程调用socketpair拿到两个fd,然后fork出子进程,这样两个进程就可以通信了,不需要寻址的过程,也就不需要地址的概念了。下面我从内核角度看看socketpair的实现。// 分原创 2021-06-19 01:49:59 · 275 阅读 · 0 评论 -
从内核看Unix域的实现(基于5.9.9)
前言:Unix域是进程间通信的一种方式,他的特点是可以传递文件描述符,在内核中,Unix域是网络的一部分,使用上也遵循网络编程的API。本文分析Unix域的实现。我们从unix_net_init开始分析,网络初始化的时候会调用unix_net_init。static int __init af_unix_init(void){ int rc = -1; rc = proto_register(&unix_proto, 1); // 注册协议簇 sock_register(&u原创 2021-06-18 23:24:40 · 276 阅读 · 0 评论 -
从内核看文件描述符传递的实现(基于5.9.9)
文件描述符是内核提供的一个非常有用的技术,典型的在服务器中,主进程负责接收请求,然后把请求传递给子进程处理。下面我们先看一下文件描述符传递到底是什么概念。假设主进程打开了一个文件,我们看看进程和文件系统的关系。如果这时候主进程fork出一个子进程,那么架构如下。我们看到主进程和子进程都指向同一个文件。那么如果这时候住进程又打开了一个文件。架构如下。我们看到新打开的文件,子进程是不会再指向了的。假设文件底层的资源是TCP连接,而主进程想把这个关系同步到子进程中,即交给子进程处理,那怎么办呢?这时候原创 2021-06-17 23:36:43 · 378 阅读 · 2 评论 -
从内核看SO_REUSEPORT的实现(基于5.9.9)
前言:SO_REUSEPORT是提高服务器性能的一个特性,从Linux3.9后支持,本文从内核5.9.9的源码分析SO_REUSEPORT的实现,因为内核源码非常复杂,尽量把自己的思路说一下。大家有兴趣的可以自己研究。首先我们来看看SO_REUSEPORT是什么 SO_REUSEPORT (since Linux 3.9) Permits multiple AF_INET or AF_INET6 sockets to be bound to an原创 2021-06-14 02:24:24 · 379 阅读 · 1 评论 -
Node.js中关于accept时EMFILE的处理
EMFILE表示进程打开的文件描述符达到了上限,比如建立了一个TCP连接后,调用accept函数的时候就可能触发这个错误。那么这个会导致什么问题呢?首先我们看看Node.js是如何处理连接的。void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { uv_stream_t* stream; int err; stream = container_of(w, uv_stream_t, io_watcher原创 2021-06-05 05:51:18 · 422 阅读 · 0 评论 -
阻塞和非阻塞的实现
我们可能都已经听过阻塞非阻塞的概念,本文以tcp中的connect系统调用为例子(基于1.12.13内核,新版的原理类似,但是过程就很复杂了,有时间再分析),分析阻塞和非阻塞是什么并且看他是如何实现的。话不多说,直接开始。static int inet_connect(struct socket *sock, struct sockaddr * uaddr, int addr_len, int flags){ struct sock *sk=(struct sock *)sock-&g原创 2021-06-05 05:50:10 · 521 阅读 · 1 评论 -
内核系统调用的实现和互斥机制
有感于最近在知乎看到了两个问题,分享一下对内核系统调用的实现和互斥机制的认识。下面是这两个问题:https://www.zhihu.com/question/462048846/answer/1919407185https://www.zhihu.com/question/460985657/answer/1912146181系统调用的实现两个问题分别是问了TCP/IP协议和epoll的实现中,内核是否使用了多线程。这个问题的角度挺有意思的,内核虽然在内部使用进程/线程实现了某些功能(比如pdf原创 2021-06-05 05:46:38 · 502 阅读 · 1 评论 -
Node.js的底层原理
前言:本文根据最近做的一次分享整体而成,希望能帮忙大家深入理解Node.js的一些原理和实现。大家好,我是一名Node.js爱好者,今天我分享的主题是Node.js的底层原理。在大前端的趋势下,Node.js不仅拓展了前端的技术范围,同时,扮演的角色也越来越重要,深入了解和理解技术的底层原理,才能更好地为业务赋能。今天分享的内容主要分为两大部分,第一部分是Node.js的基础和架构,第二部分是Node.js核心模块的实现。一 Node.js基础和架构Node.js的组成Node.js代码架构N原创 2021-05-26 04:58:15 · 932 阅读 · 2 评论 -
【Nodejs源码剖析】基于inotify的文件监听机制
Node.js中实现了基于轮询的文件监听机制,基于轮询的监听其实效率是很低的,因为需要我们不断去轮询文件的元数据,如果文件大部分时间里都没有变化,那就会白白浪费CPU。如果文件改变了会主动通知我们那就好了,这就是基于inotify机制的文件监听。Node.js提供的接口是watch。watch的实现和watchFile的比较类似。1. function watch(filename, options, listener) { 2. // Don't make changes directly on原创 2021-05-23 13:31:48 · 270 阅读 · 1 评论 -
线程源码分析之条件变量(基于linuxthreads2.0.1)
条件变量是线程间同步的一种机制,本文分析条件变量的实现和使用。我们先看一下条件变量的定义。typedef struct{ int c_spinlock; /* Spin lock to protect the queue. */ struct _pthread_queue c_waiting; /* Threads waiting on this condition. */} pthread_cond_t;我们看到条件变量的定义很简单,条件变量通常配合原创 2021-05-22 15:18:17 · 366 阅读 · 0 评论 -
好玩的sendfile---探索Node.js中更快的数据传输方式
在Node.js中,当我们给前端返回一个静态文件的时候,我们通常会把文件先读进内容,然后通过socket接口写到底层,从而返回给前端。无论是一次性读取到内存还是使用流式的方式,都不可避免地要把数据从内核复制到用户层,再把数据复制到内核,这是一种低效的方式,因为多了无效的复制。在nginx中,可以通过sendfile指令提供效率。Node.js的copyFile底层使用了sendfile系统调用,但是网络IO的时候,没有使用该API。因为Node.js通过队列的方式,控制数据的写入。那么是否可以实现sendf原创 2021-05-19 05:26:41 · 965 阅读 · 1 评论 -
服务器处理连接的架构演变
服务器是现代软件中非常重要的一个组成。首先我们先来了解,什么是服务器。顾名思义,服务器,重点是提供服务。那么既然提供服务,那就要为众人所知。不然大家怎么能找到服务呢。就像我们想去吃麦当劳一样,那我们首先得知道他在哪里。所以,服务器很重要的一个属性就是需要发布服务信息,服务信息包括提供的服务和服务地址。这样大家才能知道需要什么服务的时候,去哪里找。对应到计算机中,服务地址就是ip+端口。所以一个如果你想成为一个服务器,那么你就要首先公布你的ip和端口,但是ip和端口不容易记,不利于使用,所以又设计出DNS协议原创 2021-05-12 23:06:20 · 160 阅读 · 0 评论 -
捕获TCP/IP协议栈数据包的原理
wireshark或tcpdump相信大家都用过,这些工具看起来都很酷,因为我们平时都是在界面看到应用层的数据,这些工具居然可以让我们看到tcp/ip协议栈每层的数据。本文介绍一下查看tcp/ip协议栈数据的方法。并实现一个简陋的sniffer,通过nodejs暴露出来使用。我们先看实现。#include <stdio.h>#include <errno.h> #include <unistd.h>#include <sys/socket.h>#i原创 2021-05-11 23:56:19 · 591 阅读 · 0 评论 -
从linux5.9看icmp的处理流程
前言:昨天有个同学碰到发送udp包时收到destination unreachable的icmp包问题,本文简单介绍一下linux5.9中icmp包的处理流程。发送icmp包的流程下面以udp为例看看什么时候会发送destination unreachable包。我们从收到一个udp包开始分析,具体函数是udp_rcv。int udp_rcv(struct sk_buff *skb){ return __udp4_lib_rcv(skb, &udp_table, IPPROTO_UDP.原创 2021-05-06 12:22:18 · 454 阅读 · 2 评论 -
从linux5.9看网络层的设计
前言:很久没有看内核的代码了,假期开始看一下,之前看了一下0.11和1.2.13的代码,虽然大致了解了一些原理,但是毕竟比较旧了,再者很多功能还没有实现,比如epoll,所以这次选取的是5.9的版本,再也不怕过时了,当然,现在内核的代码量级非常大,不可能看得完也不可能都看,只是选取自己感兴趣的一些点看一下。看内核代码,总的来说是非常有趣的,不仅是因为知其然知其所以然,而且看到朴素的c语言,还有世界级大佬写代码的思路、思想,设置注释,都是非常有意思的事情。今天分析的内容是从socket函数开始,看看l.原创 2021-05-02 23:02:09 · 158 阅读 · 0 评论 -
cluster模块的设计和实现
我们知道nodejs中实现了cluster模块,实现了服务器的多进程架构下,多个进程可以共同处理请求的能力。本文介绍如何实现一个cluster模块。# 1 轮询模模式下面我们来看一下实现。parent.js```const childProcess = require('child_process');const net = require('net');const workers = [];const workerNum = 10;let index = 0;// 创建原创 2020-12-23 03:05:29 · 355 阅读 · 0 评论 -
通过linux源码分析nodejs的keep-alive
之前已经分析过了keep-alive,最近在使用nodejs的keep-alive的时候发现了遗漏了一个内容。本文进行一个补充说明。我们先看一下nodejs中keep-alive的使用。enable:是否开启keep-alive,linux下默认是不开启的。initialDelay:多久没有收到数据包就开始发送探测包。接着我们看看这个api在libuv中的实现。int uv__tcp_keepalive(int fd, int on, unsigned int delay) { if (set原创 2020-06-25 13:49:12 · 383 阅读 · 0 评论 -
libuv之inotify源码分析
inotify是linux系统提供用于监听文件系统的机制。inotify机制的逻辑大致是1 init_inotify创建一个inotify机制的实例,返回一个文件描述符。类似epoll。2 inotify_add_watch往inotify实例注册一个需监听的文件(inotify_rm_watch是移除)。3 read((inotify实例对应的文件描述符, &buf, sizeof...原创 2020-04-22 00:37:45 · 372 阅读 · 0 评论 -
云风coroutine库源码分析
coroutine库是云风大佬以前写的一个协程库,短小精悍,源码分析在这(https://github.com/theanarkh/read-coroutine-code)。今天就分析一下这个库的原理。话不多说,直接开始。首先了解一下数据结构。// 记录协程公共数据的结构体struct schedule { // 协程的公共栈 char stack[STACK_SIZE]; // 主上...原创 2020-04-21 13:20:26 · 587 阅读 · 0 评论 -
理解服务器设计的基本模式
前言:服务器是现代软件中非常重要的一个组成。今天分享一下服务器设计的一些模式。因为现代的服务器软件中,常见的都是基于TCP的,所以本文的内容也是基于TCP的。 首先我们先来了解,什么是服务器。顾名思义,服务器,重点是提供服务。那么既然提供服务,那就要为众人所知。不然大家怎么能找到服务呢。就像我们想去吃麦...原创 2020-04-18 16:08:20 · 245 阅读 · 0 评论 -
理解协程的实现
glibc提供了四个函数给用户实现上下文的切换。int getcontext(ucontext_t *ucp);int setcontext(const ucontext_t *ucp);void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...);int swapcontext(ucontext_t *oucp, con...原创 2020-04-12 11:25:09 · 323 阅读 · 0 评论 -
用js模拟一下操作系统
var TASKNUM = 10; var STATE = { RUNNING: 0, HANGON: 1, STOP: 2 }; var DEFAULTPRI = 10; var lastPid = 0; function Task(config = {}) { this.pid = lastPid++; this.state = STATE.RUNNING;...原创 2020-04-11 12:46:36 · 278 阅读 · 0 评论 -
io复用之poll源码分析(基于linux2.6.13.1)
poll系统调用是io复用早期的实现,和select、epoll类似。今天来分析一下他的原理。先看一下poll的声明。int poll(struct pollfd *fds, nfds_t nfds, int timeout);再看一下相关的数据结构。struct pollfd { int fd; short events; /* 用户感兴趣的事...原创 2020-04-09 00:57:19 · 170 阅读 · 0 评论 -
读取一个文件的时候,操作系统发生了什么
驱动层是和硬盘控制台打交道的,他负责把文件系统的请求下发到硬盘控制台。和把硬盘的数据提供给文件系统。先看相关数据结构。struct blk_dev_struct { void (*request_fn)(void); struct request * current_request;};struct request { int dev; /* -1 if no request */...原创 2020-04-06 01:48:06 · 901 阅读 · 0 评论 -
理解SIGALRM信号
SIGALRM信号是操作系统中的其中一个信号。他的作用是设置进程隔多久后会收到一个SIGALRM信号。他的功能和定时器很像。下面我们看一下他的原理。 alarm系统调用是设置多久触发SIGALRM信号的函数。下面是他的声明。#include <unistd.h>unsigned alarm(unsigned seconds);...原创 2020-04-05 23:57:20 · 7934 阅读 · 0 评论