• 1 前言
  • 2 和中断相关的linux实时性分析以及中断线程化的背景介绍
    • 2.1 非抢占式linux内核的实时性
    • 2.2 抢占式linux内核的实时性
    • 2.3 进一步提高linux内核的实时性
  • 3 request_threaded_irq的接口规格
    • 3.1 输入参数描述
    • 3.2 输出参数描述
    • 3.3 Interrupt type flags
  • 4 request_threaded_irq代码分析
    • 4.1 request_threaded_irq主流程
    • 4.2 注册irqaction
      • 4.2.1 nested IRQ的处理代码
      • 4.2.2 forced irq threading处理
      • 4.2.3 创建interrupt线程
    • 4.2.4 共享中断的检查
      • 4.2.5 thread mask的设定
      • 4.2.6 用户IRQ flag和底层interrupt flag的同步

1 前言

本文主要的议题是作为一个普通的驱动工程师,在撰写自己负责的驱动的时候,如何向Linux Kernel中的中断子系统注册中断处理函数?为了理解注册中断的接口,必须了解一些中断线程化(threaded interrupt handler)的基础知识,这些在第二章描述。第三章主要描述了驱动申请 interrupt line接口API request_threaded_irq的规格。第四章是进入request_threaded_irq的实现细节,分析整个代码的执行过程。

2 和中断相关的linux实时性分析以及中断线程化的背景介绍

2.1 非抢占式linux内核的实时性

在遥远的过去,linux2.4之前的内核不支持抢占特性的,具体可以参考下图:

config

事情的开始源自高优先级任务(橘色block)由于要等待外部事件(例如网络数据)而进入睡眠,调度器调度了某个低优先级的任务(紫色block)执行。该低优先级任务欢畅的执行,直到触发了一次系统调用(例如通过read()文件接口读取磁盘上的文件等)而进入了内核态。仍然是熟悉的配方,仍然是熟悉的味道,低优先级任务正在执行不会变化,只不过从user space切换到了kernel space。外部事件总是在你不想让它来的时候到来,T0时刻,高优先级任务等待的那个中断事件发生了。

中断虽然发生了,但软件不一定立刻响应,可能由于在内核态执行的某些操作不希望被外部事件打断而主动关闭了中断(或是关闭了CPU的中断,或者MASK了该IRQ number),这时候,中断信号没有立刻得到响应,软件仍然在内核态执行低优先级任务系统调用的代码。在T1时刻,内核态代码由于退出临界区而打开中断(注意:上图中的比例是不协调的,一般而言,linux kernel不会有那么长的关中断时间,上面主要是为了表示清楚,同理,从中断触发到具体中断服务程序的执行也没有那么长,都是为了表述清楚),中断一旦打开,立刻跳转到了异常向量地址,interrupt handler抢占了低优先级任务的执行,进入中断上下文(虽然这时候的current task是低优先级任务,但是中断上下文和它没有任何关系)。

从CPU开始处理中断到具体中断服务程序被执行还需要一个分发的过程。这个期间系统要做的主要操作包括确定HW interrupt ID,确定IRQ Number,ack或者mask中断,调用中断服务程序等。T0到T2之间的delay被称为中断延迟(Interrupt Latency),主要包括两部分,一部分是HW造成的delay(硬件的中断系统识别外部的中断事件并signal到CPU),另外一部分是软件原因(内核代码中由于要保护临界区而关闭中断引起的)。

该中断的服务程序执行完毕(在其执行过程中,T3时刻,会唤醒高优先级任务,让它从sleep状态进入runable状态),返回低优先级任务的系统调用现场,这时候并不存在一个抢占点,低优先级任务要完成系统调用之后,在返回用户空间的时候才出现抢占点。漫长的等待之后,T4时刻,调度器调度高优先级任务执行。有一个术语叫做任务响应时间(Task Response Time)用来描述T3到T4之间的delay。

2.2 抢占式linux内核的实时性

2.6内核和2.4内核显著的不同是提供了一个CONFIG_PREEMPT的选项,打开该选项后,linux kernel就支持了内核代码的抢占(当然不能在临界区),其行为如下:

config

T0到T3的操作都是和上一节的描述一样的,不同的地方是在T4。对于2.4内核,只有返回用户空间的时候才有抢占点出现,但是对于抢占式内核而言,即便是从中断上下文返回内核空间的进程上下文,只要内核代码不在临界区内,就可以发生调度,让最高优先级的任务调度执行

在非抢占式linux内核中,一个任务的内核态是不可以被其他进程抢占的。这里并不是说kernel space不可以被抢占,只是说进程通过系统调用陷入到内核的时候,不可以被其他的进程抢占。实际上,中断上下文当然可以抢占进程上下文(无论是内核态还是用户态),更进一步,中断上下文是拥有至高无上的权限,它甚至可以抢占其他的中断上下文。引入抢占式内核后,系统的平均任务响应时间会缩短,但是,实时性更关注的是:无论在任何的负载情况下,任务响应时间是确定的。因此,更需要关注的是worst-case的任务响应时间。这里有两个因素会影响worst case latency:

(1)为了同步,内核中总有些代码需要持有自旋锁资源,或者显式的调用preempt_disable禁止抢占,这时候不允许抢占

(2)中断上下文(并非只是中断handler,还包括softirqtimertasklet!!!中断上下文!!!)总是可以抢占进程上下文

因此,即便是打开了PREEMPT的选项,实际上linux系统的任务响应时间仍然是不确定的。一方面内核代码的临界区非常多,我们需要找到,系统中持有锁,或者禁止抢占的最大的时间片。另外一方面,在上图的T4中,能顺利的调度高优先级任务并非易事,这时候可能有触发的软中断,也可能有新来的中断,也可能某些driver的tasklet要执行,只有在没有任何bottom half的任务要执行的时候,调度器才会启动调度。

