Linux内核(十三)系统软中断 software
文章目录
- 中断概述
- Linux内核中断
- 软中断相关代码解析
- 软中断结构体
- 软中断类型
- 软中断两种触发方式
- 函数__do_softirq解析
- 定时器的软中断实现解析
- 定时器相关代码
- 总结
Linux版本:linux-3.18.24.x
中断概述
中断要求
快进快出,提高执行效率,尽量少做和硬件相关或数据处理等耗时繁琐的操作。
中断注意事项
中断会关闭调度,中断优先级高于任何任务的优先级。长时间中断处理会影响系统的效率,继而影响其他系统任务进行,系统任务超时引起不可预料的后果。
中断处理程序中不可以睡眠,不可以使用引起睡眠的函数(如ssleep、msleep、kmalloc、copy_to_user、copy_from_user),也不能使用可以引起睡眠的锁(metux锁),可以使用自旋锁。
Linux内核中断
Linux中断 中有两个“术语”:“中断上半部”, “中断下半部”(又称 硬中断,软中断)
中断上半部分(硬中断)特征
- 对时间要求高;
- 与硬件相关;
- 不能被中断打断;
- 这时会在所有处理器上屏蔽当前中断线,如果这个中断处理是SA_INTERRUPT标记的,那么所有的本地中断都会全局的被屏蔽掉。
结合串口接收数据中断程序来了解硬中断,涉及函数:static irqreturn_t imx_rxint(int irq, void *dev_id)
imx_rxint-> spin_lock_irqsave(&sport->port.lock, flags); // 保存中断状态,禁止本地中断,并获取自旋锁-> while (readl(sport->port.membase + USR2) & USR2_RDR) { // 读取寄存器数据sport->port.icount.rx++; // 记录接收数量}-> rx &= sport->port.read_status_mask; // 判断接收数据是否正常if (rx & URXD_BRK)flg = TTY_BREAK;else if (rx & URXD_PRERR)flg = TTY_PARITY;else if (rx & URXD_FRMERR)flg = TTY_FRAME;if (rx & URXD_OVRRUN)flg = TTY_OVERRUN;-> tty_insert_flip_char(port, rx, flg); // 数据放入tty中-> spin_unlock_irqrestore(&sport->port.lock, flags); // 恢复中断状态,启动本地中断,释放自旋锁-> tty_flip_buffer_push(port); // 将终端翻转缓冲区的推入队列到行规则(通知tty有数据来了)
可以看出串口中断中只有数据读取和校验,没有过多的繁琐操作。剩下的数据处理之类的操作在队列里进行。有关串口数据的来龙去脉可看TTY-UART驱动框架解析、 UART用户层与驱动层调用关系解析(I)、UART用户层与驱动层调用关系解析 (II)、UART用户层与驱动层调用关系解析 (III)
中断下半部分(软中断)特征
- 会恢复响应所有中断
- 对时间要求不高
- 可以延迟执行
- 可以被其他中断打断
这就使系统处于中断屏蔽状态的时间尽可能的短了,中断响应能力自然也就高了。
中断下半部分是需要系统的软中断来保证的。软中断也叫可延迟函数,tasklet就是软中断基础上实现的。
软中断相关代码解析
软中断结构体
软中断使用struct softirq_action结构体管理软件中断的注册和激活操作
struct softirq_action
{void (*action)(struct softirq_action *);
};
软中断类型
软中断类型提供10中软中断,当然软中断允许用户添加,但是内核开发者希望用户尽可能使用基于软中断的tasklet形式实现
// include/linux/interrupt.h
enum
{HI_SOFTIRQ=0,/* 优先级高的tasklets */TIMER_SOFTIRQ,/* 定时器的下半部 */NET_TX_SOFTIRQ,/* 发送网络数据包 */NET_RX_SOFTIRQ,/* 接收网络数据包 */BLOCK_SOFTIRQ,/* BLOCK装置 */BLOCK_IOPOLL_SOFTIRQ,TASKLET_SOFTIRQ,/* 正常优先级的tasklets */SCHED_SOFTIRQ,/* 调度程序 */HRTIMER_SOFTIRQ,/* 高分辨率定时器 */RCU_SOFTIRQ, /* RCU锁定 */NR_SOFTIRQS /* 10 */
};
以上的软中断都会有一个对应的回调函数保存在…softirq_vec
// kernel/softirq.c
static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;const char * const softirq_to_name[NR_SOFTIRQS] = {"HI", "TIMER", "NET_TX", "NET_RX", "BLOCK", "BLOCK_IOPOLL","TASKLET", "SCHED", "HRTIMER", "RCU"
};void open_softirq(int nr, void (*action)(struct softirq_action *))
{softirq_vec[nr].action = action;
}
软中断两种触发方式
方式一 调用api:raise_softirq
定时器中断处理函数中调用该函数,启动定时器软中断
// kernel/softirq.c
void raise_softirq(unsigned int nr)
{unsigned long flags;local_irq_save(flags);-> arch_local_irq_restore(ARCH_IRQ_DISABLED); // 关闭本地CPU中断raise_softirq_irqoff(nr); // 修改软中断标志位local_irq_restore(flags);
}inline void raise_softirq_irqoff(unsigned int nr)
{__raise_softirq_irqoff(nr);-> or_softirq_pending(1UL << nr);-> this_cpu_or(irq_stat.__softirq_pending, (x)) // 保存触发软中断标志位if (!in_interrupt()) // 判断系统当前是否在中断里wakeup_softirqd(); // 如果不在中断,唤醒软中断线程。在线程里执行软中断的回调。如果在中断里,什么都不做,等中断退出再处理
}
__softirq_pending标志保存每个软中断当前状态。__softirq_pending字段中的每一个bit,对应着某一个软中断,某个bit被置位,说明有相应的软中断等待处理。每个CPU都有一份表用来记录。这个表的定义:
typedef struct {unsigned int __softirq_pending;
} ____cacheline_aligned irq_cpustat_t;irq_cpustat_t irq_stat[NR_CPUS] ____cacheline_aligned;
EXPORT_SYMBOL(irq_stat);
内核的其它代码主动调用raise_softirq。若这时正好不是在中断上下文中,线程将被唤醒。
线程最后也要调用__do_softirq,线程函数run_ksoftirqd函数。
static void run_ksoftirqd(unsigned int cpu)
{local_irq_disable();if (local_softirq_pending()) {__do_softirq(); // 最终调用__do_softirqlocal_irq_enable();cond_resched();preempt_disable();rcu_note_context_switch(cpu);preempt_enable();return;}local_irq_enable();
}static struct smp_hotplug_thread softirq_threads = {.store = &ksoftirqd,.thread_should_run = ksoftirqd_should_run,.thread_fn = run_ksoftirqd, // 唤醒线程执行回调函数.thread_comm = "ksoftirqd/%u",
};static __init int spawn_ksoftirqd(void)
{register_cpu_notifier(&cpu_nfb);BUG_ON(smpboot_register_percpu_thread(&softirq_threads)); // 注册并创建线程return 0;
}
early_initcall(spawn_ksoftirqd); // 系统启动后自动执行
方法二 在irq_exit执行中执行回调函数
void irq_exit(void)
{
...preempt_count_sub(HARDIRQ_OFFSET);if (!in_interrupt() && local_softirq_pending()) // 保证不在中断里并且通过local_softirq_pending判断当前CPU有无等待的软中断invoke_softirq(); // 如果满足以上条件,最终执行__do_softirq();
...
}
函数__do_softirq解析
可以看出以上两种触发方式最终都会调用到__do_softirq函数,来看下在这个函数里的操作…
asmlinkage __visible void __do_softirq(void)
{...pending = local_softirq_pending(); // 取出pending的状态__local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET); // 禁止软中断,主要是为了防止和软中断守护进程发生竞争restart:set_softirq_pending(0); // 清除所有的软中断待决标志local_irq_enable(); // 打开本地CPU中断// 循环执行待决软中断的回调函数h = softirq_vec;while ((softirq_bit = ffs(pending))) {unsigned int vec_nr;int prev_count;h += softirq_bit - 1;vec_nr = h - softirq_vec;prev_count = preempt_count();kstat_incr_softirqs_this_cpu(vec_nr);trace_softirq_entry(vec_nr);h->action(h); // 处理回调函数,这函数就是最开始提到软中断表中对应函数trace_softirq_exit(vec_nr);if (unlikely(prev_count != preempt_count())) {pr_err("huh, entered softirq %u %s %p with preempt_count %08x, exited with %08x?\n",vec_nr, softirq_to_name[vec_nr], h->action,prev_count, preempt_count());preempt_count_set(prev_count);}h++;pending >>= softirq_bit;}rcu_bh_qs();local_irq_disable();// 如果上面的循环完毕,发现新的软中断被触发pending = local_softirq_pending();if (pending) {if (time_before(jiffies, end) && !need_resched() &&--max_restart)goto restart; // 重新启动循环wakeup_softirqd(); // 如果满足没有新的软中断产生或者循环max_restart=10,唤醒守护进程,剩下交给守护进程}lockdep_softirq_end(in_hardirq);account_irq_exit_time(current);__local_bh_enable(SOFTIRQ_OFFSET); // 退出前,恢复软中断WARN_ON_ONCE(in_interrupt());tsk_restore_flags(current, old_flags, PF_MEMALLOC);
}
定时器的软中断实现解析
linux 定时器是依赖于TIMER_SOFTIRQ软中断实现的。 当系统硬件中断退出后软件会遍历中断的使能位并执行相关软中断回调函数。
内核启动开始中有软中断的初始化
// init/main.c
start_kernel-> init_timers(); // 初始化定时器-> open_softirq(TIMER_SOFTIRQ, run_timer_softirq); // 开启定时器软中断
当定时器中断触发软中断后,会通过__do_softirq函数调用run_timer_softirq函数
static void run_timer_softirq(struct softirq_action *h)
{struct tvec_base *base = __this_cpu_read(tvec_bases); // 获取本地CPU参数,该中断只在这个CPU有效hrtimer_run_pending();if (time_after_eq(jiffies, base->timer_jiffies))__run_timers(base);-> base->running_timer = timer; // 找出超时的定时器timer赋给base->running_timer-> detach_expired_timer(timer, base); // 删除这个定时器...-> spin_unlock(&base->lock);-> call_timer_fn(timer, fn, data); // 调用回调函数-> spin_lock(&base->lock);...
}
定时器通过周期tick的事件处理程序触发软中断(这里周期tick事件涉及到clockeven,后续文章详细解析内核代码)
// kernel/time/tick-common.c
tick_handle_periodic // 周期tick的事件处理程序-> tick_periodic(cpu);-> update_process_times(user_mode(get_irq_regs()));-> run_local_timers();-> raise_softirq(TIMER_SOFTIRQ); // 触发软中断
定时器相关代码
上述提到在软中断中会从 struct tvec_base *base 处理超时的定时器,并执行对应的处理函数。这里介绍下定时器函数如何添加到base中的。
// kernel/time/timer.c
// 添加定时器 添加定时器是将一个定时器挂到所在链表中
void add_timer(struct timer_list *timer)
{BUG_ON(timer_pending(timer));mod_timer(timer, timer->expires);-> __mod_timer(timer, expires, false, TIMER_NOT_PINNED);
}
EXPORT_SYMBOL(add_timer);
static inline int
__mod_timer(struct timer_list *timer, unsigned long expires,bool pending_only, int pinned)
{struct tvec_base *base, *new_base;unsigned long flags;int ret = 0 , cpu;timer_stats_timer_set_start_info(timer);BUG_ON(!timer->function);// 获取tvec_base变量,base为PerCPU变量,给base加锁base = lock_timer_base(timer, &flags);// 判断定时器是否存在。如果timer已经存在,则删除,此处的操作是在加锁状态先的,因此和softirq里面不会重入ret = detach_if_pending(timer, base, false);if (!ret && pending_only)goto out_unlock;debug_activate(timer, expires);cpu = get_nohz_timer_target(pinned);new_base = per_cpu(tvec_bases, cpu);// 判断定时器是否属于当前CPUif (base != new_base) {// 不能修改一个正在运行的timer的base,否则del_timer_sync会出问题if (likely(base->running_timer != timer)) {/* See the comment in lock_timer_base() */timer_set_base(timer, NULL);spin_unlock(&base->lock);base = new_base;spin_lock(&base->lock);timer_set_base(timer, base);}}timer->expires = expires;// 将timer加入本地CPU base中internal_add_timer(base, timer);out_unlock:spin_unlock_irqrestore(&base->lock, flags); // 释放锁,恢复中断return ret;
}
小结:
1、同一个timer只能被提交一次,但是同一个timer是有可能在不同的cpu上同时运行的。
2、CPU0注册一个timer,并已经在CPU0上运行了,CPU1也注册了这个timer,因为运行的timer已经在链表上删除了且没有获取base->lock,因此lock_timer_base和timer_pending都可以向下执行并注册成功。
// 删除定时器
int del_timer(struct timer_list *timer)
{struct tvec_base *base;unsigned long flags;int ret = 0;debug_assert_init(timer);timer_stats_timer_clear_start_info(timer);if (timer_pending(timer)) { // 判断一个定时器是否存在在链表中,但是此定时器有可能正在运行base = lock_timer_base(timer, &flags);ret = detach_if_pending(timer, base, true);-> detach_timer(timer, clear_pending); // 删除spin_unlock_irqrestore(&base->lock, flags);}return ret;
}
EXPORT_SYMBOL(del_timer);
del_timer_sync和mod_timer函数实现目的是一样的。
// 判断当前定时器是否在其他CPU上运行,如果运行等待其运行完成后删除此定时器
int del_timer_sync(struct timer_list *timer)
{...for (;;) {int ret = try_to_del_timer_sync(timer);-> base = lock_timer_base(timer, &flags);if (base->running_timer != timer) { //判断当前timer是否在运行,如果在运行则等待timer_stats_timer_clear_start_info(timer);ret = detach_if_pending(timer, base, true); // 删除成功返回1}-> spin_unlock_irqrestore(&base->lock, flags);if (ret >= 0)return ret;cpu_relax();}
}
EXPORT_SYMBOL(del_timer_sync);
总结
1、软中断是一组静态定义的下半部接口,可以在所有处理器上同时执行,即使两个类型相同也可以。但一个软中断不会抢占另一个软中断,唯一可以抢占软中断的是硬中断。
2、软中断是指令触发产生的,而硬中断是由外设引发的。
3、硬中断处理程序要确保它能快速地完成任务,这样程序执行时才不会等待较长时间,称为上半部。
4、软中断处理硬中断未完成的工作,是一种推后执行的机制,属于下半部。
5、硬中断和软中断都可以抢占(或者称为中断)异常(最典型的是系统调用),但是异常不能抢占硬中断和软中断。
6、硬中断和软中断(只要是中断上下文)执行的时候都不允许内核抢占,换句话说,中断上下文中永远不允许进程切换。在执行过程中不能休眠,不能阻塞,一旦休眠或者阻塞,则系统直接挂死。
相关文章:
Linux内核(十三)系统软中断 software
文章目录中断概述Linux内核中断软中断相关代码解析软中断结构体软中断类型软中断两种触发方式函数__do_softirq解析定时器的软中断实现解析定时器相关代码总结Linux版本:linux-3.18.24.x 中断概述 中断要求 快进快出,提高执行效率,…...
Linux -- 查看进程 PS 命令 详解
我们上篇介绍了, Linux 中的进程等概念,那么,在Linux 中如何查看进程呢 ??我们常用到的有两个命令, PS 和 top 两个命令,今天先来介绍下 PS 命令~!PS 命令 :作用 &#x…...
C2科一考试道路通行规定
目录 低能见度等恶劣环境下的通行规定 驾驶机动车禁止行为 停车规定 通行常识 高速公路限速规定 三观不一样的人,无论重来多少次,结果都一样 他不会懂你的委屈 只是觉得自已没错 两个人真正的可悲连吵架都不在一个点上 有句话说得好 我要是没点自我…...
进程概念(详细版)
进程的概念本文主要介绍进程的相关知识 文章目录认识冯诺依曼体系结构操作系统的基本概念操作系统的作用是什么系统调用和库函数相关概念进程基本概念描述进程进程控制块(PCB)task_struct 结构体进程是如何被操作系统管理起来的先描述再组织描述好,组织好࿰…...
学习大数据应该掌握哪些技能
想要了解大数据开发需要掌握哪些技术,不妨先一起来了解一下大数据开发到底是做什么的~ 1、什么是大数据? 关于大数据的解释,比较官方的定义是指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合,是需要新处理模…...
【spring】Spring Data --Spring Data JPA
Spring Data 的委托是为数据访问提供熟悉且符合 Spring 的编程模型,同时仍保留着相关数据存储的特殊特征。 它使使用数据访问技术、关系和非关系数据库、map-reduce 框架和基于云的数据服务变得容易。这是一个伞形项目,其中包含许多特定于给定数据库…...
mysql数据库之视图
视图(view)是一种虚拟的存在,视图中的数据并不在数据库中实际存在,行和列数据来自定义视图的查询中使用的表,并且是在使用视图时动态生成的。 通俗的讲,视图之保存了查询的sql逻辑,不保存查询结…...
数据库事务详解
概述事务就是数据库为了保证数据的原子性,持久性,隔离性,一致性而提供的一套机制, 在同一事务中, 如果有多条sql执行, 事务可以确保执行的可靠性.数据库事务的四大特性一般来说, 事务是必须满足 4 个条件(ACID):原子性(Atomicity&…...
Nessus: 漏洞扫描器-网络取证工具
Nessue 要理解网络漏洞攻击,应该理解攻击者不是单独攻击,而是组合攻击。因此,本文介绍了关于Nessus历史的研究,它是什么以及它如何与插件一起工作。研究了Nessus的特点,使其成为网络取证中非常推荐的网络漏洞扫描工具…...
操作系统实战45讲之现代计算机组成
我以前觉得计算机理论不让我感兴趣,而比较喜欢实践,即敲代码,现在才发现理论学好了,实践才能有可能更顺利,更重要的是理论与实践相结合。 在现代,几乎所有的计算机都是遵循冯诺依曼体系结构,而遵…...
Simple Baselines for Image Restoration
Abstract.尽管近年来在图像恢复领域取得了长足的进步,但SOTA方法的系统复杂性也在不断增加,这可能会阻碍对方法的分析和比较。在本文中,我们提出了一个简单的基线,超过了SOTA方法,是计算效率。为了进一步简化基线&…...
Python数据可视化:局部整体图表可视化(基础篇—6)
目录 1、饼图 2、圆环图 3、马赛克图 4、华夫饼图 5、块状/点状柱形图 在学习本篇博文之前请先看一看之前发过的关联知识:...
CSDN新星计划新玩法、年度勋章挑战赛开启
文章目录🌟 写在前面🌟 逐步亮相的活动🌟 勋章挑战赛🌟 新星计划🌟 有付费课程才可参与?🌟 成就铭牌🌟 博客跟社区的关系🌟 写在最后🌟 写在前面 哈喽&#…...
Docker之部署Mysql
通过docker对Mysql进行部署。 如果没有部署过docker,看我之前写的目录拉取镜像运行容器开放端口拉取镜像 前往dockerHub官网地址,搜索mysql。 找到要拉取的镜像版本,在tag下找到版本。 拉取mysql镜像,不指定版本数,…...
基于C/C++获取电脑网卡的IP地址信息
目录 前言 一、网卡是什么? 二、实现访问网卡信息 1.引入库及相关的头文件 2.操作网卡数据 3. 完整代码实现 4.结果验证 总结 前言 简单示例如何在windows下使用c/c代码实现 ipconfig/all 指令 提示:以下是本篇文章正文内容,下面案例可供参考…...
28相似矩阵和若尔当标准型
一、关于正定矩阵的一些补充 在此之前,先讲一下对称矩阵中那些特征值为正数的矩阵,这样特殊的矩阵称为正定矩阵。其更加学术的定义是: SSS 是一个正定矩阵,如果对于每一个非零向量xxx,xTSx>0x^TSx>0xTSx>0 正…...
springboot操作MongoDB
启动类及配置import com.mongodb.client.MongoClient;import com.mongodb.client.MongoClients;import com.mongodb.client.internal.MongoClientImpl;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplicatio…...
1月奶粉电商销售数据榜单:销售额约20亿,高端化趋势明显
鲸参谋电商数据监测的2023年1月份京东平台“奶粉”品类销售数据榜单出炉! 根据鲸参谋数据显示,1月份京东平台上奶粉的销量约675万件,销售额约20亿元,环比均下降19%左右。与去年相比,整体也下滑了近34%。可以看出&#…...
跨境数据传输是日常业务中经常且至关重要的组成部分
跨境数据传输是日常业务中经常且至关重要的组成部分。在过去的20年中,由于全球通信网络和业务流程的发展,全球数据流的模式已迅速发展。随着数据从数据中心移到数据中心和/或跨边界移动,安全漏洞已成为切实的风险。有可能违反国家和国际数据传…...
错误: tensorflow.python.framework.errors_impl.OutOfRangeError的解决方案
近日,在使用CascadeRCNN完成目标检测任务时,我在使用这个模型训练自己的数据集时出现了如下错误: tensorflow.python.framework.errors_impl.OutOfRangeError: PaddingFIFOQueue _1_get_batch/batch/padding_fifo_queue is closed and has in…...
springboot项目初始化执行sql
Sprint Boot应用可以在启动的时候自动执行项目根路径下的SQL脚本文件。我们需要先将sql脚本写好,并将这些静态资源都放置在src/main/resources文件夹下。 再配置application.yml: spring.datasource.initialization-mode 必须配置初始化模式initializa…...
Kubernetes之存储管理(中)
NFS网络存储 emptyDir和hostPath存储,都仅仅是把数据存储在pod所在的节点上,并没有同步到其他节点,如果pod出现问题,通过deployment会产生一个新的pod,如果新的pod不在之前的节点,则会出现问题,…...
MySQL workbench的基本操作
1. 创建新的连接 hostname主机名输入“local host”和“127.0.0.1”效果是一样的,指的是本地的服务器。 需要注意的是,此处的密码在安装软件的时候已经设定。 点击【Test Connection】,测试连接是否成功。 创建完的连接可以通过,…...
【Flink】FlinkSQL中Table和DataStream互转
在我们实际使用Flink的时候会面临很多复杂的需求,很可能需要FlinkSQL和DataStream互相转换的情况,这就需要我们熟练掌握Table和DataStream互转,本篇博客给出详细代码以及执行结果,可直接使用,通过例子可学会Table和DataStream互转,具体步骤如下: maven如下<?xml ver…...
网络总结知识点(网络工程师必备)一
♥️作者:小刘在C站 ♥️个人主页:小刘主页 ♥️每天分享云计算网络运维课堂笔记,努力不一定有收获,但一定会有收获加油!一起努力,共赴美好人生! ♥️夕阳下,是最美的绽放,树高千尺,落叶归根人生不易,人间真情 目录 1.TCP UDP协议的区别 2.ARP是第几层协议,其作用...
离线安装samba与配置(.tar方式安装)
一、samba离线安装【安装并设置成功后,相关文件及其位置:①smbd:/usr/local/samba/sbin/smbd②nmdb:/usr/local/samba/sbin/nmbd③配置文件 smb.conf:/usr/local/samba/lib/smb.conf④添加用户的 smbpasswd 文件&#…...
[Java基础]—JDBC
前言 其实学Mybatis前就该学了,但是寻思目前主流框架都是用mybatis和mybatis-plus就没再去看,结果在代码审计中遇到了很多cms是使用jdbc的因此还是再学一下吧。 第一个JDBC程序 sql文件 INSERT INTO users(id, NAME, PASSWORD, email, birthday) VAL…...
基本面向对象编程-计算机基本功能实现_
《C/S项目实训》实验报告 实验名称: 基本面向对象编程-计算机基本功能实现_ 一、实验目的 通过综合实践项目,理解Java 程序设计是如何体现面向对象编程基本思想,掌握OOP方法,掌握事件触发、消息响应机制。进一步巩固面向对…...
C++面向对象之多态性
文章目录C面向对象之多态性1.静态多态2.动态多态3.多态的好处3.1使用方法4.纯虚函数5.虚析构与纯虚析构5.1问题5.2解决6.其他知识点7.代码8.测试结果8.1父类中无虚函数,父类的指针指向子类对象,将调用父类中的函数,无法调用子类中的重写函数&…...
Android性能优化系列篇:弱网优化
弱网优化1、Serializable原理通常我们使用Java的序列化与反序列化时,只需要将类实现Serializable接口即可,剩下的事情就交给了jdk。今天我们就来探究一下,Java序列化是怎么实现的,然后探讨一下几个常见的集合类,他们是…...
wordpress部署阿里云/网络培训机构
物联网学习入门篇之MQTT协议 MQTT: Massage Queuing Telemetry Transport,消息队列遥测传输 MQTT是基于互联网的基础协议TCP/IP协议而构建的,由IBM在1999年发布,基于发布和订阅两种模式(publish/subscribe)算是一种轻量级通讯协议…...
外贸网站建设浩森宇特/灰色广告投放平台
映射 | MAP 集合的映射操作是将来在编写Spark/Flink用得最多的操作,是我们必须要掌握的。因为进行数据计算的时候,就是一个将一种数据类型转换为另外一种数据类型的过程。 map方法接收一个函数,将这个函数应用到每一个元素,返回一…...
赣榆县建设局网站/顺德搜索seo网络推广
前言 alpine 提供了非常好用的apk软件包管理工具,通过apk –help命令查看完整的包管理命令。更新索引 update:从远程镜像源中更新本地镜像源索引,update命令会从各个镜像源列表下载APKINDEX.tar.gz并存储到本地缓存,一般在/var/ca…...
网站建设用哪种语言好/广告公司的业务范围
有些手机用久了,手机的音量就越来越小,相信很多人都碰到过这种情况,这个时候该怎么办呢?有没有办法来提高手机的音量呢?今天我就来介绍3种方法来提升手机的音量。方法一:开启手机的“单声道音频”。很多手机…...
美女教师做爰网站/多地优化完善疫情防控措施
引言 前面Android开发之旅:环境搭建及HelloWorld,我们介绍了如何搭建Android开发环境及简单地建立一个HelloWorld项目,本篇将通过HelloWorld项目来介绍Android项目的目录结构。本文的主要主题如下: 1、HelloWorld项目的目录结构 1…...
网站开发客户提供素材/深圳哪里有网络推广渠避
linux环境下如何查看docker是否已安装_网站服务器运行维护linux环境下查看docker是否已安装的方法是:可以通过执行【docker version】命令查看,如果输出信息中包含client和service两部分,则说明docker已经安装成功了。1、查看所有的docker容器…...