深入解析Linux内核网络-拥塞控制系列(一)

news2024/7/7 17:16:56

谈起网络拥塞控制,大家可能很熟悉八股文中的"加法增大“、”乘法减小“、”慢开始“、“拥塞避免”、“快重传”、“快恢复”等概念。没错,这是一种经典网络拥塞控制算法的基础理论,但在实际的实现时不同的拥塞控制算法,有很大差别。本文从Linux内核源码中学习网络拥塞控制算法的具体实现框架。从当前网络拥塞控制算法的发展历程上看,网络拥塞控制算法的类型主要有以下四种:

  • 基于丢包的拥塞控制算法,这类算法将丢包视为发生了网络拥塞。采取缓慢的探测方式,逐渐增大拥塞窗口,当出现丢包时,将拥塞窗口减少,代表的算法有Tahoe、Reno、NewReno、BIC、Cubic等。
  • 基于延时的拥塞控制算法,这类算法将延时增大视为发生了网络拥塞,延时增大时减少拥塞窗口,延时减少时增大拥塞窗口,代表的算法有Vegas、Westwood等。
  • 基于链路容量的拥塞控制算法,代表算法是BBR,其采用了另类的方式,不再使用丢包、延时等信号去衡量拥塞是否发生,而是直接对网络建模来避免以及应对真实的网络拥塞。
  • 基于学习的拥塞控制算法,这类算法也没有特定的拥塞信号,一般是基于训练数据、评价函数,通过机器学习生成网络拥塞控制策略模型,代表算法有Remy、PCC、Aurora、DRL-CC、Orca等。

由于每类拥塞控制算法的核心理念有很大差别,关于每种算法的实现与原理在后续的文章中进行呈现。本次文章先对Linux内核中网络拥塞控制实现细节、大致框架,进行分析和大概学习。在进行正式的分析前先简单梳理一下常识与概念:

  • 什么是网络拥塞:网络拥塞是指在网络中传输的数据量超过网络链路或节点的处理能力,导致网络延迟增加、丢包率升高和带宽利用率下降的现象。
  • 窗口(Window):如下图的TCP协议头中占据16位,用于接收端告诉发送端还有多少缓冲区可以接收数据。

  • 滑动窗口、发送窗口:下图所示黑色方框代表发送窗口。滑动窗口只是一种形象的称呼,即发送窗口一直移动从而达到发送新的数据的目的,如下图当接收到接收端发来的ACK数据包后发送窗口向右移动。图中灰色的方框代表已经发送且确认的数据,红色代表已发送且刚刚确认的数据,正是因为刚刚确认了5byte的数据,才驱动发送窗口可以向右移动5个单位,使得序号52~56的数据(绿色方框,代表允许发送的待发送数据)可以发送,当37~51区间的数据(蓝色方框,代表发送但未确认的数据包)能够被确认时,发送窗口才能向右滑动。发送窗口前方的数据(黄色方框,不允许发送的待发送数据)只能等待发送窗窗口区间内才能发送。TCP的滑动窗口是动态的,我们可以想象成小学常见的一个数学题,一个水池,体积V,每小时进水量V1,出水量V2。当水池满了就不允许再注入了,如果有个液压系统控制水池大小,那么就可以控制水的注入速率和量。这样的水池就类似TCP的窗口。应用根据自身的处理能力变化,通过本端TCP接收窗口大小控制来对对对端的发送窗口流量限制。

  • 拥塞窗口:上面介绍了发送窗口的概念,在TCP协议中有一个反映网络传输能力的变量,叫做拥塞窗口(congestion window),记作cwnd。发送端实际的发送窗口大小实际是为 接收端通告窗口 rwnd 与 拥塞窗口 cwnd 较小的那个值。
W=min(cwnd,rwnd)

从上面的概念中可以得知,拥塞窗口可以间接反映网络的状况,进而去限制发送窗口的大小。拥塞窗口作为网络拥塞控制中核心变量之一,对网络拥塞控制起到关键作用。如下图是四个核心结构体,四个结构的关系具有面向对象的特征,通过层层继承,实现了类的复用;内核中网络相关的很多函数,参数往往都是struct sock,函数内部依照不同的业务逻辑,将struct sock转换为不同的业务结构。

