c语言
文章平均质量分 68
theanarkh
这个作者很懒,什么都没留下…
展开
-
利用多线程和 C++ 实现一个简单的 HTTP 服务器
无原创 2022-09-07 23:43:25 · 1161 阅读 · 0 评论 -
端口复用之 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 · 608 阅读 · 0 评论 -
No.js 支持 HTTP 模块
前言:No.js 初步支持了 HTTP 能力,目前只是支持解析 HTTP 请求,很多地方还需要慢慢琢磨,本文简单介绍其实现。1 HTTP 解析器No.js 使用 Node.js 的 HTTP 解析器 llhttp 实现 HTTP 协议的解析,llhttp 负责解析 HTTP 报文,No.js 需要做的事情是保存解析的结果并封装具体的能力。看看 No.js 是如何封装 llhttp 的。class HTTP_Parser { public: HTTP_Parser(llhttp_原创 2021-10-04 19:00:31 · 443 阅读 · 1 评论 -
Node.js HTTP 解析器 llhttp 的使用
前言:llhttp 是 Node.js 的 HTTP 1.1 解析器,用于替代早期的http_parser,性能上有了非常大的提升,最近打算在 No.js 里引入 llhttp 来处理 HTTP 协议的解析,本文简单介绍一下如何使用。llhttp 项目是 Node.js 中的子项目,地址在:https://github.com/nodejs/llhttp。使用步骤如下:安装 npx:npm i npx -g执行 ts 生成 c 代码:npx ts-node bin/generate.ts,或者执行原创 2021-10-03 11:39:07 · 506 阅读 · 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 评论 -
JS运行时Just源码解读
前言:本文介绍一下新的JS运行时Just的一些设计和实现。1 模块的设计像Node.js一样,Just也分为内置JS和C++模块,同样是在运行时初始化时会处理相关的逻辑。1.1 C++模块Node.js在初始化时,会把C++模块组织成一个链表,然后加载的时候通过模块名找到对应的模块配置,然后执行对应的钩子函数。Just则是用C++的map来管理C++模块。目前只有五个C++模块。 just::modules["sys"] = &_register_sys; just::modules原创 2021-08-26 23:52:42 · 566 阅读 · 0 评论 -
从内核看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 评论 -
通过N-API使用Libuv线程池
Node.js不适合处理耗时操作是一直存在的问题,为此Node.js提供了三种解决方案。1 子进程2 子线程3 Libuv线程池前两种是开发效率比较高的,因为我们只需要写js。但是也有些缺点1 执行js的成本2 虽然可以间接使用Libuv线程池,但是受限于Node.js提供的API。3 无法利用c/c++层提供的解决方案(内置或业界的)。这时候我们可以尝试第三种解决方案。直接通过N-API使用Libuv线程池。下面我们看看这么做。N-API提供了几个API。napi_create_asyn原创 2021-06-05 05:41:29 · 1451 阅读 · 0 评论 -
通过源码理解IGMP v1的实现(基于linux1.2.13)
IGMP是组成员管理协议,我们知道一般的通信是单播的,虽然主机发出的单播报文,局域网中的每个主机都会收到,但是默认情况下,主机只会处理目的ip是自己的报文。如果我想让多个主机都可以处理我发出的报文怎么办呢?这就是IGMP做的事情。他定义了组的概念,我们可以使用多播的方式,给一个组发送报文,属于这个组的主机都可以处理这个报文。下面我们看看多播是怎么实现的。首先我们看一下网络架构。ip地址中给多播预留了一段范围的ip。IGMP的一个多播组其实就是一个多播ip。主机记录了本主机加入的多播组信息。组播路由记录了原创 2020-09-16 00:07:03 · 865 阅读 · 0 评论 -
nodejs创建线程问题
我们知道在nodejs中可以使用new Worker创建线程。今天有个同学恰好问到,怎么判断创建线程成功,这也是最近开发线程池的时候遇到的问题。nodejs文档里也没有提到如何捕获创建失败这种情况。所以只能通过源码去找答案。不过坏消息是,我们无法捕获这个这个错误。下面看一下源码。我们直接从c++层开始分析。当我们调用new Worker的时候,最后会调用c++的StartThread函数(node_worker.cc)创建一个线程。CHECK_EQ(uv_thread_create_ex(&w-原创 2020-08-12 23:14:10 · 878 阅读 · 0 评论 -
发布你的第一个nodejs c++插件
之前分享了如何写一个nodejs的c++插件。今天分享一下如何发布一个c++插件,github:https://github.com/theanarkh/learn-to-write-nodejs-addons环境1 ubuntu18.04。2 安装nodejs v12和npm install node-gyp -g。写代码写一个测试的例子。test.cc// hello.cc using N-API#include <node_api.h>namespace demo {原创 2020-08-08 17:35:20 · 303 阅读 · 0 评论 -
通过源码理解http层和tcp层的keep-alive
前言:最近在研究websocket和keep-alive。而websocket涉及到长连接,过多无用的长连接对系统来说是负担,是否可以尽快发现对端是否已经掉线,从而释放这个连接来减少系统压力呢,就这个初衷,通过wireshark和nodejs调试一下心跳机制。引发了一些研究和思考。我们知道建立tcp连接的代价是比较昂贵的,每次连接需要三次握手,还有慢开始,或者建立一个连接只为了传少量数据等都影响了效率。这时候如果能保持连接,那会大大提高效率。但是如果一直保持连接,又没有数据或者有一端已经断网则会浪费资源,原创 2020-07-25 20:56:21 · 256 阅读 · 0 评论 -
尝试为nodejs贡献代码
1 需求背景一直都有在看一些开源项目的代码,但是还没有试过提交pr。因为最近在研究websocket和keep-alive。而websocket涉及到长连接,过多无用的长连接对系统来说是负担,是否可以尽快发现对端是否已经掉线,从而释放这个连接来减少系统压力呢,就这个初衷,想通过wireshark和nodejs调试一下心跳机制,但是发现nodejs对这个的支持不是很好。tcp的心跳机制,支持三个配置,但是nodejs的setKeepAlive只支持一个配置(后面发现最新版代码里有一点支持的痕迹了,但是没有给原创 2020-07-04 15:26:11 · 260 阅读 · 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 评论 -
nginx的timeout(基于nginx1.17.9)
nginx中使用timeout的地方非常多,本文主要分析客户端和nginx通信时涉及到的几个timeout。连接建立成功,接收业务数据超时接收http报文的超时1 连接建立成功,接收业务数据超时这个逻辑从ngx_event_accept函数开始分析,ngx_event_accept是nginx在监听某个端口时,底层建立tcp连接成功后回调的函数。我们首先需要了解,在nginx中。一个连接是使用ngx_connection_t表示。每个ngx_connection_t对应两个ngx_event_t原创 2020-06-14 23:19:37 · 497 阅读 · 0 评论 -
ngx_http_auth_basic_module源码解析(基于nginx1.17.9)
auth_basic模块是nginx中比较简单的模块。地址在http://nginx.org/en/docs/http/ngx_http_auth_basic_module.html。我们通过分析这个模块的代码,不仅知道如何使用,还可以了解到http认证的实现。该模块支持http认证和验证的功能。支持两个配置。location / { // 在浏览器弹框里的提示语 auth_basic "hello"; // 用户输入密码后,以password文件来判断是否正确原创 2020-06-14 17:22:39 · 348 阅读 · 0 评论 -
通过源码理解http层和tcp层的keep-alive
很久没更新文章了,今天突然想到这个问题,打算深入理解一下。我们知道建立tcp连接的代价是比较昂贵的,三次握手,慢开始,或者建立一个连接只为了传少量数据。这时候如果能保存连接,那会大大提高效率。下面我们通过源码来看看keep-alive的原理。本文分成两个部分http层的keep-alivetcp层的keep-alive1 http层的keep-alive最近恰好在看nginx1.17.9,我们就通过nginx来分析。我们先来看一下nginx的配置。keepalive_timeout timeo原创 2020-06-13 01:46:10 · 187 阅读 · 0 评论 -
libuv之unix域的使用
之前分析了unix域在libuv的基本原理。今天以一个简单的例子看一下如何使用它。本文涉及到一些网络编程的知识,不过文章不打算讲解这些,如果不了解可以先了解一下,或者留言。void remove_sock(int sig) { uv_fs_t req; // 删除unix域对应的路径 uv_fs_unlink(loop, &req, PIPENAME, NULL); // 退出进程 exit(0);}int main() { loop = uv_原创 2020-05-13 00:27:13 · 378 阅读 · 0 评论 -
libuv源码分析之unix域
unix域是一种基于单主机的进程间通信方式。实现模式类似tcp通信。今天先分析他的实现,后续会分析他的使用。在libuv中,unix域用uv_pipe_t表示。struct uv_stream_s { // uv_handle_s的字段 void* data; // 所属事件循环 uv_loop_t* loop; // handle类型 uv_handle_type type; // 关闭handle时的回调 uv_close_cb c原创 2020-05-12 23:09:54 · 323 阅读 · 0 评论 -
理解libuv的基本原理
libuv的实现是一个很经典生产者-消费者模型。libuv在整个生命周期中,每一次循环都执行每个阶段(phase)维护的任务队列。逐个执行节点里的回调,在回调中,不断生产新的任务,从而不断驱动libuv。今天我们分析一下libuv的整体架构,从而学会如何使用libuv。我们从libuv的一个小例子开始。#include <stdio.h>#include <uv.h>int64_t counter = 0;void wait_for_a_while(uv_idle_t*原创 2020-05-08 23:35:22 · 759 阅读 · 0 评论 -
libuv小册之线程池篇
最近开始写小册子,一篇篇来,写完了再整理总结到一起。Libuv是基于事件驱动的异步io库,他本身是一个单进程单线程的。但是难免会有耗时的操作。如果在Libuv的主循环里执行的话,就会阻塞后面的任务执行。所以Libuv里维护了一个线程池。他负责处理Libuv中耗时的操作,比如文件io、dns、用户自定义的耗时任务。文件io因为存在跨平台兼容的问题。无法很好地在事件驱动模块实现异步io。下面分析一下...原创 2020-05-04 14:51:21 · 747 阅读 · 0 评论 -
libuv源码分析之stream第二篇
上一篇分析了流的基础知识和读写操作的实现。今天继续分析。1 关闭流的写端// 关闭流的写端int uv_shutdown(uv_shutdown_t* req, uv_stream_t* stream, uv_shutdown_cb cb) { // 流是可写的,并且还没关闭写端,也不是处于正在关闭状态 if (!(stream->flags & UV_HANDL...原创 2020-05-03 16:19:18 · 256 阅读 · 0 评论 -
libuv源码分析之stream第一篇
流的实现在libuv里占了很大篇幅,今天分析一下流的实现。首先看数据结构。流在libuv里用uv_stream_s表示,他属于handle族。继承于uv_handle_s。struct uv_stream_s { // uv_handle_s的字段 void* data; // 所属事件循环 uv_loop_t* loop; // handle类型 ...原创 2020-05-02 16:14:51 · 430 阅读 · 0 评论 -
通过tinyhttpd-0.1.0源码理解服务器原理
tinyhttpd是一个demo版的服务器。代码几百行。源码分析在这。从中可用一窥服务器的基础原理。他采用的是一个请求新开一个线程处理的方式。里面涉及了多进程、多线程、进程间通信等知识。 我们从main函数开始分析。int main(void){ int server_sock = -1; u_short port = 0; int cli...原创 2020-04-10 00:51:55 · 265 阅读 · 0 评论 -
线程源码分析之信号量(基于linuxthreads2.0.1)
/* Semaphores a la POSIX 1003.1b */#include "pthread.h"#include "semaphore.h"#include "internals.h"#include "restart.h"#ifndef HAS_COMPARE_AND_SWAP/* If we have no atomic compare and swap, fa...原创 2019-10-18 01:30:03 · 170 阅读 · 0 评论 -
文件系统源码分析之inode.c
inode.c主要是管理文件系统中inode结构的,功能包括,把硬盘的inode读入内容,把内存的inode内容写入硬盘,或内存的inode表中获取一个空的或者指定的inode,生成一个管道的inode,查找inode中的某个块,或者在inode中生成新的块。/* * linux/fs/inode.c * * (C) 1991 Linus Torvalds */#include...原创 2019-01-09 01:54:14 · 1561 阅读 · 0 评论 -
文件系统源码解析之bitmap.c
bitmap.c主要是利用inode位图和数据块位图管理硬盘中的inode和数据块的使用。总共四个函数,分别是新建、销毁inode,新建、销毁数据块四个函数。销毁的时候只是把位图清0,然后把位图的数据回写硬盘,并没有清除硬盘上的数据。新建的时候,主要是先把位图置1,然后获得一个buffer(没有数据)或者inode(有数据,inode的一些属性),然后等待写入硬盘。/* * linux/fs...原创 2019-01-09 01:20:56 · 841 阅读 · 0 评论 -
文件系统源码分析之buffer.c
buffer是在内存里开辟一块空间做缓存,他是应用层和硬盘之间的一层缓存,主要是为了不用每次都访问硬盘,提高效率。缓存的结构由两部分组成,一个是哈希链表,一个是双向循环链表,第一个链表是使用数据的时候为了快速找到对应的buffer,第二个链表是为了找可用的buffer。buffer的操作主要是从buffer池中找到一个空闲的结构,然后请求底层,他的下一层是io调度层,buffer的读写都是发请...原创 2019-01-09 00:42:03 · 583 阅读 · 0 评论 -
tinyhttpd-0.1.0源码解析
/* J. David's webserver *//* This is a simple webserver. * Created November 1999 by J. David Blackstone. * CSE 4344 (Network concepts), Prof. Zeigler * University of Texas at Arlington *//* Thi...原创 2019-01-23 23:21:57 · 268 阅读 · 0 评论 -
libuv之文件监听---fs-poll.c
#include "uv.h"#include "uv-common.h"#include <assert.h>#include <stdlib.h>#include <string.h>struct poll_ctx { uv_fs_poll_t* parent_handle; /* NULL if parent has been sto...原创 2018-12-08 03:02:25 · 673 阅读 · 0 评论 -
libuv之定时器---timer.c
#include "uv.h"#include "uv-common.h"#include "heap-inl.h"#include <assert.h>#include <limits.h>// 取出loop中的计时器堆指针static struct heap *timer_heap(const uv_loop_t* loop) {#ifdef _W...原创 2018-12-08 00:35:44 · 1061 阅读 · 0 评论 -
操作系统的fork
fork函数的特点,我们听得最多的可能是执行一次返回两次,我们可能会疑惑,执行一个函数怎么可能返回了两次呢?下面从linux0.11的代码去看一下fork函数的实现(1.2.xx版本也可以,2.6的已经改动很大了),0.11中,我们执行fork新建一个进程的时候,调用的底层api是copy_process,这个函数最后返回新建进程的id给调用函数,所以我们fork的时候,父进程fork函数是返回了...原创 2018-05-13 03:30:19 · 404 阅读 · 0 评论 -
关于c语言中删除单向链表节点的问题
二话不说先上代码,这是摘自网上的一段代码,用以解读题目所说的问题。// 假设h为指向链表头结点指针,那么head就是指向h指针的指针void delete(node ** head){ for (node** curr = head; *curr; ) { /* curr指向h的地址,所以*curr的值是h内存里存的东西,也就是 第一个节点原创 2017-07-29 02:57:10 · 983 阅读 · 0 评论 -
nginx数据结构之ngx_list分析(nginx0.1.0)
/* * Copyright (C) Igor Sysoev */#ifndef _NGX_LIST_H_INCLUDED_#define _NGX_LIST_H_INCLUDED_#include <ngx_config.h>#include <ngx_core.h>typedef struct ngx_list_part_s ngx_lis...原创 2019-02-26 23:00:43 · 290 阅读 · 3 评论 -
linux系统调用之read源码解析(基于linux0.11)
进程通过系统调用,从而进入中断处理,中断处理从系统调用表里找到sys_read函数执行。int sys_read(unsigned int fd,char * buf,int count){ struct file * file; struct m_inode * inode; if (fd>=NR_OPEN || count<0 || !(file=current->...原创 2019-05-03 17:55:21 · 1461 阅读 · 0 评论 -
linux信号处理源码分析(基于linux0.11)
linux的信号处理时机在系统调用结束后。这里以fork系统调用函数为例子讲解这个过程。下面是fork函数的定义。_syscall0(int,fork)宏展开#define _syscall0(type,name) \type name(void) \{ \long __res; \__asm__ volatile ("int $0x80" \ // 输入输出都是eax,输入是系...原创 2019-08-29 01:02:29 · 497 阅读 · 0 评论 -
操作系统定时器原理分析(基于linux0.11)
操作系统的定时器原理是,操作系统维护了一个定时器节点的链表,新增一个定时器节点时,设置一个jiffies值,这是触发定时中断的频率。linux0.11版本里是1秒触发100次,即10毫秒一次。加入新增一个定时器的jiffies值是2,那经过两次定时中断后就会被执行。jiffies值在每次定时中断时会加一。_timer_interrupt: push %ds # save ds,es and ...原创 2019-08-27 00:31:56 · 888 阅读 · 0 评论 -
理解进程的退出(基于linux0.11)
int sys_exit(int error_code){ return do_exit((error_code&0xff)<<8);}int do_exit(long code){ int i; // 释放代码段和数据段页表,页目录,物理地址 free_page_tables(get_base(current->ldt[1]),get_limit(0x...原创 2019-05-04 17:41:07 · 1568 阅读 · 0 评论 -
linux系统调用之sys_close(基于linux0.11)
进程PCB中有一个指针数组,文件描述符是数组索引,每个元素指向一个file结构体,file结构体有一个字段指向文件对应的inode。关闭一个文件主要的步骤是1 根据文件描述符,把指针数组对应项置空。2 如果指向的file结构也没有其他进程使用了,则file结构体可以重用。但是他指向的inode节点需要回写到硬盘。具体参考iput函数。// 解除文件描述符->file结构体->i...原创 2019-05-04 17:16:31 · 1359 阅读 · 1 评论 -
linux系统调用之sys_unlink(基于linux0.11)
sys_unlink是删除硬链接的系统调用,引用数为0时底层的文件会被删除sys_unlink是删除硬链接的系统调用,引用数为0时底层的文件会被删除// 删除硬链接int sys_unlink(const char * name){ const char * basename; int namelen; struct m_inode * dir, * inode; struct bu...原创 2019-05-04 16:50:25 · 1695 阅读 · 0 评论