2.3 进一步提高linux内核的实时性

通过上一个小节的描述,相信大家都确信中断对linux实时性是最大的敌人。那么怎么破?我曾经接触过一款RTOS,它的中断handler非常简单,就是发送一个inter-task message到该driver thread,对任何的一个驱动都是如此处理。这样,每个中断上下文都变得非常简短,而且每个中断都是一致的。在这样的设计中,外设中断的处理线程化了,然后,系统设计师要仔细的为每个系统中的task分配优先级,确保整个系统的实时性。

在Linux kernel中,一个外设的中断处理被分成top halfbottom half,top half进行最关键,最基本的处理,而比较耗时的操作被放到bottom half(softirq、tasklet)中延迟执行。虽然bottom half被延迟执行,但始终都是先于进程执行的。为何不让这些耗时的bottom half和普通进程公平竞争呢?因此,linux kernel借鉴了RTOS的某些特性,对那些耗时的驱动interrupt handler进行线程化处理,在内核的抢占点上,让线程(无论是内核线程还是用户空间创建的线程,还是驱动的interrupt thread)在一个舞台上竞争CPU。

3 request_threaded_irq的接口规格

3.1 输入参数描述

输入参数 描述
irq 要注册handler的那个IRQ number。这里要注册的handler包括两个,一个是传统意义的中断handler,我们称之primary handler,另外一个是threaded interrupt handler
handler primary handler。需要注意的是primary handler和threaded interrupt handler不能同时为空,否则会出错
thread_fn threaded interrupt handler。如果该参数不是NULL,那么系统会创建一个kernel thread,调用的function就是thread_fn
irqflags 参见第三节
devname
dev_id 参见第四章,第一节中的描述。

3.2 输出参数描述

0表示成功执行,负数表示各种错误原因。

3.3 Interrupt type flags

flag定义 描述
IRQF_TRIGGER_XXX 描述该interrupt line触发类型的flag
IRQF_DISABLED 首先要说明的是这是一个废弃的flag,在新的内核中,该flag没有任何的作用了。具体可以参考:Disabling IRQF_DISABLED
旧的内核(2.6.35版本之前)认为有两种interrupt handler:slow handler和fast handle。在request irq的时候,对于fast handler,需要传递IRQF_DISABLED的参数,确保其中断处理过程中是关闭CPU的中断,因为是fast handler,执行很快,即便是关闭CPU中断不会影响系统的性能。但是,并不是每一种外设中断的handler都是那么快(例如磁盘),因此就有 slow handler的概念,说明其在中断处理过程中会耗时比较长。对于这种情况,在执行interrupt handler的时候不能关闭CPU中断,否则对系统的performance会有影响。
新的内核已经不区分slow handler和fast handle,都是fast handler,都是需要关闭CPU中断的,那些需要后续处理的内容推到threaded interrupt handler中去执行。
IRQF_SHARED 这是flag用来描述一个interrupt line是否允许在多个设备中共享。如果中断控制器可以支持足够多的interrupt source,那么在两个外设间共享一个interrupt request line是不推荐的,毕竟有一些额外的开销(发生中断的时候要逐个询问是不是你的中断,软件上就是遍历action list),因此外设的irq handler中最好是一开始就启动判断,看看是否是自己的中断,如果不是,返回IRQ_NONE,表示这个中断不归我管。 早期PC时代,使用8259中断控制器,级联的8259最多支持15个外部中断,但是PC外设那么多,因此需要irq share。现在,ARM平台上的系统设计很少会采用外设共享IRQ方式,毕竟一般ARM SOC提供的有中断功能的GPIO非常的多,足够用的。 当然,如果确实需要两个外设共享IRQ,那也只能如此设计了。对于HW,中断控制器的一个interrupt source的引脚要接到两个外设的interrupt request line上,怎么接?直接连接可以吗?当然不行,对于低电平触发的情况,我们可以考虑用与门连接中断控制器和外设。
IRQF_PROBE_SHARED IRQF_SHARED用来表示该interrupt action descriptor是允许和其他device共享一个interrupt line(IRQ number),但是实际上是否能够share还是需要其他条件:例如触发方式必须相同。有些驱动程序可能有这样的调用场景:我只是想scan一个irq table,看看哪一个是OK的,这时候,如果即便是不能和其他的驱动程序share这个interrupt line,我也没有关系,我就是想scan看看情况。这时候,caller其实可以预见sharing mismatche的发生,因此,不需要内核打印“Flags mismatch irq……“这样冗余的信息
IRQF_PERCPU 在SMP的架构下,中断有两种mode,一种中断是在所有processor之间共享的,也就是global的,一旦中断产生,interrupt controller可以把这个中断送达多个处理器。当然,在具体实现的时候不会同时将中断送达多个CPU,一般是软件和硬件协同处理,将中断送达一个CPU处理。但是一段时间内产生的中断可以平均(或者按照既定的策略)分配到一组CPU上。这种interrupt mode下,interrupt controller针对该中断的operational register是global的,所有的CPU看到的都是一套寄存器,一旦一个CPU ack了该中断,那么其他的CPU看到的该interupt source的状态也是已经ack的状态。
和global对应的就是per cpu interrupt了,对于这种interrupt,不是processor之间共享的,而是特定属于一个CPU的。例如GIC中interrupt ID等于30的中断就是per cpu的(这个中断event被用于各个CPU的local timer),这个中断号虽然只有一个,但是,实际上控制该interrupt ID的寄存器有n组(如果系统中有n个processor),每个CPU看到的是不同的控制寄存器。在具体实现中,这些寄存器组有两种形态,一种是banked,所有CPU操作同样的寄存器地址,硬件系统会根据访问的cpu定向到不同的寄存器,另外一种是non banked,也就是说,对于该interrupt source,每个cpu都有自己独特的访问地址。
IRQF_NOBALANCING 这也是和multi-processor相关的一个flag。对于那些可以在多个CPU之间共享的中断,具体送达哪一个processor是有策略的,我们可以在多个CPU之间进行平衡。如果你不想让你的中断参与到irq balancing的过程中那么就设定这个flag
IRQF_IRQPOLL
IRQF_ONESHOT one shot本身的意思的只有一次的,结合到中断这个场景,则表示中断是一次性触发的,不能嵌套。对于primary handler,当然是不会嵌套,但是对于threaded interrupt handler,我们有两种选择,一种是mask该interrupt source,另外一种是unmask该interrupt source。一旦mask住该interrupt source,那么该interrupt source的中断在整个threaded interrupt handler处理过程中都是不会再次触发的,也就是one shot了。这种handler不需要考虑重入问题。
具体是否要设定one shot的flag是和硬件系统有关的,我们举一个例子,比如电池驱动,电池里面有一个电量计,是使用HDQ协议进行通信的,电池驱动会注册一个threaded interrupt handler,在这个handler中,会通过HDQ协议和电量计进行通信。对于这个handler,通过HDQ进行通信是需要一个完整的HDQ交互过程,如果中间被打断,整个通信过程会出问题,因此,这个handler就必须是one shot的。
IRQF_NO_SUSPEND 这个flag比较好理解,就是说在系统suspend的时候,不用disable这个中断,如果disable,可能会导致系统不能正常的resume。
IRQF_FORCE_RESUME 在系统resume的过程中,强制必须进行enable的动作,即便是设定了IRQF_NO_SUSPEND这个flag。这是和特定的硬件行为相关的。
IRQF_NO_THREAD 有些low level的interrupt是不能线程化的(例如系统timer的中断),这个flag就是起这个作用的。另外,有些级联的interrupt controller对应的IRQ也是不能线程化的(例如secondary GIC对应的IRQ),它的线程化可能会影响一大批附属于该interrupt controller的外设的中断响应延迟。
IRQF_EARLY_RESUME
IRQF_TIMER