struct tcp_sockstruct inet_connection_sock结构体的基础上继承而来,在struct inet_connection_sock上增加了一些tcp协议相关的字段,如滑动窗口协议,拥塞算法等一些TCP专有的属性。由于这种继承关系,可以互相转换,如下举例两种转换方式,第一种是struct sock转换为struct tcp_sock,第二种是struct sock转换成struct inet_connection_sock。下面将struct tcp_sock展开可以看到与网络拥塞控制相关的字段。

static inline struct tcp_sock *tcp_sk(const struct sock *sk)
{
 return (struct tcp_sock *)sk;
}
static inline struct inet_connection_sock *inet_csk(const struct sock *sk)
{
 return (struct inet_connection_sock *)sk;
}

struct tcp_sock中定义的关于网络拥塞控制相关的字段如下所示:

struct tcp_sock {//在 inet_connection_sock  基础上增加了 滑动窗口 拥塞控制算法等tcp 专有 属性
    __be32    pred_flags;/*首部预测标志 在接收到 syn 跟新窗口 等时设置此标志 ,
    此标志和时间戳 序号等 用于判断执行 快速还是慢速路径*/

    u64    bytes_received;    /* RFC4898 tcpEStatsAppHCThruOctetsReceived
                 * sum(delta(rcv_nxt)), or how many bytes
                 * were acked.
                 */
    u32    segs_in;    /* RFC4898 tcpEStatsPerfSegsIn
                 * total number of segments in.
                 */
     u32    rcv_nxt;    /* What we want to receive next  等待接收的下一个序列号    */
    u32    copied_seq;    /* Head of yet unread data        */

/* rcv_nxt on last window update sent最早接收但没有确认的序号, 也就是接收窗口的左端,
        在发送ack的时候, rcv_nxt更新 因此rcv_wup 更新比rcv_nxt 滞后一些  */
    u32    rcv_wup;    

    u32    snd_nxt;    /* Next sequence we send 等待发送的下一个序列号        */
    u32    segs_out;    /* RFC4898 tcpEStatsPerfSegsOut
                 * The total number of segments sent.
                 */
    u64    bytes_acked;    /* RFC4898 tcpEStatsAppHCThruOctetsAcked
                 * sum(delta(snd_una)), or how many bytes
                 * were acked.
                 */
    struct u64_stats_sync syncp; /* protects 64bit vars (cf tcp_get_info()) */

     u32    snd_una;    /* First byte we want an ack for  最早一个未被确认的序号    */
     u32    snd_sml;    /* Last byte of the most recently transmitted small packet  最近发送一个小于mss的最后 一个字节序列号
    在成功发送, 如果报文小于mss,跟新这个字段 主要用来判断是否启用 nagle 算法*/
    u32    rcv_tstamp;    /* timestamp of last received ACK (for keepalives)  最近一次收到ack的时间 用于 tcp 保活*/
    u32    lsndtime;    /* timestamp of last sent data packet (for restart window) 最近一次发送 数据包时间*/
    u32    last_oow_ack_time;  /* timestamp of last out-of-window ACK */

    u32    tsoffset;    /* timestamp offset */

    struct list_head tsq_node; /* anchor in tsq_tasklet.head list */
    unsigned long    tsq_flags;

    /* Data for direct copy to user cp 数据到用户进程的控制块 有用户缓存以及其长度 prequeue 队列 其内存*/
    struct {
        struct sk_buff_head    prequeue // tcp 段 缓冲到此队列 知道进程主动读取才真正的处理;
        struct task_struct    *task;
        struct msghdr        *msg;
        int            memory;// prequeue 当前消耗的内存
        int            len;// 用户缓存中 当前可以使用的缓存大小 
    } ucopy;

