分类 Linux 相关 下的文章

移动光猫探索

移动光猫 H2-2

解锁 telnet 教程: https://www.cnblogs.com/dingshaohua/p/17388270.html

查看开启端口和服务:

$ nmap -O -sV 192.168.1.1
Starting Nmap 7.80 ( https://nmap.org ) at 2025-01-15 10:33 GMT
Nmap scan report for 192.168.1.1 (192.168.1.1)
Host is up (0.0031s latency).
Not shown: 996 closed ports
PORT     STATE SERVICE
23/tcp   open  telnet     BusyBox telnetd 1.00-pre7 - 1.14.0
80/tcp   open  http       Mini web server 1.0 (ZTE ZXV10 W300 ADSL router http config)
5080/tcp open  onscreen?
8080/tcp open  http-proxy
17998/tcp open  unknown
17999/tcp open  unknown

Service Info: OS: Linux 2.4.17; Device: broadband router; CPE: cpe:/h:zte:zxv10_w300, cpe:/o:montavista:linux_kernel:2.4.17
$ uname -a
Linux zxic 4.1.25 #3 SMP PREEMPT Fri Oct 13 16:33:00 CST 2023 armv7l GNU/Linux

#cpu
/ # cat /proc/cpuinfo
processor       : 0
model name      : ARMv7 Processor rev 1 (v7l)
BogoMIPS        : 1987.37
Features        : half thumb fastmult edsp tls
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x4
CPU part        : 0xc09
CPU revision    : 1

processor       : 1
model name      : ARMv7 Processor rev 1 (v7l)
BogoMIPS        : 1993.93
Features        : half thumb fastmult edsp tls
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x4
CPU part        : 0xc09
CPU revision    : 1

Hardware        : ZTE ZX279128S (Device Tree)
Revision        : 0000
Serial          : 0000000000000000

# 内存
/ # free -m
             total         used         free       shared      buffers
Mem:           450          243          207            6            0
-/+ buffers:                243          207
Swap:            0            0            0

谁打开的 80, 5080, 8080?

/proc/1214 # netstat -tlnp | grep 80
tcp        0      0 192.168.1.1:80          0.0.0.0:*               LISTEN      1214/httpd
tcp        0      0 :::8080                 :::*                    LISTEN      1750/java
tcp        0      0 fe80::1:80              :::*                    LISTEN      1214/httpd
tcp        0      0 ::ffff:192.168.1.1:5080 :::*                    LISTEN      1750/java

关于 80 端口, 也就是管理页面, 使用的是 apache http server.

bash 里 I have no name!

通常情况下, 我们启动 Docker 都是以 root 为当前用户启动 container 的. 但是我们也可以设置以特定的用户来运行 container 里面的主进程.

  1. 加上 --user 参数

    # 这里指定一个不存在的 user id 1000 来执行. 
    docker run --name noNameTest --user 1000:1000 neo4j:latest
  2. 在 Dockerfile 里面设置用户

    USER <user>[:<group>] # 用户名:组名
    USER UID[:GID] # 用户ID:组ID

但是如果你登录进入该 contianer 却看到你的用户名是: I have no name!, 那说明你的 Shell (Bash 或其它) 再向你抱怨, 它无法找到你当前的用户名. 如下面:

I have no name!@my_container:$ 

I have no name!@my_container:$ id
uid=1000 gid=1000 groups=1000
I have no name!@my_container:$ whoami
whoami: cannot find name for user ID 1000

这是因为 Shell 通常去 /etc/passwd 去找这个用户ID, 却找不到.

在 Kubernetes 里面这样以非 root 用户去运行, 可以最小化权限, 防止特权升级, 提高安全性和隔离性.当容器以不存在的用户 ID 和组 ID 运行时,通常这些用户没有在容器内配置 sudo 或 su 的权限,也没有相应的配置文件(如 /etc/sudoers)来允许权限提升.

在 Dockerfile 中使用 USER 指令指定非 root 用户。
在 Kubernetes Pod 的安全上下文(SecurityContext)中指定 runAsUser 和 runAsGroup 来配置非 root 用户运行。

汇编指令把数据从内存一块区域复制到另外一块区域

最近再次阅读 CSAPP 里面的有关汇编的一节, 它说到了 mov 指令, mov 可以把常量, 寄存器的值, 内存的值移动(move)到寄存器, 内存. 但是不能直接把某个地址内存的值移动到另外一个内存地址. 为什么会有这个限制?

如果考虑到当前的值要做计算, 那么这个值从内存加载到CPU, 然后做计算, 之后再保存到内存. 看上去没啥性能损失, 但是假如只是把某个内存的数据复制(移动)到另外一个内存地址, 那么这个性能损失就很大.

为什么会这么想, 这是因为在 这本书里提到这么一个有关延迟度量的对比:
latency scale.png

可以看到从内存加载, 然后再保存到内存需要的相对时间确实很长, 如果有一个方案直接告诉内存控制器, 把某地址开始的多少数据直接复制到另外一块区域, 那将是飞速提升, 因为只要CPU发出一个内存控制器的指令就可以. 但是这看上去很美, 因为内存控制器并不知道进程虚拟内存这些东西, 它只知道硬件物理内存. 虚拟内存要写的2个连续字节可能在物理内存上是相隔很远的两块地方.

如果一个内存数据并不需要做算术逻辑运算, 那么可以使用专门的字符处理指令 movs, 它有对应不同宽度的: movsb, movsw, movsd, movsq. 它们的使用会涉及到 RSI, RDI, RCX 寄存器, 以及 DF flag, 并且结合 REP 指令来实现连续复制.

但是这里我更关心的是, 这些被移动的字节是不是要到CPU绕一圈? 也就是说这些字节是不是需要占用系统总线(system bus). 根据我互联网上查到的数据, 这些字节通常情况下都需要到 CPU 绕一圈, 涉及到加载(load)和保存(save). 也就是避免不了性能的延迟. 但是如果要移动的数据已经在缓存 (L1, L2, L3), 那么将不需要走系统总线加载. 同时这些加载可能都是成块加载, 所以通常连续的内存区域都会同时被一次加载, 提高了性能. 另外从缓存刷新到内存也是成块(页)刷新到, 那么它也会提高性能.

go语言把部分CPU从 AVX 迁移到 REP movsb - https://github.com/golang/go/issues/66958

有关 memcpy

Linux内核中的memcpy函数是一个用于内存复制的函数,它的主要功能是将一块内存中的数据原封不动地复制到另一块内存中。这个函数在内核编程中非常重要,因为它直接操作内存,效率要求非常高。

声明在 string.h 中
void *memcpy(void *dest, const void *src, size_t n);

为了提高效率,Linux内核中的memcpy通常实现为汇编语言,特别是对于小数据块的复制,会有特别的优化。对于更大的数据块,它可能会利用CPU的特定指令(如SSE/AVX指令集)来提高复制速度。

memcpy 可以根据CPU 的指令集选用 movs 系列的指令, 也可以选择 AVX 或 SIMD 来提高在该架构上的性能.

summary

从内存到内存的复制, 还是需要占用系统总线的.

Linux source 命令是 bash 的 builtin

今天执行一个 shell 脚本, 发现 shell 里面报错: source: not found. 可是去执行 source 命令, 发现又有. 到底怎么回事呢?

原来 source 是 bash 的 builtin 命令, 而我运行这个脚本使用的是 sh script.sh, 它其实使用的是 /usr/bin/sh, 然而在这个 Ubuntu 上面 /usr/bin/sh 其实是指向了 /usr/bin/dash. 然而 source 这个命令在 dash 没有.

supra@suprabox:~$ which sh
/usr/bin/sh
supra@suprabox:~$ which bash
/usr/bin/bash

# 没有找到 source 命令
supra@suprabox:~$ which source

# source 的类型
supra@suprabox:~$ type source
source is a shell builtin

# source 命令存在, 当前我的默认shell 是bash
supra@suprabox:~$ source -h
-bash: source: -h: invalid option
source: usage: source filename [arguments]

supra@suprabox:~$ ls -lah /usr/bin/sh
lrwxrwxrwx 1 root root 4 Mar 31  2024 /usr/bin/sh -> dash
supra@suprabox:~$ ls -lah /usr/bin/bash
-rwxr-xr-x 1 root root 1.4M Mar 31  2024 /usr/bin/bash
supra@suprabox:~$ ls -lah /usr/bin/dash
-rwxr-xr-x 1 root root 127K Mar 31  2024 /usr/bin/dash

Bash 内置的命令执行不会启动另外一个进程, 在当前进程执行.

enable -a 启用所有内置命令, 即查看所有内置命令.

supra@suprabox:~$ enable -a
enable .
enable :
enable [
enable alias
enable bg
enable bind
enable break
enable builtin
enable caller
enable cd
enable command
enable compgen
enable complete
enable compopt
enable continue
enable declare
enable dirs
enable disown
enable echo
enable enable
enable eval
enable exec
enable exit
enable export
enable false
enable fc
enable fg
enable getopts
enable hash
enable help
enable history
enable jobs
enable kill
enable let
enable local
enable logout
enable mapfile
enable popd
enable printf
enable pushd
enable pwd
enable read
enable readarray
enable readonly
enable return
enable set
enable shift
enable shopt
enable source
enable suspend
enable test
enable times
enable trap
enable true
enable type
enable typeset
enable ulimit
enable umask
enable unalias
enable unset
enable wait

当 CPU 空闲(idle)的时候在干什么

今天学习 Linux schedule 框架的时候, 看到如下的图. 这个图列出了 Linux 内核里面的几个 schedule classes, 以及它们之间的关系.
图片源自于: https://deepdives.medium.com/digging-into-linux-scheduler-47a32ad5a0a8
1_IfRhjQssWLhgJqeItlUAdw.webp

图中各个方块分别表示 Linux 内核的几个 schedule class, 他们按照顺序通过链表的形式连在一起, 当前面的 schedule class 没有需要执行的task的时候, 就去下一个 schedule class 去查找. 当前面所有 schedule class 都没有 task 可以执行的时候, 就去执行 idle 这个虚拟的task.

各个 schedule class 的描述:

  1. Stop Scheduler (SCHED_STOP) - 停止调度器 这个调度类是用于管理特殊的停止任务,这些任务用于在CPU上停止所有其他任务的执行,通常用于实现CPU热插拔和一些系统级别的调试操作。这个调度类的任务具有最高的优先级,可以抢占系统中的任何其他任务。
  1. Deadline Scheduler (SCHED_DEADLINE) - 截止时间调度器 这是一种较新的调度类,它为任务提供了严格的截止时间保证。SCHED_DEADLINE使用了称为“恒定带宽算法”的调度策略,允许任务指定它们的运行周期、运行时间和截止时间。这个调度器试图保证每个任务在其截止时间之前都能完成指定的运行时间,适用于对时间敏感度非常高的任务。
  1. Real-Time Scheduler (RT) - 实时调度器 实时调度类分为两种策略:SCHED_FIFO(先进先出)和SCHED_RR(轮转)。这些策略用于实时任务,这些任务需要快速且可预测的响应时间。在SCHED_FIFO中,只有当运行的实时任务主动放弃CPU或者被更高优先级的实时任务抢占时,它才会停止运行。SCHED_RR与SCHED_FIFO类似,但是它为每个实时任务提供了时间片,使得同一优先级的实时任务可以轮流运行。
  1. Completely Fair Scheduler (CFS) - 完全公平调度器 CFS是Linux内核的默认调度类,用于普通的非实时任务。它的目标是确保所有运行的进程都能获得公平的CPU时间。CFS使用红黑树来跟踪所有可运行的进程,并尽量平等地分配CPU时间。它基于虚拟运行时间的概念,该时间考虑了进程的权重。权重越高的进程获得的CPU时间越多。
  1. Idle Scheduler (SCHED_IDLE) - 空闲调度器 这个调度类用于非常低优先级的任务,只有当系统中没有其他可运行的任务时,这些任务才会被调度执行。它通常用于那些可以在系统空闲时运行的后台任务,例如内存压缩守护进程(kcompactd)。

每个调度类都实现了一组调度器框架定义的通用接口,这些接口包括任务入队、出队、选择下一个运行任务等操作。调度器通过这些接口与调度类交互,以实现不同类型的任务调度需求。

Idle 调度器是如何实现的?

参考这篇文章: https://manybutfinite.com/post/what-does-an-idle-cpu-do/
最终是通过 IA 32 指令集的 HTL(halt) 指令来完成的.
hlt: Halt,使CPU进入halt状态,这是一种低功耗模式。在这种模式下,CPU会停止执行指令,直到下一个中断到来。当系统没有其他任务要执行时,操作系统会执行HLT指令来减少能耗和散热。

查询Linux的最新代码:
https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/irqflags.h#L48
asm volatile("sti": : :"memory");
这段代码的作用是使CPU开始响应外部中断,并且立即进入低功耗的halt状态,直到下一个中断到来。这通常用在系统空闲循环或者其他需要CPU等待事件的场合。由于这段代码会改变CPU的中断使能状态,并且可能影响内存的一致性.

在其他处理器架构中,可能有类似功能但名称不同的指令。例如,在ARM架构中,WFI(Wait for Interrupt)指令和WFE(Wait for Event)指令可以用来让处理器等待外部事件或中断。