4 request_threaded_irq代码分析

4.1 request_threaded_irq主流程

int request_threaded_irq(unsigned int irq, irq_handler_t handler, irq_handler_t thread_fn, unsigned long irqflags, const char *devname, void *dev_id) 
{  if ((irqflags & IRQF_SHARED) && !dev_id)---(1) return -EINVAL;desc = irq_to_desc(irq); -----(2) if (!desc)         return -EINVAL;if (!irq_settings_can_request(desc) || ----(3) WARN_ON(irq_settings_is_per_cpu_devid(desc))) return -EINVAL;if (!handler) { ----(4) if (!thread_fn) return -EINVAL; handler = irq_default_primary_handler; }action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);action->handler = handler; action->thread_fn = thread_fn; action->flags = irqflags; action->name = devname; action->dev_id = dev_id;chip_bus_lock(desc); retval = __setup_irq(irq, desc, action); ---(5) chip_bus_sync_unlock(desc); 
}

(1)对于那些需要共享的中断,在request irq的时候需要给出dev id,否则会出错退出。为何对于IRQF_SHARED的中断必须要给出dev id呢?实际上,在共享的情况下,一个IRQ number对应若干个irqaction(!!!),当操作irqaction的时候,仅仅给出IRQ number就不是非常的足够了,这时候,需要一个ID表示具体的irqaction,这里就是dev_id的作用了。我们举一个例子:

void free_irq(unsigned int irq, void *dev_id)

释放一个IRQ资源的时候,不但要给出IRQ number,还要给出device ID。只有这样,才能精准的把要释放的那个irqaction从irq action list上移除。dev_id在中断处理中有没有作用呢?我们来看看source code:

irqreturn_t handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action) 
{do { irqreturn_t res;  res = action->handler(irq, action->dev_id);
…… action = action->next; } while (action);
…… 
}

linux interrupt framework虽然支持中断共享,但是它并不会协助解决识别问题,它只会遍历该IRQ number上注册的irqaction的callback函数,这样,虽然只是一个外设产生的中断,linux kernel还是把所有共享的那些中断handler都逐个调用执行(!!!)。为了让系统的performance不受影响,irqaction的callback函数必须在函数的最开始进行判断,是否是自己的硬件设备产生了中断!!!(读取硬件的寄存器),如果不是,尽快的退出。

需要注意的是,这里dev_id不能在中断触发的时候用来标识需要调用哪一个irqaction的callback函数,通过上面的代码也可以看出,dev_id有些类似一个参数传递的过程,可以把具体driver的一些硬件信息,组合成一个structure,在触发中断的时候可以把这个structure传递给中断处理函数。

(2)通过IRQ number获取对应的中断描述符。在引入CONFIG_SPARSE_IRQ选项后,这个转换变得不是那么简单了。在过去,我们会以IRQ number为index,从irq_desc这个全局数组中直接获取中断描述符。如果配置CONFIG_SPARSE_IRQ选项,则需要从radix tree中搜索。CONFIG_SPARSE_IRQ选项的更详细的介绍请参考IRQ number和中断描述符

(3)并非系统中所有的IRQ number都可以request,有些中断描述符被标记为IRQ_NOREQUEST,标识该IRQ number不能被其他的驱动request。一般而言,这些IRQ number有特殊的作用,例如用于级联的那个IRQ number是不能request。irq_settings_can_request函数就是判断一个IRQ是否可以被request