    u32    snd_wl1;    /* Sequence for window update记录跟新发送窗口的那个ack 段号 用来判断是否 需要跟新窗口
    如果后续收到ack大于snd_wll 则表示需要更新 窗口*/
    u32    snd_wnd;    /* The window we expect to receive 接收方 提供的窗口大小 也就是发送方窗口大小    */
    u32    max_window;    /* Maximal window ever seen from peer 接收方通告的最大窗口    */
    u32    mss_cache;    /* Cached effective mss, not including SACKS  发送方当前有效的mss*/

    u32    window_clamp;    /* Maximal window to advertise 滑动窗口最大值        */
    u32    rcv_ssthresh;    /* Current window clamp  当前接收窗口的阈值            */
    ......
     u32    snd_ssthresh;    /* Slow start size threshold 拥塞控制 满启动阈值        */
     u32    snd_cwnd;    /* Sending congestion window    当前拥塞窗口大小  ---发送的拥塞窗口    */
    u32    snd_cwnd_cnt;    /* Linear increase counter    自从上次调整拥塞窗口后 到目前位置接收到的
    总ack段数 如果该字段为0  表示调整拥塞窗口但是没有收到ack,调整拥塞窗口之后 收到ack段就回让
    snd_cwnd_cnt 加1 */
    u32    snd_cwnd_clamp; /* Do not allow snd_cwnd to grow above this  snd_cwnd  的最大值*/
    u32    snd_cwnd_used;//记录已经从队列发送而没有被ack的段数
    u32    snd_cwnd_stamp;//记录最近一次检验cwnd 的时间;     拥塞期间 每次会检验cwnd而调节拥塞窗口 ,
    //在非拥塞期间,为了防止应用层序造成拥塞窗口失效  因此在发送后 有必要检测cwnd
    u32    prior_cwnd;    /* Congestion window at start of Recovery.在进入 Recovery 状态时的拥塞窗口 */
    u32    prr_delivered;    /* Number of newly delivered packets to在恢复阶段给接收者新发送包的数量
                 * receiver in Recovery. */
    u32    prr_out;    /* Total number of pkts sent during Recovery.在恢复阶段一共发送的包的数量 */

     u32    rcv_wnd;    /* Current receiver window 当前接收窗口的大小        */
    u32    write_seq;    /* Tail(+1) of data held in tcp send buffer   已加入发送队列中的最后一个字节序号*/
    u32    notsent_lowat;    /* TCP_NOTSENT_LOWAT */
    u32    pushed_seq;    /* Last pushed seq, required to talk to windows */
    u32    lost_out;    /* Lost packets丢失的数据报            */
    u32    sacked_out;    /* SACK'd packets启用 SACK 时,通过 SACK 的 TCP 选项标识已接收到的段的数量。
                 不启用 SACK 时,标识接收到的重复确认的次数,该值在接收到确认新数据段时被清除。            */
    u32    fackets_out;    /* FACK'd packets    FACK'd packets 记录 SND.UNA 与 (SACK 选项中目前接收方收到的段中最高序号段) 之间的段数。FACK
            用 SACK 选项来计算丢失在网络中上的段数  lost_out=fackets_out-sacked_out  left_out=fackets_out        */

    /* from STCP, retrans queue hinting */
    struct sk_buff* lost_skb_hint; /*在重传队列中, 缓存下次要标志的段*/
    struct sk_buff *retransmit_skb_hint;/* 表示将要重传的起始包*/

    /* OOO segments go in this list. Note that socket lock must be held,
     * as we do not use sk_buff_head lock.
     */
    struct sk_buff_head    out_of_order_queue;

    /* SACKs data, these 2 need to be together (see tcp_options_write) */
    struct tcp_sack_block duplicate_sack[1]; /* D-SACK block */
    struct tcp_sack_block selective_acks[4]; /* The SACKS themselves*/

    struct tcp_sack_block recv_sack_cache[4];

    struct sk_buff *highest_sack;   /* skb just after the highest
                     * skb with SACKed bit set
                     * (validity guaranteed only if
                     * sacked_out > 0)
                     */

    int     lost_cnt_hint;/* 已经标志了多少个段 */
    u32     retransmit_high;    /* L-bits may be on up to this seqno  表示将要重传的起始包 */

