编写service
1 | [Unit] |
设置service类型为forking。
编写脚本
1 | #!/bin/bash |
由于设置了service类型为forking,需要在我们的脚本的最后执行exit退出systemd
为我们fork的进程。
Say what you do and do what you say
1 | [Unit] |
设置service类型为forking。
1 | #!/bin/bash |
由于设置了service类型为forking,需要在我们的脚本的最后执行exit退出systemd
为我们fork的进程。
ag类似于grep的工具,可以提供更快的搜索。
1 | The Silver Searcher is like grep or ack, except faster. It's written in super-optimized C (like grep) and it's intelligent about entirely skipping files that you don't want to waste time searching (like ack). |
这个问题我已经遇到好几次,每次都要到网上搜索一番,这里记录一下。
1 | 2020/10/26 01:01:33 tcp:127.0.0.1:50610 accepted tcp:beacons.gcp.gvt2.com:443 [proxy] |
v2ray报这个错主要是时间不同步问题,只要把客户端时间设置下就解决了。
随着IPv6的大面积普及,我们普通用户也可以拥有“外网”IP了。有了外网IP,可以在任何支持IPv6的设备上访问我们的“服务”。现在有个问题,分配的IPv6地址不是固定的,可能过段时间就变了,如果能自动把整个地址更新到域名提供商处,即DDNS。我们就可以自由访问我们的服务了。
我的域名是在腾讯云处注册的,其它类似操作。
我从外部找到并修改的脚本,xxxx自动替换成自己的配置。
1 | #!/bin/ash |
获取IPv6的函数需要自己根据情况修改。
给openwrt添加cron任务,让脚本每隔5分钟自动执行,实现DDNS功能。
1 | root@OpenWrt:~# crontab -l |
使用kodi的PVR观看iptv时,画面刷新率不够,到PVR设置页面,把刷新率设置选项关闭,这样电视的刷新率就和系统设置的刷新率一致,不会出现画观看电视面刷新率不够的问题。
这里设置正确的路径否则报无法找到视频地址错误
1 | controller/ytdownloader.php |
参考https://gist.github.com/GAS85/79849bfd09613067a2ac0c1a711120a6
注意要想ocdownloader使用aria2,需要设置不加密方式,还是不太安全
1 | apt install python-pip |
在关于DMA API的内核文档中提到
1 | Note that the DMA API works with any bus independent of the underlying |
推荐使用dma_map_*() 而不是 pci_map_*()。
dma api是独立于总线的接口,使用pci_map_*()是不合适的。
使用该接口时包含相关的头文件,地址使用dma_addr_t。
1 | #include <linux/dma-mapping.h> |
能够用于DMA的内存在大部分平台上是要求物理连续的,因此下面的内存可以用于DMA。
1 page allocator or kmalloc or kmem_cache_alloc
可以直接把这些地址用于dma_map_*()等api
2 vmalloc/vmap地址不可用于DMA,物理上不连续
告诉内核外设对dma的支持情况,不管流式还是一致性dma都要这个步骤
如果有特别的需求,可以使用以下的只设置对应方式的dma
1 dma_set_mask 流式
2 dms_set_coherent_mask 一致性
如果以上设置失败,有两种方式
1 使用非dma方式
2 忽略外设
在驱动初始化时映射好,驱动卸载时接触映射,在外设工作中保证访问的一致性
常用于网卡的环形buffer,scsi的mailbox命令结构,设备固件等。
用于DMA的内存存在于驱动的整个生命周期中。
一致性dma不可避免的使用内存屏障。cpu可能执行指令重排,导致外设首先看到的不是想要的数据。
采用如下的内存屏蔽保证驱动正常工作。
1 | desc->word0=address; |
在某些平台上,驱动刷新写缓存,如写某个寄存器后再都寄存器,需要刷缓存。
流式dma是指在dma传输时才映射,一旦传输完就解除映射。除非使用dma_sync_*调用。
流式dma可以认为是异步dma
常用例子是外设的发送接收buffer,scsi外设读写的buffer。
注意dma地址是共享资源,流式dma会比较节省。
1 DMA API
由于debian下使用kodi不是官方推荐的,所以更新发行版到ubuntu 20.04 lts版。
但是kodi经过HDMI连接电视后声音非常小,把电视上音量调到最大,勉强可以听清。
使用电视自带的系统播放视频,声音正常,可以排除电视的问题。
google搜索相关的问题得到以下信息。
1 | https://discourse.coreelec.org/t/solved-sound-volume-via-hdmi-suddenly-very-low/6045/4 |
解决方法是连接键盘,按+/-键,出现kodi音量调节项,设置到最大音量恢复正常。
但是还有点问题,每次电视关掉再打开声音又变小,必须重启kodi才恢复正常。
这个patch的描述如下:
Previously there is an off-by-one bug on determining when to abort
a stalled window-probing socket. This patch fixes that so it is
consistent with tcp_write_timeout().
从描述看关于何时中止执行窗口探测的socket的。下面从协议规范中寻找定时器使用。