irq_settings_is_per_cpu_devid函数用来判断一个中断描述符是否需要传递per cpu的device ID。per cpu的中断上面已经描述的很清楚了,这里不再细述。如果一个中断描述符对应的中断ID是per cpu的,那么在申请其handler的时候就有两种情况,一种是传递统一的dev_id参数(传入request_threaded_irq的最后一个参数),另外一种情况是针对每个CPU,传递不同的dev_id参数。在这种情况下,我们需要调用request_percpu_irq接口函数而不是request_threaded_irq。

(4)传入request_threaded_irqprimary handlerthreaded handler参数有下面四种组合:

primary handler threaded handler 描述
NULL NULL 函数出错,返回-EINVAL
设定 设定 正常流程。中断处理被合理的分配到primary handler和threaded handler中。
设定 NULL 中断处理都是在primary handler中完成
NULL 设定 这种情况下,系统会帮忙设定一个default的primary handler:irq_default_primary_handler,协助唤醒threaded handler线程

(5)这部分的代码很简单,分配struct irqaction,赋值,调用__setup_irq进行实际的注册过程。这里要罗嗦几句的是锁的操作,在内核中,有很多函数,有的是需要调用者自己加锁保护的,有些是不需要加锁保护的。对于这些场景,linux kernel采取了统一的策略:基本函数名字是一样的,只不过需要调用者自己加锁保护的那个函数需要增加__的前缀,例如内核有有下面两个函数:setup_irq和__setup_irq。这里,我们在setup irq的时候已经调用chip_bus_lock进行保护,因此使用lock free的版本__setup_irq。

chip_bus_lock定义如下:

static inline void chip_bus_lock(struct irq_desc *desc) 
{ if (unlikely(desc->irq_data.chip->irq_bus_lock)) desc->irq_data.chip->irq_bus_lock(&desc->irq_data); 
}

大部分的interrupt controller并没有定义irq_bus_lock这个callback函数,因此chip_bus_lock这个函数对大多数的中断控制器而言是没有实际意义的。但是,有些interrupt controller是连接到慢速总线上的,例如一个i2c接口的IO expander芯片(这种芯片往往也提供若干有中断功能的GPIO,因此也是一个interrupt controller),在访问这种interrupt controller的时候需要lock住那个慢速bus(只能有一个client在使用I2C bus)。

4.2 注册irqaction

4.2.1 nested IRQ的处理代码

在看具体的代码之前,我们首先要理解什么是nested IRQ。nested IRQ不是cascade IRQ,在GIC代码分析中我们有描述过cascade IRQ这个概念,主要用在interrupt controller级联的情况下。为了方便大家理解,我还是给出一个具体的例子吧,具体的HW block请参考下图:

config

上图是一个两个GIC级联的例子,所有的HW block封装在了一个SOC chip中。为了方便描述,我们先进行编号:Secondary GICIRQ number是A外设1的IRQ number是B,外设2的IRQ number是C。对于上面的硬件,linux kernel处理如下:

(a)IRQ A中断描述符被设定为不能注册irqaction(不能注册specific interrupt handler!!!,或者叫中断服务程序)

(b)IRQ Ahigh level irq-events handler(处理interrupt flow control!!!)负责进行IRQ number的映射,在其irq domain上翻译出具体外设的IRQ number,并重新定向外设IRQ number对应的highlevel irq-events handler(!!!)。

(c)所有外设驱动的中断正常request irq,可以任意选择线程化的handler,或者只注册primary handler

需要注意的是,对root GICSecondary GIC寄存器的访问非常快,因此ack、mask、EOI等操作也非常快。

我们再看看另外一个interrupt controller级联的情况:

config

IO expander HW block提供了有中断功能的GPIO,因此它也是一个interrupt controller,有它自己的irq domain和irq chip。上图中外设1和外设2使用了IO expander上有中断功能的GPIO,它们有属于自己的IRQ number以及中断描述符。IO expander HW block的IRQ line连接到SOC内部的interrupt controller上,因此,这也是一个interrupt controller级联的情况,对于这种情况,我们是否可以采用和上面GIC级联的处理方式呢?

不行,对于GIC级联的情况,如果secondary GIC上的外设1产生了中断,整个关中断的时间是IRQ A的中断描述符的highlevel irq-events handler处理时间+IRQ B的的中断描述符的highlevel irq-events handler处理时间+外设1的primary handler的处理时间。所幸对root GIC和Secondary GIC寄存器的访问非常快,因此整个关中断的时间也不是非常的长。但是如果是IO expander这个情况,如果采取和上面GIC级联的处理方式一样的话,关中断的时间非常长。我们还是用外设1产生的中断为例子好了。这时候,由于IRQ B的的中断描述符的highlevel irq-events handler处理设计I2C的操作,因此时间非常的长,这时候,对于整个系统的实时性而言是致命的打击。对这种硬件情况,linux kernel处理如下:

(a)IRQ A的中断描述符的highlevel irq-events handler根据实际情况进行设定,并且允许注册irqaction。对于连接到IO expander上的外设,它是没有real time的要求的(否则也不会接到IO expander上),因此一般会进行线程化处理。由于threaded handler中涉及I2C操作,因此要设定IRQF_ONESHOT的flag。

(b)在IRQ A的中断描述符的threaded interrupt handler中进行进行IRQ number的映射,在IO expander irq domain上翻译出具体外设的IRQ number,并直接调用handle_nested_irq函数处理该IRQ。

(c)外设对应的中断描述符设定IRQ_NESTED_THREAD的flag,表明这是一个nested IRQ。nested IRQ没有highlevel irq-events handler,也没有primary handler,它的threaded interrupt handler是附着在其parent IRQ的threaded handler上的。

具体的nested IRQ的处理代码如下:

static int __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) 
{
…… nested = irq_settings_is_nested_thread(desc); if (nested) { if (!new->thread_fn) { ret = -EINVAL; goto out_mput; } new->handler = irq_nested_primary_handler; } else {  
…… }
……
}

如果一个中断描述符是nested thread type的,说明这个中断描述符应该设定threaded interrupt handler(当然,内核是不会单独创建一个thread的,它是借着其parent IRQinterrupt thread执行),否则就会出错返回。对于primary handler,它应该没有机会被调用到,当然为了调试,kernel将其设定为irq_nested_primary_handler,以便在调用的时候打印一些信息,让工程师直到发生了什么状况。

4.2.2 forced irq threading处理

具体的forced irq threading的处理代码如下:

static int __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) 
{
……nested = irq_settings_is_nested_thread(desc); if (nested) {  
……} else { if (irq_settings_can_thread(desc)) irq_setup_forced_threading(new); }
……
}

forced irq threading其实就是将系统中所有可以被线程化的中断handler全部线程化,即便你在request irq的时候,设定的是primary handler,而不是threaded handler。当然那些不能被线程化的中断(标注了IRQF_NO_THREAD的中断,例如系统timer)还是排除在外的。irq_settings_can_thread函数就是判断一个中断是否可以被线程化,如果可以的话,则调用irq_setup_forced_threading在set irq的时候强制进行线程化。具体代码如下:

static void irq_setup_forced_threading(struct irqaction *new) 
{ if (!force_irqthreads)-------(a) return; if (new->flags & (IRQF_NO_THREAD | IRQF_PERCPU | IRQF_ONESHOT))-------(b) return;new->flags |= IRQF_ONESHOT; -------(d)if (!new->thread_fn) {----(c) set_bit(IRQTF_FORCED_THREAD, &new->thread_flags); new->thread_fn = new->handler; new->handler = irq_default_primary_handler; } 
}

(a)系统中有一个强制线程化的选项:CONFIG_IRQ_FORCED_THREADING,如果没有打开该选项,force_irqthreads总是0,因此irq_setup_forced_threading也就没有什么作用,直接return了。如果打开了CONFIG_IRQ_FORCED_THREADING,说明系统支持强制线程化,但是具体是否对所有的中断进行强制线程化处理还是要看命令行参数threadirqs。如果kernel启动的时候没有传入该参数,那么同样的,irq_setup_forced_threading也就没有什么作用,直接return了。只有bootloader向内核传入threadirqs这个命令行参数,内核才真正在启动过程中,进行各个中断的强制线程化的操作。

(b)看到IRQF_NO_THREAD选项你可能会奇怪,前面irq_settings_can_thread函数不是检查过了吗?为何还要重复检查?其实一个中断是否可以进行线程化可以从两个层面看:一个是从底层看,也就是从中断描述符、从实际的中断硬件拓扑等方面看。另外一个是从中断子系统的用户层面看,也就是各个外设在注册自己的handler的时候是否想进行线程化处理。所有的IRQF_XXX都是从用户层面看的flag,因此如果用户通过IRQF_NO_THREAD这个flag告知kernel,该interrupt不能被线程化,那么强制线程化的机制还是尊重用户的选择的。

PER CPU的中断都是一些较为特殊的中断,不是一般意义上的外设中断,因此对PER CPU的中断不强制进行线程化。IRQF_ONESHOT选项说明该中断已经被线程化了(而且是特殊的one shot类型的),因此也是直接返回了。

(c)强制线程化只对那些没有设定thread_fn的中断进行处理,这种中断将全部的处理放在了primary interrupt handler中(当然,如果中断处理比较耗时,那么也可能会采用bottom half的机制),由于primary interrupt handler是全程关闭CPU中断的,因此可能对系统的实时性造成影响,因此考虑将其强制线程化。struct irqaction中的thread_flags是和线程相关的flag,我们给它打上IRQTF_FORCED_THREAD的标签,表明该threaded handler是被强制threaded的。new->thread_fn = new->handler这段代码表示将原来primary handler中的内容全部放到threaded handler中处理,新的primary handler被设定为default handler。

(d)强制线程化是一个和实时性相关的选项,从我的角度来看是一个很不好的设计(个人观点),各个驱动工程师在撰写自己的驱动代码的时候已经安排好了自己的上下文环境。有的是进程上下文,有的是中断上下文,在各自的上下文环境中,驱动工程师又选择了适合的内核同步机制。但是,强制线程化导致原来运行在中断上下文的primary handler现在运行在进程上下文,这有可能导致一些难以跟踪和定位的bug。

当然,作为内核的开发者,既然同意将强制线程化这个特性并入linux kernel,相信他们有他们自己的考虑。我猜测这是和一些旧的驱动代码维护相关的。linux kernel中的中断子系统的API的修改会引起非常大的震动,因为内核中成千上万的驱动都是需要调用旧的接口来申请linux kernel中断子系统的服务,对每一个驱动都进行修改是一个非常耗时的工作,为了让那些旧的驱动代码可以运行在新的中断子系统上,因此,在kernel中,实际上仍然提供了旧的request_irq接口函数,如下:

static inline int __must_check 
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev) 
{ return request_threaded_irq(irq, handler, NULL, flags, name, dev); 
}

接口是OK了,但是,新的中断子系统的思路是将中断处理分成primary handler和threaded handler,而旧的驱动代码一般是将中断处理分成top half和bottom half,如何将这部分的不同抹平?linux kernel是这样处理的(这是我个人的理解,不保证是正确的):

(d-1)内核为那些被强制线程化的中断handler设定了IRQF_ONESHOT的标识。这是因为在旧的中断处理机制中,top half是不可重入的,强制线程化之后,强制设定IRQF_ONESHOT可以保证threaded handler是不会重入的。