    u32    prior_ssthresh; /* ssthresh saved at recovery start表示前一个snd_ssthresh得大小    */
    u32    high_seq;    /* snd_nxt at onset of congestion拥塞开始时,snd_nxt的大----开始拥塞的时候下一个要发送的序号字节*/

    u32    retrans_stamp;    /* Timestamp of the last retransmit,
                 * also used in SYN-SENT to remember stamp of
                 * the first SYN. */
    u32    undo_marker;    /* snd_una upon a new recovery episode. 在使用 F-RTO 算法进行发送超时处理,或进入 Recovery 进行重传,
                    或进入 Loss 开始慢启动时,记录当时 SND.UNA, 标记重传起始点。它是检测是否可以进行拥塞控制撤销的条件之一,一般在完成
                    拥塞撤销操作或进入拥塞控制 Loss 状态后会清零。*/
    int    undo_retrans;    /* number of undoable retransmissions. 在恢复拥塞控制之前可进行撤销的重传段数。
                    在进入 FTRO 算法或 拥塞状态 Loss 时,清零,在重传时计数,是检测是否可以进行拥塞撤销的条件之一。*/
    u32    total_retrans;    /* Total retransmits for entire connection */

    u32    urg_seq;    /* Seq of received urgent pointer  紧急数据的序号 所在段的序号和紧急指针相加获得*/
    unsigned int        keepalive_time;      /* time before keep alive takes place */
    unsigned int        keepalive_intvl;  /* time interval between keep alive probes */

    int            linger2;

/* Receiver side RTT estimation */
    struct {
        u32    rtt;
        u32    seq;
        u32    time;
    } rcv_rtt_est;

/* Receiver queue space */
    struct {
        int    space;
        u32    seq;
        u32    time;
    } rcvq_space;

/* TCP-specific MTU probe information. */
    struct {
        u32          probe_seq_start;
        u32          probe_seq_end;
    } mtu_probe;
    u32    mtu_info; /* We received an ICMP_FRAG_NEEDED / ICMPV6_PKT_TOOBIG
               * while socket was owned by user.
               */

#ifdef CONFIG_TCP_MD5SIG
    const struct tcp_sock_af_ops    *af_specific;
    struct tcp_md5sig_info    __rcu *md5sig_info;
#endif

    struct tcp_fastopen_request *fastopen_req;

    struct request_sock *fastopen_rsk;
    u32    *saved_syn;
};

下面看一个特别重要的框架,也可以称为是拥塞控制引擎,如下结构体所示,tcp_congestion_ops描述了一套拥塞控制算法所需要支持的操作。这个框架定义了一些钩子函数,Linux内核中不同的拥塞控制算法根据算法思想实现以下钩子函数,然后进行注册即可完成拥塞控制算法的设计。

 资料直通车:Linux内核源码技术学习路线+视频教程内核源码

学习直通车:Linuxc/c++高级开发【直播公开课】

零声白金VIP体验卡:零声白金VIP体验卡(含基础架构/高性能存储/golang/QT/音视频/Linux内核)

struct tcp_congestion_ops {
 struct list_head list;
 u32 key;
 u32 flags;

 /* initialize private data (optional) */
 void (*init)(struct sock *sk);
 /* cleanup private data  (optional) */
 void (*release)(struct sock *sk);

 /* return slow start threshold (required) */
 u32 (*ssthresh)(struct sock *sk);
 /* do new cwnd calculation (required) */
 void (*cong_avoid)(struct sock *sk, u32 ack, u32 acked);
 /* call before changing ca_state (optional) */
 void (*set_state)(struct sock *sk, u8 new_state);
 /* call when cwnd event occurs (optional) */
 void (*cwnd_event)(struct sock *sk, enum tcp_ca_event ev);
 /* call when ack arrives (optional) */
 void (*in_ack_event)(struct sock *sk, u32 flags);
 /* new value of cwnd after loss (required) */
 u32  (*undo_cwnd)(struct sock *sk);
 /* hook for packet ack accounting (optional) */
 void (*pkts_acked)(struct sock *sk, const struct ack_sample *sample);
 /* suggest number of segments for each skb to transmit (optional) */
 u32 (*tso_segs_goal)(struct sock *sk);
 /* returns the multiplier used in tcp_sndbuf_expand (optional) */
 u32 (*sndbuf_expand)(struct sock *sk);
 /* call when packets are delivered to update cwnd and pacing rate,
  * after all the ca_state processing. (optional)
  */
 void (*cong_control)(struct sock *sk, const struct rate_sample *rs);
 /* get info for inet_diag (optional) */
 size_t (*get_info)(struct sock *sk, u32 ext, int *attr,
      union tcp_cc_info *info);