协议中主要有4个定时器,注意这里每个TCP连接都有4个定时器。
TCP uses a time out timer for retransmission of lost segments.
TCP uses a time wait timer during connection termination.
TCP uses a keep alive timer to prevent long idle TCP connections.
TCP uses a persistent timer to deal with a zero-widow-size deadlock situation.
It keeps the window size information flowing even if the other end closes its receiver window.
从patch的描述看主要涉及窗口探测,即Persistent Timer。
Consider the following situation:
发送测收到zero-window-size的ack报文后,发送测不能再发送报文,等待后续接收测更新窗口大小报文通知。为了防止该通知消息被丢的情况,使用
persistent timer,发送测会周期性的窗口探测报文消息,接收测回应该窗口探测消息。如果窗口大小为0,则继续窗口探测过程;非零则恢复数据传输过程。
接下来分析下在linux内核中TCP协议栈所使用的定时器。
在linux内核中每条tcp连接使用一个inet_connection_sock的结构描述。
1 | struct inet_connection_sock { |
sk_timer作为keepalive timer使用,在socket设置SOCK_KEEPOPEN时才启用。
keepalive timer流程如下:
linux内核对keepalive的处理代码如下:
1 | static void tcp_keepalive_timer (struct timer_list *t) |
在上面代码中使用icsk_probes_out对probe次数进行统计。keepalive_probes缺省设置为9。
1 | net.ipv4.tcp_keepalive_probes = 9 |
使用上面的内核选项可以设置该值。
tcp_write_wakeup负责发出probe报文,这个函数首先判断socket发送队列是否
有报文等待发送,如果有则发送sk->sk_send_head报文(注意这个指针总是指向下一个要
发送的报文);如果发送队列空则发送probe报文(注意这个报文的序列号为out of date即无效)。
如果报文能够到达接收测,则接收测回复ack消息。下面是收到ack报文的处理流程:
1 | static void tcp_ack_probe(struct sock *sk) |
如果接收测返回报文通知窗口大小为0则继续窗口探测过程,由于有接收到报文,
这时keepalive超时时判断距离上次报文时间不会超过7200s,这样keepalive定时器
重置为7200s超时。
如果报文无法到达接收测,keepalive在75秒后超时,重新发送probe报文直到到达内核设置的
tcp_keepalive_probes次后终止该tcp连接。
linux内核使用icsk_probes_out统计keepalive的探测次数和zero-window-size的探测次数。考虑下面的
一种情况:
keepalive定时器超时了3次后,才从接收测收到ack消息,这时开始窗口探测过程,这里并没有对icsk_probes_out
清零,而是继续增加icsk_probes_out次数,这样处理有意简化窗口探测的过程。
即重传定时器,对应协议中的Time Out Timer和Persistent Timer。
内核使用一个定时器处理重传定时器和zero-window-size的探测定时器使用。
1 | net.ipv4.tcp_retries2 = 15 |
内核设置重传次数和zero-window-size次数限制为上面的值。


待更新
最近在看内核一个patch的修改,这个patch的commit描述
如下:
While trying to reproduce a reported kernel panic on arm64, I discovered
that AUTH_GSS basically doesn’t work at all with older enctypes on arm64
systems with CONFIG_VMAP_STACK enabled. It turns out there still a few
places using stack memory with scatterlists, causing krb5_encrypt() and
krb5_decrypt() to produce incorrect results (or a BUG if CONFIG_DEBUG_SG
is enabled).
描述里边提到CONFIG_VMAP_STACK内核宏和scatterlist。下面对它们分析。
1 | root@ubuntu:/share/linux# find . -name Kconfig | xargs grep -rsn VMAP_STACK |
从搜索结果看,这个功能是和架构相关的。
再来看下该配置宏的具体描述:
config VMAP_STACK
default y
bool “Use a virtually-mapped stack”
depends on HAVE_ARCH_VMAP_STACK
depends on !KASAN || KASAN_VMALLOC
—help—
Enable this if you want the use virtually-mapped kernel stacks
with guard pages. This causes kernel stack overflows to be
caught immediately rather than causing difficult-to-diagnose
corruption.To use this with KASAN, the architecture must support backing virtual mappings with real shadow memory, and KASAN_VMALLOC must be enabled.
如果内核开启CONFIG_VMAP_STACK,内核可以快速检测内核栈overflow异常,比之前在内核栈溢出
访问时出问题难以诊断,内核栈溢出肯定访问垃圾数据,其结果不可预测的,难以排查,在有个guardpage后
只要内核栈溢出访问,内核可以快速捕获到。
内核栈的申请通过alloc_thread_stack_node实现。
1 | static unsigned long *alloc_thread_stack_node(struct task_struct *tsk, int node) |
为了避免vmalloc的频繁调用,内核使用cached_stacks本地缓存数组,保存进程退出时的内核栈指针。在
创建进程时可以直接从cached_stacks本地缓存数组获取可用的内核栈。
如果cached_stacks本地缓存数组不可用,则通过vmalloc接口申请内核栈。vmalloc申请的空间并不保证是
物理连续的页。
vmalloc申请的空间在使用上要注意:
DMA硬件没有页面映射,需要物理上连续的空间。
后面分析
在不启用CONFIG_VMAP_STACK时,内核使用alloc_pages_node申请内核栈空间。这个接口申请连续的物理页
作为内核栈。
前面分析了内核栈的分配,那么内核栈占多少空间呢?
THREAD_SIZE宏定义了内核栈的大小。
THREAD_SIZE是架构相关的,在x86上定义为2个page的大小。
1 | #define THREAD_SIZE_ORDER 1 |
在x86_64上定义为如下大小,开启KASAN,则分配8个page的大小,不开启KSASAN则定义为4个page的大小。
1 | #ifdef CONFIG_KASAN |
这里提高了KASAN功能,这个是什么功能呢?
Kasan 是 Kernel Address Sanitizer 的缩写,它是一个动态检测内存错误的工具,主要功能是
检查内存越界访问和使用已释放的内存等问题。Kasan 集成在 Linux 内核中,随 Linux 内核
代码一起发布,并由内核社区维护和发展。
这里不对KASAN功能进行分析,更多的可以参考下面的文档。
KASAN扩展阅读
内核栈布局和架构相关,x86配置如下
1 | $ cat arch/x86/Kconfig |
由上可见在x86上,内核栈就是单纯的内核栈,thread_info结构不在内核栈的低端地址上,而是移到了task_struct结构中,这样好处是
内核栈溢出时不会破坏thread_info结构,更加安全。
之前内核栈的布局是这样的,如下所示:
1 | high address low address |
scatterlist用来描述一内存段,其结构如下:
1 | struct scatterlist { |
page_link是page指针,这里复用该page指针使其支持scatterlist chain。
每个scatterlist描述一个物理页的内存片段。
为何vmalloc空间不能用于scatterlist呢?
1 | vmalloc buffer |
如果vmalloc buffer跨page边界,看会发生什么。
1 | $ cat net/sunrpc/auth_gss/gss_krb5_crypto.c |
在sg_set_page后,scatterlist只记录了pageX,后面的pageY是没有记录的,后面使用scatterlist时
可能访问到垃圾数据。可见vmalloc空间时不能用于scatterlist的。
本文分析了CONFIG_VMAP_STACK和scatterlist的功能,在使能CONFIG_VMAP_STACK后,内核栈上分配的
空间是物理上不连续的,不能用于scatterlist,不能用于DMA。