(d-2)在那些被强制线程化的中断线程中,disable bottom half的处理。这是因为在旧的中断处理机制中,botton half是不可能抢占top half的执行,强制线程化之后,应该保持这一点。

4.2.3 创建interrupt线程

代码如下:

if (new->thread_fn && !nested) { struct task_struct *t; static const struct sched_param param = { .sched_priority = MAX_USER_RT_PRIO/2, };t = kthread_create(irq_thread, new, "irq/%d-%s", irq,---(a) new->name);sched_setscheduler_nocheck(t, SCHED_FIFO, ¶m);get_task_struct(t);---(b) new->thread = t; set_bit(IRQTF_AFFINITY, &new->thread_flags);---(c) 
}if (!alloc_cpumask_var(&mask, GFP_KERNEL)) {---(d) ret = -ENOMEM; goto out_thread; 
} 
if (desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)--(e) new->flags &= ~IRQF_ONESHOT;

(a)调用kthread_create来创建一个内核线程,并调用sched_setscheduler_nocheck来设定这个中断线程的调度策略和调度优先级。这些是和进程管理相关的内容,我们留到下一个专题再详细描述吧。

(b)调用get_task_struct可以为这个threaded handler的task struct增加一次reference count,这样,即便是该thread异常退出也可以保证它的task struct不会被释放掉。这可以保证中断系统的代码不会访问到一些被释放的内存。irqaction的thread 成员被设定为刚刚创建的task,这样,primary handler就知道唤醒哪一个中断线程了。

(c)设定IRQTF_AFFINITY的标志,在threaded handler中会检查该标志并进行IRQ affinity的设定。

(d)分配一个cpu mask的变量的内存,后面会使用到

(e)驱动工程师是撰写具体外设驱动的,他/她未必会了解到底层的一些具体的interrupt controller的信息。有些interrupt controller(例如MSI based interrupt)本质上就是就是one shot的(通过IRQCHIP_ONESHOT_SAFE标记),因此驱动工程师设定的IRQF_ONESHOT其实是画蛇添足,因此可以去掉。

4.2.4 共享中断的检查

代码如下:

old_ptr = &desc->action; 
old = *old_ptr;if (old) { if (!((old->flags & new->flags) & IRQF_SHARED) ||--(a) ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK) || ((old->flags ^ new->flags) & IRQF_ONESHOT)) goto mismatch;/* All handlers must agree on per-cpuness */ if ((old->flags & IRQF_PERCPU) != (new->flags & IRQF_PERCPU)) goto mismatch;/* add new interrupt at end of irq queue */ do {---------(b) thread_mask |= old->thread_mask; old_ptr = &old->next; old = *old_ptr; } while (old); shared = 1; 
}

(a)old指向注册之前的action list,如果不是NULL,那么说明需要共享interrupt line。但是如果要共享,需要每一个irqaction都同意共享(IRQF_SHARED),每一个irqaction的触发方式相同(都是level trigger或者都是edge trigger),相同的oneshot类型的中断(都是one shot或者都不是),per cpu类型的相同中断(都是per cpu的中断或者都不是)。

(b)将该irqaction挂入队列的尾部。

4.2.5 thread mask的设定

代码如下:

if (new->flags & IRQF_ONESHOT) { if (thread_mask == ~0UL) {---(a) ret = -EBUSY; goto out_mask; } new->thread_mask = 1 << ffz(thread_mask);} else if (new->handler == irq_default_primary_handler && !(desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)) {-----(b) ret = -EINVAL; goto out_mask; }

对于one shot类型的中断,我们还需要设定thread mask。如果一个one shot类型的中断只有一个threaded handler(不支持共享),那么事情就很简单(临时变量thread_mask等于0),该irqaction的thread_mask成员总是使用第一个bit来标识该irqaction。但是,如果支持共享的话,事情变得有点复杂。我们假设这个one shot类型的IRQ上有A,B和C三个irqaction,那么A,B和C三个irqaction的thread_mask成员会有不同的bit来标识自己。例如A的thread_mask成员是0x01,B的是0x02,C的是0x04,如果有更多共享的irqaction(必须是oneshot类型),那么其thread_mask成员会依次设定为0x08,0x10……

(a)在上面“共享中断的检查”这个p中,thread_mask变量保存了所有的属于该interrupt line的thread_mask,这时候,如果thread_mask变量如果是全1,那么说明irqaction list上已经有了太多的irq action(大于32或者64,和具体系统和编译器相关)。如果没有满,那么通过ffz函数找到第一个为0的bit作为该irq action的thread bit mask。

(b)irq_default_primary_handler的代码如下:

static irqreturn_t irq_default_primary_handler(int irq, void *dev_id) 
{ return IRQ_WAKE_THREAD; 
}

代码非常的简单,返回IRQ_WAKE_THREAD,让kernel唤醒threaded handler就OK了。使用irq_default_primary_handler虽然简单,但是有一个风险:如果是电平触发的中断,我们需要操作外设的寄存器才可以让那个asserted的电平信号消失,否则它会一直持续。一般,我们都是直接在primary中操作外设寄存器(slow bus类型的interrupt controller不行),尽早的clear interrupt,但是,对于irq_default_primary_handler,它仅仅是wakeup了threaded interrupt handler,并没有clear interrupt,这样,执行完了primary handler,外设中断仍然是asserted,一旦打开CPU中断,立刻触发下一次的中断,然后不断的循环。因此,如果注册中断的时候没有指定primary interrupt handler,并且没有设定IRQF_ONESHOT,那么系统是会报错的。当然,有一种情况可以豁免,当底层的irq chip是one shot safe的(IRQCHIP_ONESHOT_SAFE)。