 char   name[TCP_CA_NAME_MAX];
 struct module  *owner;
};

用户可以通过自定义以上钩子函数实现定制拥塞控制算法,并进行注册。以下截取cubic拥塞控制算法对接口的实现、注册的代码片段。可以注意到cubic只实现了拥塞控制引擎tcp_congestion_ops的部分钩子函数,因为有一些钩子函数是必须实现,有一些是根据算法选择实现的。

static struct tcp_congestion_ops cubictcp __read_mostly = {
 .init  = bictcp_init,
 .ssthresh = bictcp_recalc_ssthresh,
 .cong_avoid = bictcp_cong_avoid,
 .set_state = bictcp_state,
 .undo_cwnd = tcp_reno_undo_cwnd,
 .cwnd_event = bictcp_cwnd_event,
 .pkts_acked     = bictcp_acked,
 .owner  = THIS_MODULE,
 .name  = "cubic",
};

static int __init cubictcp_register(void)
{
 BUILD_BUG_ON(sizeof(struct bictcp) > ICSK_CA_PRIV_SIZE);
 beta_scale = 8*(BICTCP_BETA_SCALE+beta) / 3
  / (BICTCP_BETA_SCALE - beta);

 cube_rtt_scale = (bic_scale * 10); /* 1024*c/rtt */

 cube_factor = 1ull << (10+3*BICTCP_HZ); /* 2^40 */

 /* divide by bic_scale and by constant Srtt (100ms) */
 do_div(cube_factor, bic_scale * 10);

 return tcp_register_congestion_control(&cubictcp);
}

static void __exit cubictcp_unregister(void)
{
 tcp_unregister_congestion_control(&cubictcp);
}

module_init(cubictcp_register);
module_exit(cubictcp_unregister);

在Linux用户态可以通过参数查看当前使用的拥塞控制算法、当前可支持的拥塞控制算法。如下表所示是两个参数以及含义。

法。可以看到当前可支持的拥塞控制算法中包含bbr算法,bbr算法在内核版本4.9开始支持的。

如果留意的话,在本文开始时提到了很多传统的拥塞控制算法,那么在上面的命令中没有看到,其实有众多拥塞控制算法在Linux中没有进行安装,如下命令查看Linux系统中所有已实现的拥塞控制算法模块

如果想安装特定的拥塞控制算法可以通过modprobe命令对指定的拥塞控制算法进行安装,如下所示安装了Vegas拥塞控制算法,此时再查看当前系统中可以使用的拥塞控制算法,多了一个Vegas算法。

除了可以动态查看当前Linux系统可用的拥塞控制算法、当前使用的拥塞控制算法外还可以动态切换拥塞控制算法。如下所示将默认的cubic拥塞控制算法切换为bbr拥塞控制算法。

切换后验证如下,当前运行的拥塞控制算法由之前的cubic拥塞控制算法切换到了bbr拥塞控制算法。

至此本文关于Linux内核网络中拥塞控制的大概框架、原理介绍到这,文中有表达有误或者不准确的地方欢迎指正。关于具体的每个拥塞控制算法的实现,将在后续文章中呈现。

原文作者:技术简说

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1283132.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2、Linux_远程操作