4.2.6 用户IRQ flag和底层interrupt flag的同步

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. 9. tasklet[有完整中断(包括top和bottom half)调用示例]

    1 前言 2 为什么需要tasklet?2.1 基本的思考 2.2 对linux中的bottom half机制的思考3 tasklet的基本原理3.1 如何抽象一个tasklet 3.2 系统如何管理tasklet? 3.3 如何定义一个tasklet? 3.4 如何调度一个tasklet 3.5 在什么时机会执行tasklet?1 前言 对于中断处理而言,linu…...

    2024/4/24 9:46:57
  2. 14. 中断唤醒系统流程

    1 前言 2 中断唤醒流程2.1 enable_irq_wake 2.2 Suspend to RAM流程 2.3 resume流程 2.4 system core operations有什么用? 2.5 gic interrupt controller中断执行流程 2.6 dpm_resume_noirq()1 前言 曾几何时,不知道你是否想过外部中断是如何产生的呢?又是如何唤醒系统的呢…...

    2024/4/29 10:33:29
  3. HTTPS工作原理

    当你打开浏览器,访问某个网站,如果网址旁有个小锁,代表访问的网址是安全的,反之不安全。当我们没有看到那个小锁的小图标的时候,需要提高警惕,不要随意输入个人重要的资料。所有的银行和支付相关的网站都是 100% 使用 HTTPS 的。为什么需要 HTTPS?我们为什么需要 HTTPS?…...

    2024/4/24 9:46:47
  4. 开始好好学习 开始写博客吧

    总之开始试着写一下学习博客,希望能有所收获 浑浑噩噩几年,感觉自己没学到什么东西,疫情期间在家学习实在是没有自觉。 咬牙努力下,开始学着写一下博客,记录下接下来的变成学习生活。 博客内容:平时作业学到的内容(当一个笔记本)、前端学习(目标)、课余感兴趣的知识记…...

    2024/4/28 12:48:08
  5. 5月份Github上最热门的开源项目,已经新鲜出炉了!!

    5月份GitHub上最热门的开源项目排行已经出炉啦,一起来看看上榜详情吧: 1,deno https://github.com/denoland/deno Star 61499 Deno 是 V8 上的安全 TypeScript 运行时。部分特性如下:支持 TypeScript 2.8 开箱即用,使用 V8 6.8.275.3 引擎 无 package.json、npm,不追求兼…...

    2024/5/2 16:05:51
  6. Android Studio开发APP过程中发现的优质参考博客

    作为一个应届毕业生,完成了两个APP的创作之后,现在开始管理收藏夹,所以将收藏夹的文章转移到此处。 显然不是全部搜索的文章,在学习过程中你会搜索很多很多很多很多的文章,然后进行信息筛选和综合。这里只是冰山一角,还有一些确实很优秀但是内容比较精炼,被我吃透之后就…...

    2024/4/16 9:47:16
  7. Capsule Network Performance on Complex Data 翻译

    1. 简介近年来,卷积神经网络(CNN)在深度学习领域中发挥了重要作用。事实证明,CNN的变体在跨不同领域的分类任务中非常成功。但是,CNN的主要缺点有两个:无法考虑要素之间的重要空间层次结构以及缺乏旋转不变性[1]。只要在测试数据中存在某个对象的某些关键特征,CNN便会将…...

    2024/4/16 9:47:36
  8. 一文搞懂nmcli不能tab补全和全部使用方法

    文章目录nmcli命令无法tab补全 解决方案nmcli说明注意事项nmcli命令合集查看合集添加网络连接参数介绍合集添加单网卡实例修改网络连接单项参数介绍合集修改已经配置好的参数测试激活禁用删除等介绍合集总结测试实例添加单网卡实例nmcli做一个team双网卡绑定(链路聚合) nmcli…...

    2024/4/16 9:48:13
  9. 由文章中英文字母出现频率分析破解密文

    原题: 使用频率分析法,尝试破译密文: WB WI KJB MK RMIT BMIQ BJ RASHMWK RMVP YJERYRKB MKD WBI IWOKWXWVMKVR MKDIJYR YNIB URYMWK NKRASHMWKRD BJ OWER M VJYSHRBR RASHMKMBWJK JKR CJNHD PMER BJ LR FNMHWXWRD MKD WKISWURD BJ INVP MK RABRKB BPMB PR VJNHD URMVP BP…...

    2024/4/18 5:30:02
  10. C++设计模式——单例模式(Singleton Pattern)

    C++设计模式——单例模式(Singleton Pattern)微信公众号:幼儿园的学霸目录 文章目录C++设计模式——单例模式(Singleton Pattern)目录定义代码示例懒汉模式线程/内存不安全方式智能指针+双检锁模式和智能指针+call_once模式局部静态变量模式饿汉模式总结参考资料 定义 单例模式…...

    2024/4/15 6:03:04
  11. Vue基础学习

    到公司第一天,把快遗忘的vue再看一遍官网的文档。 Vue.js是什么? Vue.js是一套用于构建用户界面的渐进式框架。 声明式渲染 下面展示一些 内联代码片。 <div id="app"> {{mesage}} </div>var app = new Vue({el:#app,data:{message:Hello Vue!} })结果…...

    2024/4/16 9:48:23
  12. 人人都是产品经理2.0-01章摘要总结

    人人都是产品经理2.0-01章摘要总结1.1从一个小故事谈起(伪需求)1.2产品经理的前世今生1.3思维方式与性格特点1.3.1从学生到职场1.3.2从用户到产品经理1.3.3从现象到本质1.3.4还有什么性格特质是加分项1.4产品经理的日常1.5延伸阅读与练习 1.1从一个小故事谈起(伪需求)反映出…...

    2024/4/19 8:56:40
  13. 实验三面向对象(上)

    实验目的: 1.了解类的成员设计。 2.掌握类与对象的关系。 3. 掌握构造方法的重载及调用、掌握static关键字的、this关键字。 4.了解内部类的特点及使用。 实验内容: 1.编写Java应用程序,定义时钟类Clock,包含3个int型成员变量hour,minute,second,分别表示时,分,秒,…...

    2024/4/24 9:46:45
  14. 三、Linux进阶——Apache(web服务器部署)

    1. Apache的作用 在web被访问时通常使用http://方式 http:// 文本传输协议 http:// 超文本传输协议提供软件: Apache:目前主流 nginx:轻量级 stgw:腾讯 jfe:京东 Tengine:阿里巴巴 2. Apache的安装 dnf install httpd.x86_64 -y3. Apache的启用 1、启用Apache,并设定…...

    2024/4/24 9:46:44
  15. 部署一套完整的Kubernetes高可用集群(上)

    部署一套完整的Kubernetes高可用集群(上)原创 阿良 DevOps技术栈 今天目录[-]一、前置知识点1.1 生产环境可部署Kubernetes集群的两种方式目前生产部署Kubernetes集群主要有两种方式:kubeadmKubeadm是一个K8s部署工具,提供kubeadm init和kubeadm join,用于快速部署Kuberne…...

    2024/4/24 9:46:44
  16. redux4 - 处理有多个 reducer时如何合并,以及实现 combineReducers

    参考源码redux仓库 redux原则之一就是单一性,即状态,仓库,reducer都是唯一的,但在组件式的技术开发中,为了好管理状态,通常一个组件,或嵌套组件对应一个 reducer, 最后再合并成一个大的reducer代码太多,请参考演示代码 代码演示两个组件的都有各自的组件状态,怎么进行…...

    2024/4/24 9:46:42
  17. 【华为云技术分享】低代码开发平台发展趋势:低代码——炒作还是趋势?

    在《人月神话》的开篇提到焦油坑,没有别的场景比巨兽在焦油坑中垂死挣扎的场面更令人震撼。上帝见证着恐龙、猛犸象、剑齿虎在焦油中挣扎。他们挣扎的越是猛烈,焦油纠缠的越紧,没有任何猛兽足够壮烈或具有足够的技巧,能够挣扎束缚,他们最后都沉到了坑底。大型软件系统开发…...

    2024/4/24 9:46:41
  18. UNSW 第五学期 总结

    我真是服了,老遇上百年未有之变局,先是建校以来第一次改成了三学期制,现在又遇上全球疫情。人生,刺激啊。 这学期最大的问题就是,由于校内所有建筑尽数封闭,我完全找不到一个自习的地方,之前所总结的:千万不能在宿舍里学习,哦豁,其解决方案直接作废。现在只能在宿舍里…...

    2024/4/24 9:46:40
  19. 对后疫情时代数字资产投资策略思考

    对后疫情时代数字资产投## 标题对后疫情时代数字资产投资策略思考 全球正在面临一次压力测试,各行各业面临严峻考验,对任何数字化技术和数字化行业都是“危中有机”的,区块链也不例外。 在各个社区,关于后疫情时代数字资产投资策略的讨论总是热点问题。 “疫情是否会对区块…...

    2024/4/24 9:46:39
  20. 超炫酷的可视化,大屏数据展示组件库-dataV组件库,“react-vue -组件库”

    对可视化有所了解的应该都知道,某云平台的一款datav大屏可视化的工具,作者前年买了个个人版的,直接在页面上拖住就可以了确实很强。最近平台发来邮件,说我的个人版datav到期了,本来想续租,发现之前的个人版下架了,只剩下企业版、专业版、至尊版,最便宜的企业版一年4800…...

    2024/5/5 5:12:04

最新文章

  1. VSCode 配置 Qt 开发环境

    文章目录 1. 环境说明2. 配置系统环境变量 1. 环境说明 操作系统&#xff1a;Windows 11VSCode版本&#xff1a;1.88.1CMake版本&#xff1a;3.27.7Qt6版本&#xff1a;6.7.0(MinGW 11.2.0 64-bit) 2. 配置系统环境变量 自行根据自己的Qt安装路径配置 配置 MinGW 和 CMake C…...

    2024/5/5 13:30:37
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. HTML——3.链接、头部、图像

    一、链接 HTML 中的链接由 <a> 标签定义&#xff0c;用于创建可点击的文本或图像&#xff0c;以便导航到其他页面或资源。下面是一个简单的 HTML 链接示例&#xff1a; <a href"https://www.example.com">Visit Example</a> 在这个示例中&#…...

    2024/5/4 15:09:26
  4. Kimi精选提示词,总结PPT内容

    大家好&#xff0c;我是子云&#xff0c;最近真是觉得Kimi这个大模型&#xff0c;产品体验很棒&#xff0c;能力也是不错&#xff0c;感觉产品经理用心了。 发现一个Kimi 一个小技巧&#xff0c;可以学习到很多高级提示词。 Kimi输入框可以配置常用提示词&#xff0c;同时也可…...

    2024/5/4 11:52:56
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/4 23:54:56
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/4 23:54:56
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/5/4 23:54:56
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/4 23:55:17
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/5/4 23:54:56
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/5/4 23:55:05
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/5/4 23:54:56
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/5/4 23:55:16
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/5/4 23:54:56
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/4 18:20:48
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/5/4 23:54:56
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/5/4 23:55:17
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/5/4 23:55:06
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/5/4 23:54:56
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/4 23:55:06
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/5/5 8:13:33
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/5/4 23:55:16
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/5/4 23:54:58
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/4 23:55:01
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/5/4 23:54:56
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57