远程操作 1.配置ifconfig 1.1输入 ifconfig 查看 ip 的命令( ifconfig ) 1.2搜索 ifconfig 命令&#xff08;yum search ifconfig&#xff09; 1.3配置网卡 进入如下目录配置网卡 cd /etc/syscofig/network-scripts编辑 ifcfg-ens33 vi ifcfg-ens33按 i 键进入编辑模式 按 …

minio服务端搭建使用

一.minio文件服务搭建 非docker环境部署(Linux部署) 1.官网下载安装包&#xff1a;MinIO | Code and downloads to create high performance object storage 2、上传安装包文件到目录(这个可以自由选择) /home/minio/ 3、为minio添加权限 sudo chmod x minio 4、 创建mini…

【C语言】扫雷小游戏初学者版

成功的秘诀就是每天都比别人多努力一点。 今天给大家带来一款非常经典的小游戏——扫雷的实现和讲解 这里是目录 前言整体框架1.打印菜单2.创建二维数组3.初始化棋盘4.打印棋盘5.布置棋盘中的雷6.排查雷和统计雷总体代码test.cgame.cgame.h 进阶&#xff08;递归展开&#xff0…

【arduino库之TroykaDHT(针对DHT系列温湿度传感器)】

该库允许您从 DHT 系列传感器读取温度和湿度。 该库允许获取以摄氏度、开尔文和华氏度为单位的相对湿度和温度数据。支持的传感器&#xff1a;DH11、DHT21、DHT22。 TroykaDHT库的的使用非常简单&#xff0c;它包含7个函数&#xff1a; begin //初始化接口&#xff0c;做好…

Matlab 生成license

参考下面两个帖子 https://ww2.mathworks.cn/matlabcentral/answers/389888-matlab https://www.mathworks.com/matlabcentral/answers/131749-id-id-id-id 登陆 https://ww2.mathworks.cn/licensecenter 针对R2020b版本,点击下面红框生成 ip addr | grep ether看第一行 根据…

如何使用cpolar+Plex在Windows系统上搭建私人媒体影音站点公网可访问

文章目录 1.前言2. Plex网站搭建2.1 Plex下载和安装2.2 Plex网页测试2.3 cpolar的安装和注册 3. 本地网页发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 1.前言 用手机或者平板电脑看视频&#xff0c;已经算是生活中稀松平常的场景了&#xff0c;特别是各…

数据可视化私有化部署:为何成本居高不下?

尽管在可视化设计这行干了好多年&#xff0c;也接手过不少项目&#xff0c;但昂贵的私有化部署费用总能让我发出由衷的感叹&#xff1a;“这几十万一年也太贵了&#xff01;”。可以预见&#xff0c;数据可视化软件私有化部署所带来的高昂成本&#xff0c;将是许多企业面临的问…

探索数据之美:优雅权重计算方法与Python实践

写在开头 在数据的世界里,我们常常需要通过各种方法为不同的数据点分配合理的权重。这是数据分析中至关重要的一环,它决定了模型的准确性和结果的可信度。本文将引导您探索数据分析中常用的权重计算方法,并通过清晰的Python代码实现,让您轻松驾驭权重的奥秘。 1.常见分类…

人机交互——言语信息表示模型

如何将大量的言语碎片进行统一表示和存储&#xff0c;以便能够提取不同类型言语信息中的重要特征和语义信息&#xff0c;并计算和推理用户的交互意图&#xff0c;是一个极具挑战性的问题。 1.言语信息表示模型概述 2.言语信息表示模型结构 3.言语信息表示模型应用

什么是网络爬虫?有什么用?怎么爬?

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 【导读】 网络爬虫也叫做网络机器人&#xff0c;可以代替人们自动地在互联网中进行数据信息的采集与整理。 在大数据时代&#xff0c;信息的采集是一项重要的工作&#xff0c;如果单纯靠人力进行信息采集&#xff0c;不仅低…

认识Docker

Docker 是世界领先的软件容器平台&#xff0c;所以想要搞懂 Docker 的概念我们必须先从容器开始说起。 1.1 什么是容器? 先来看看容器较为官方 一句话概括容器&#xff1a;容器就是将软件打包成标准化单元&#xff0c;以用于开发、交付和部署。 容器镜像是轻量的、可执行的…

跨境卖家2023圣诞节攻略:激发购物热情,海外网红营销大作战

随着全球电商的快速发展&#xff0c;跨境卖家们正面临着越来越激烈的竞争。在这个竞争激烈的市场中&#xff0c;如何通过有效的营销策略吸引目标受众&#xff0c;成为跨境卖家需要认真思考的问题之一。尤其是在圣诞节这个全球性的购物狂欢节&#xff0c;海外网红营销成为了一种…

12月第一天,给搞电商的家人们整活儿!

今年&#xff0c;大家出门的热情直线飙升&#xff01; 特种兵旅游带火N个小众城市、Citywalk大军激活大街小巷的商业活力、线下演出火爆带动各地文旅指数狂飙&#xff0c;户外运动也乘风而上&#xff0c;徒步/露营/钓鱼/骑行/冲浪/滑雪等花式运动在各大社媒平台集中刷屏&#…

接口验签规则

一、验签的背景 在网络发展快速的过程中&#xff0c;总是会忽略接口数据安全问题&#xff0c;进行验签则能够在一定程度上能够防刷&#xff0c;数据篡改。 二、什么是加签验签 加签验签&#xff0c; 发送消息方&#xff0c;对消息加签名&#xff1b; 接受消息方&#xff0…

记一次引入低版本包导致包冲突,表现为NoClassDefFoundError的故障

简而言之&#xff0c;因为参考别的项目处理excel的代码if(org.apache.poi.hssf.usermodel.HSSFDateUtil.isCellDateFormatted(cell)) &#xff0c;为了使用这个HSSFDateUtil类我引入了依赖&#xff1a; <dependency><groupId>org.apache.poi</groupId><a…

探索 Linux Namespace:Docker 隔离的神奇背后

来自&#xff1a;探索云原生 https://www.lixueduan.com 原文&#xff1a;https://www.lixueduan.com/posts/docker/03-container-core/ 在 深入理解 Docker 核心原理&#xff1a;Namespace、Cgroups 和 Rootfs 一文中我们分析了 Docker 是由三大核心技术实现的。 今天就一起分…

【ARM Trace32(劳特巴赫) 使用介绍 12 -- Trace32 常用命令之 d.dump | data.dump 介绍】

文章目录 Trace32 常用命令之 d.dump | data.dump 介绍1 字节显示 (Byte)4 字节显示&#xff08;word&#xff09;8 字节显示&#xff08;通常long&#xff09;十进制显示显示指定列数显示地址范围内的值 Trace32 常用命令之 d.dump | data.dump 介绍 在 TRACE32 调试环境中&a…

S32K116新建工程Debug可以运行,冷启动无法运行问题分析

S32K116使用IAR建立工程后&#xff0c;软件debug可以运行&#xff0c;断电冷启动无法运行。 这种现象基本上都是RAM未初始化导致&#xff0c;由于Debug时&#xff0c;调试器会自动初始化芯片&#xff0c;很多问题都不会暴露处理。 大家可以开一下Startup的汇编文件&#xff0c;…

羊大师教你如何在冬天运动,然后悄悄惊艳所有人

羊大师教你如何在冬天运动&#xff0c;然后悄悄惊艳所有人 寒冷的冬季&#xff0c;寂静的清晨&#xff0c;你是否也曾感到在冰冷的天气中进行锻炼是一件非常困难的事情&#xff1f;但是&#xff0c;现在请跟随小编羊大师一起来探索冬季秘密运动&#xff0c;让你在春节惊艳众人…

XC4060 40V降5V/3.3V 0.6A小电流高耐压芯片 适用于单片机供电输出、电池供电设备

XC4060器件是高效率&#xff0c;同步降压DC/DC稳压器。具有较宽的输入范围&#xff0c;它们适用于广泛的应用&#xff0c;例如来自非稳压源的功率调节。他们的特点是一个长距离(500mQ/300mQ2型) 内部开关的效率最高 (92%)。Sum od (非A选项)和PWM模式(A选项)&#xff0c;工作频…