linux内核-软中断与Bottom Half

2023-11-19

中断服务一般都是在将中断请求关闭的条件下执行的,以避免嵌套而使控制复杂化。可是,如果关中断的时间持续太长就可能因为CPU不能及时响应其他的中断请求而使中断(请求)丢失,为此,内核允许在将具体的中断服务程序挂入中断请求队列时将SA_INTERRUPT标志置成0,使这个中断服务程序在开中断的条件下执行。然而,实际的情况往往是:若是服务的全过程关中断则扩大打击面,而全称开中断则又造成不安定因素,很难取舍。一般来说,一次中盾服务的过程常常可以分成两部分。开头的部分往往是必须在关中断条件下执行的。这样才能在不受干扰的条件下原子地完成一些关键性操作。同时,这部分操作的时间性又往往很强,必须在中断请求发生后立即或至少是在一定的时间现值中完成,而且相继的多次中断请求也不能合并在一起处理。而后半部分,则通常可以、而且应该在开中断条件下执行,这样才不至于因将中断关闭过久而造成其他中断的丢失。同时,这些操作常常允许延迟到稍后才能执行,而且有可能将多次中断服务中的相关部分合并在一起处理。这里的后半部分就称为bottom half,在内核代码中常常缩写成bh。这个概念在相当程度上来自RISC系统结构。在RISC的CPU中,通常由大量的寄存器。当中断发生时,要将所有这些寄存器的内容压栈,并在返回时加以恢复,为此而付出很高的代价。所以,在RISC结构的系统中往往把中断服务分成两部分。第一部分只保存为数不多的寄存器(内容),并利用这些为数不多的寄存器来完成有限的关键性的操作,称为轻量级中断。而另一部分,那就相当于这里的bh了。虽然i386的结构主要是CISC的,面临的问题不尽相同,但前述的问题已经使bh的必要性在许多情况下变得很明显了。

linux内核为将中断服务非诚两半提供了方便,并设立了相应的机制。在以前的内核中,这个机制就称为bh。但是,在2.4版(确切的说是2.3.43)中有了新的发展和推广。

以前的内核中设置了一个函数指针数组bh_base,其大小为32,数值中的每个指针可以用来指向一个具体的bh函数。同时,又设置了两个32位无符号整数bh_active和bh_mask(2.3.38版本),每个无符号的整数中的32位对应着数组bh_base中的32个元素。

我们可以在中断与bh二者之间建立起一种类比。

  1. 数组bh_base相当于硬件中断机制中的数组irq_desc。不过irq_desc中的每个元素代表着一个中断通道,所以是一个中断服务程序队列。而bh_base中的每个元素却最多只能代表一个bh函数。但是,尽管如此,二者在概念上还是相同的。
  2. 无符号整数bh_active在概念上相当于硬件的中断请求寄存器,而bh_mask则相当于中断屏蔽寄存器。
  3. 需要执行一个bh函数时,就通过一个函数mark_bh将bh_active中的某一位设成1,相当于中断源发生了中断请求,而所设置的具体标志位则类似于中断向量。
  4. 如果相当于中断屏蔽寄存器的bh_mask中相应位也是1,即系统允许这个bh函数,那么就会在每次执行完do_IRQ中的中断服务程序后,以及每次系统调用结束之时,在一个函数do_bottom_half中执行相应的bh函数。而do_bottom_half,则类似于do_IRQ。

为了简化bh函数的设计,在do_bottom_half中也像do_IRQ中一样,把bh函数的执行严格地串行化了。这种串行化有两方面的考虑和措施:

一方面,bh函数的执行不允许嵌套。如果在执行bh函数的过程中发生中断,那么由于每次中断服务以后在do_IRQ中都要检查和处理bh函数的执行,就有可能嵌套。为此,在do_bottom_half中针对同一CPU上的嵌套执行加了锁,这样,如果进入do_bottom_half以后发现已经加了锁,就立即返回。因为这说明CPU在本次中断发生之前已经在这个函数中了。

另一方面,是在多CPU系统中,在同一时间内最多只允许一个CPU执行bh函数,以防有两个甚至更多个CPU同时来执行bh函数而互相干扰。为此在do_bottom_half中针对不同CPU同时执行bh函数也加了锁。这样,如果进入do_bottom_half以后发现这个锁已经锁上,就说明已经由CPU在执行bh函数,所以也立即返回。

这两条措施,特别是第二条措施,保证了从单CPU结构到多CPU SMP结构的平稳过渡。可是,在当时的linux内核可以在多CPU SMP结构上稳定运行以后,就慢慢发现这样的处理对于多CPU SMP结构的性能有不利的影响。原因就在于上述的第二条措施使bh函数的执行完全串行化了。当系统中有很多bh函数需要执行时,虽然系统中有多个CPU存在,却只有一个CPU这么一个独木桥。跟do_IRQ做一比较就可以发现,在do_IRQ中的串行化只是针对一个具体中断通道到的,而bh函数的串行化却是全局性的,所以是防卫过当了。既然如此,就应该考虑放宽上述的第二条措施。但是,如果放宽了这一条,就要对bh函数本身的设计和实现有更高的要求(例如对使用全局变量的互斥),而原来已经存在的bh函数显然不符合这些要求。所以,比较好的办法是保留bh。另外再增设一种或几种机制。并把它们纳入一个统一的框架中。这就是2.4版中的软中断(softirq)机制。

从字面上说softirq就是软中断,可是软中断这个词(尤其是在中文里)已经被用作信号(signal)的代名词,因为信号实际上就是以软件手段实现的中断机制。但是,另一方面,把类似于bh的机制称为软中断又确实很贴切。这一方面反映了上述bh函数与中断之间的类比,另一方面也反映了这是一种在时间要求上更为软性的中断请求。实际上,这里所体现的是层次的不同。如果说硬中断通常是外部设备对CPU的中断,那么softirq通常是硬中断服务程序对内核的中断,而信号则是由内核(或其他进程)对某个进程的中断。后面这二者都是由软件产生的软中断。所以,对软中断这次的含义要根据上下文加以区分。

下面,我们以bh函数为主线,通过阅读代码来叙述2.4版内核的软中断(softirq)机制。

系统在初始化时通过函数softirq_init对内核的软中断机制进行初始化。其代码如下:

void __init softirq_init()
{
	int i;

	for (i=0; i<32; i++)
		tasklet_init(bh_task_vec+i, bh_action, i);

	open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
	open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
}

软中断本身是一种机制,同时也使一个框架。在这个框架里有bh机制,这是一种特殊的软中断,也可以说是设计最保守的,但却是最简单、最安全的软中断。除此之外,还有其他的软中断,定义如下:

enum
{
	HI_SOFTIRQ=0,
	NET_TX_SOFTIRQ,
	NET_RX_SOFTIRQ,
	TASKLET_SOFTIRQ
};

这里最值得注意的是TASKLET_SOFTIRQ,代表着一种称为tasklet的机制。也许采用tasklet这个词的原意在于表示这是一片小小的任务,但是这个词容易使人联想到task即进程而引起误会,其实这二者毫无关系。显然,NET_TX_SOFTIRQ和NET_RX_SOFTIRQ两种软中断是专为网络操作而设的,所以在softirq_init中只对TASKLET_SOFTIRQ和HI_SOFTIRQ两种软中断进行初始化。

先看bh机制的初始化。内核中为bh机制设置了一个结构数组bh_task_vec,这是tasklet_struct数据结构的数组。这种数据结构的定义也在interrupt.h中:

/* Tasklets --- multithreaded analogue of BHs.

   Main feature differing them of generic softirqs: tasklet
   is running only on one CPU simultaneously.

   Main feature differing them of BHs: different tasklets
   may be run simultaneously on different CPUs.

   Properties:
   * If tasklet_schedule() is called, then tasklet is guaranteed
     to be executed on some cpu at least once after this.
   * If the tasklet is already scheduled, but its excecution is still not
     started, it will be executed only once.
   * If this tasklet is already running on another CPU (or schedule is called
     from tasklet itself), it is rescheduled for later.
   * Tasklet is strictly serialized wrt itself, but not
     wrt another tasklets. If client needs some intertask synchronization,
     he makes it with spinlocks.
 */

struct tasklet_struct
{
	struct tasklet_struct *next;
	unsigned long state;
	atomic_t count;
	void (*func)(unsigned long);
	unsigned long data;
};

代码的作者加了详细的注释,说tasklet是多序(不是多进程或多线程!)的bh函数。为什么这么说呢?因为对tasklet的串行化不像对bh函数那样严格,所以允许在不同的CPU上同时执行tasklet,但必须是不同的tasklet。一个tasklet_struct数据结构就代表这一个tasklet,结构中的函数指针func指向其服务程序。那么,为什么bh机制中要使用这种数据结构呢?这是因为bh函数的执行(并不是bh函数本身)就是作为一个tasklet来实现的,在此基础上再加上更严格的限制,就成了bh。

函数tasklet_init的代码在kernel/softirq.c中:

softirq_init=>tasklet_init

void tasklet_init(struct tasklet_struct *t,
		  void (*func)(unsigned long), unsigned long data)
{
	t->func = func;
	t->data = data;
	t->state = 0;
	atomic_set(&t->count, 0);
}

在softirq_init中,对于bh的32个tasklet_struct结构调用tasklet_init以后,它们的函数指针func全都指向bh_action。

对其他软中断的初始化是通过open_softirq完成的,其代码如下:

softirq_init=>open_softirq


void open_softirq(int nr, void (*action)(struct softirq_action*), void *data)
{
	unsigned long flags;
	int i;

	spin_lock_irqsave(&softirq_mask_lock, flags);
	softirq_vec[nr].data = data;
	softirq_vec[nr].action = action;

	for (i=0; i<NR_CPUS; i++)
		softirq_mask(i) |= (1<<nr);
	spin_unlock_irqrestore(&softirq_mask_lock, flags);
}

内核中为软中断设置了一个以软中断号为下标的数组softirq_vec,类似于中断机制的irq_desc。

static struct softirq_action softirq_vec[32] __cacheline_aligned;

这是一个softirq_action数据结构的数组,其定义为:

/* softirq mask and active fields moved to irq_cpustat_t in
 * asm/hardirq.h to get better cache usage.  KAO
 */

struct softirq_action
{
	void	(*action)(struct softirq_action *);
	void	*data;
};

数组softirq_vec是个全局变量,系统中的各个CPU所看到的是同一个数组。但是,每个CPU各有其自己的软中断控制、状况结构,所以这些数据结构形成一个以CPU编号为下标的数组irq_stat。这个数组也是全局变量,但是各个CPU可以按其自身编号的访问相应的数据结构。我们把有关的定义列出于下,供读者自己阅读:


/* entry.S is sensitive to the offsets of these fields */
typedef struct {
	unsigned int __softirq_active;
	unsigned int __softirq_mask;
	unsigned int __local_irq_count;
	unsigned int __local_bh_count;
	unsigned int __syscall_count;
	unsigned int __nmi_count;	/* arch dependent */
} ____cacheline_aligned irq_cpustat_t;


irq_cpustat_t irq_stat[NR_CPUS];


#ifdef CONFIG_SMP
#define __IRQ_STAT(cpu, member)	(irq_stat[cpu].member)
#else
#define __IRQ_STAT(cpu, member)	((void)(cpu), irq_stat[0].member)
#endif	

  /* arch independent irq_stat fields */
#define softirq_active(cpu)	__IRQ_STAT((cpu), __softirq_active)
#define softirq_mask(cpu)	__IRQ_STAT((cpu), __softirq_mask)

数据结构中__softirq_active相当于软中断请求寄存器,__softirq_mask则相当于软中断屏蔽寄存器。函数open_softirq除把函数指针action填入softirq_vec中的相应元素外,还把所有CPU的中断屏蔽寄存器中的相应位设置成1,使这个软中断在每个CPU上都可以执行。从softirq_init中调用open_softirq把TASKLET_SOFTIRQ和HI_SOFTIRQ两个软中断的处理程序分别设置成tasklet_action和tasklet_hi_action。

内核中还有另一个以CPU编号为下标的数组tasklet_hi_vec,这是tasklet_head结构数组,每个tasklet_head结构就是一个tasklet_struct结构的队列头。


struct tasklet_head
{
	struct tasklet_struct *list;
} __attribute__ ((__aligned__(SMP_CACHE_BYTES)));

struct tasklet_head tasklet_hi_vec[NR_CPUS] __cacheline_aligned;

回到bh机制这个话题上。通过softirq_init只是使相应tasklet_struct结构中的函数指针func只想了bh_action,也就是建立了bh的执行机制,而具体的bh函数还没有与之挂钩,就好像具体的中断服务程序尚未挂入中断服务队列一样。具体bh函数是通过init_bh设置的。下面是取自sched_init中的一个片段:


	init_bh(TIMER_BH, timer_bh);
	init_bh(TQUEUE_BH, tqueue_bh);
	init_bh(IMMEDIATE_BH, immediate_bh);

以用于时钟中断的bh函数timer_bh为例,其bh向量或bh编号为TIMER_BH。目前内核中已经定义的编号如下:

/* Who gets which entry in bh_base.  Things which will occur most often
   should come first */
   
enum {
	TIMER_BH = 0,
	TQUEUE_BH,
	DIGI_BH,
	SERIAL_BH,
	RISCOM8_BH,
	SPECIALIX_BH,
	AURORA_BH,
	ESP_BH,
	SCSI_BH,
	IMMEDIATE_BH,
	CYCLADES_BH,
	CM206_BH,
	JS_BH,
	MACSERIAL_BH,
	ISICOM_BH
};

再看init_bh的代码:

void init_bh(int nr, void (*routine)(void))
{
	bh_base[nr] = routine;
	mb();
}

显然,这里的数组bh_base就是前述的函数指针数组。这里调用的函数mb与CPU中执行指令的流水线有关,而这并不是我们现在所关心的。

需要执行一个特定的bh函数时,可以通过一个inline函数mark_bh提出请求。读者在时钟中断博客中可以看到在do_timer中通过mark_bh(TIMER_BH);提出对timer_bh的执行请求。函数mark_bh的代码如下:

static inline void mark_bh(int nr)
{
	tasklet_hi_schedule(bh_task_vec+nr);
}

如前所述,内核中为bh函数的执行设立了一个tasklet_struct结构数组bh_task_vec,这里以bh函数的编号为下标就可以找到相应的数据结构,并用其调用tasklet_hi_schedule,其代码也在同一个文件中。读者应该还记得,在bh_task_vec的每个tasklet_struct结构中,函数指针func都指向bh_action。

mark_bh=>tasklet_hi_schedule

static inline void tasklet_hi_schedule(struct tasklet_struct *t)
{
	if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
		int cpu = smp_processor_id();
		unsigned long flags;

		local_irq_save(flags);
		t->next = tasklet_hi_vec[cpu].list;
		tasklet_hi_vec[cpu].list = t;
		__cpu_raise_softirq(cpu, HI_SOFTIRQ);
		local_irq_restore(flags);
	}
}

这里的smp_processor_id返回当前进程所在CPU的编号,然后以此为下标从tasklet_hi_vec中找到该CPU的队列头,把参数t所指的tasklet_struct数据结构链入这个队列。由此可见,对执行bh函数的要求是在哪一个CPU上提出的,就把它调度在哪一个CPU上执行,函数名中的schedule就是这个意思,而与进程调度毫无关系。另一方面,一个tasklet_struct代表着对bh函数的一次执行,在同一时间内只能把它链入一个队列中,而不可能同时出现在多个队列中。对于同一个tasklet_struct数据结构,如果已经对其调用了tasklet_hi_schedule,而尚未得到执行,就不允许再将其链入队列,所以在数据结构中设置了一个标志位TASKLET_STATE_SCHED来保证这一点。最后,还要通过__cpu_raise_softirq正式发出软中断请求。

mark_bh=>tasklet_hi_schedule=>__cpu_raise_softirq


static inline void __cpu_raise_softirq(int cpu, int nr)
{
	softirq_active(cpu) |= (1<<nr);
}

读者在前面已经看到过softirq_active的定义,它对给定CPU的软中断控制、状况结构操作,将其中softirq_active字段内的相应标志位设成1.

内核每当在do_IRQ中执行一个通道中的中断服务程序以后,以及每当从系统调用返回时,都要检查是否有软中断请求在等待执行。下面是do_IRQ中的一个片段:

	if (softirq_active(cpu) & softirq_mask(cpu))
		do_softirq();

另一段代码取自arch/i386/entry.S,这是在从系统调用返回时执行的:

ENTRY(ret_from_sys_call)
#ifdef CONFIG_SMP
	movl processor(%ebx),%eax
	shll $CONFIG_X86_L1_CACHE_SHIFT,%eax
	movl SYMBOL_NAME(irq_stat)(,%eax),%ecx		# softirq_active
	testl SYMBOL_NAME(irq_stat)+4(,%eax),%ecx	# softirq_mask
#else
	movl SYMBOL_NAME(irq_stat),%ecx		# softirq_active
	testl SYMBOL_NAME(irq_stat)+4,%ecx	# softirq_mask
#endif
	jne   handle_softirq




handle_softirq:
	call SYMBOL_NAME(do_softirq)
	jmp ret_from_intr

注意,这里的processor表示task_struct数据结构中该字段的位移,所以207行是从当前进程的task_struct数据结构中取当前CPU的编号。而SYMBOL_NAME(irq_stat)(,%eax)则相当于irq_stat[cpu],并且是其中第一个字段;相应地,SYMBOL_NAME(irq_stat)+4(,%eax)相当于这个数据结构中的第二个字段,并且第一个字段必须是32位。读者不妨回过去看一下irq_cpustat_t的定义,在那里有个注释,说entry.S中的代码对这个数据结构中的字段位置敏感,就是这个意思。所以,这些汇编代码实际上与上面do_IRQ中的两行C代码时一样的。

检测到软中断请求以后,就要通过do_softirq加以执行了。其代码如下:


asmlinkage void do_softirq()
{
	int cpu = smp_processor_id();
	__u32 active, mask;

	if (in_interrupt())
		return;

	local_bh_disable();

	local_irq_disable();
	mask = softirq_mask(cpu);
	active = softirq_active(cpu) & mask;

	if (active) {
		struct softirq_action *h;

restart:
		/* Reset active bitmask before enabling irqs */
		softirq_active(cpu) &= ~active;

		local_irq_enable();

		h = softirq_vec;
		mask &= ~active;

		do {
			if (active & 1)
				h->action(h);
			h++;
			active >>= 1;
		} while (active);

		local_irq_disable();

		active = softirq_active(cpu);
		if ((active &= mask) != 0)
			goto retry;
	}

	local_bh_enable();

	/* Leave with locally disabled hard irqs. It is critical to close
	 * window for infinite recursion, while we help local bh count,
	 * it protected us. Now we are defenceless.
	 */
	return;

retry:
	goto restart;
}

软中断服务程序既不允许在一个硬中断服务程序内部执行,也不允许在一个软中断服务程序内部执行,所以要通过一个宏操作in_interrupt加以检测,这是在include/asm-i386/hardirq.h中定义的:

/*
 * Are we in an interrupt context? Either doing bottom half
 * or hardware interrupt processing?
 */
#define in_interrupt() ({ int __cpu = smp_processor_id(); \
	(local_irq_count(__cpu) + local_bh_count(__cpu) != 0); })

显然,这个测试防止了软中断服务程序的嵌套,这就是前面讲的第一条串行化措施。与local_bh_disable有关的定义在include/asm-i386/softirq.h中:

#define cpu_bh_disable(cpu)	do { local_bh_count(cpu)++; barrier(); } while (0)
#define cpu_bh_enable(cpu)	do { barrier(); local_bh_count(cpu)--; } while (0)

#define local_bh_disable()	cpu_bh_disable(smp_processor_id())
#define local_bh_enable()	cpu_bh_enable(smp_processor_id())

从do_softirq的代码中可以看出,使CPU不能执行软中断服务程序的关卡只有一个,那就是in_interrupt,所以对软中断服务程序的执行并没有采取前述的第二条串行化措施。这就是说,不同的CPU可以同时进入对软中断服务程序的执行(见78行),分别执行各自所请求的软中断服务。从这个意义上,软中断服务程序的执行时并发的、多序的。但是,这些软中断服务程序的设计和实现必须十分小心,不能让它互相干扰(例如通过共享的全局变量)。至于do_softirq中其他的代码,则读者不会感到困难,我们就不多说了。

在我们这个情景中,如前所述,执行的服务程序为bh_action,其代码如下:

/* BHs are serialized by spinlock global_bh_lock.

   It is still possible to make synchronize_bh() as
   spin_unlock_wait(&global_bh_lock). This operation is not used
   by kernel now, so that this lock is not made private only
   due to wait_on_irq().

   It can be removed only after auditing all the BHs.
 */
spinlock_t global_bh_lock = SPIN_LOCK_UNLOCKED;

static void bh_action(unsigned long nr)
{
	int cpu = smp_processor_id();

	if (!spin_trylock(&global_bh_lock))
		goto resched;

	if (!hardirq_trylock(cpu))
		goto resched_unlock;

	if (bh_base[nr])
		bh_base[nr]();

	hardirq_endlock(cpu);
	spin_unlock(&global_bh_lock);
	return;

resched_unlock:
	spin_unlock(&global_bh_lock);
resched:
	mark_bh(nr);
}

这里对具体bh函数的执行(见257行)又设置了两到关卡。一道是hardirq_trylock,其定义为:

#define hardirq_trylock(cpu)	(local_irq_count(cpu) == 0)

与前面的in_interrupt比较下就可看出,这还是防止从一个硬中断服务程序内部调用bh_action。而另一道关卡spin_trylock就不同了,定义如下:

#define spin_trylock(lock)	(!test_and_set_bit(0,(lock)))

这把锁即使全局变量global_bh_lock,只要有一个CPU在253行至260行之间运行,别的CPU就不能进入这个区间了,所以在任何时间最多只有一个CPU在执行bh函数。这就是前述的第二条串行化措施。至于根据bh函数编号执行相应的函数,那就很简单了。在我们这个情景中,具体的bh函数是timer_bh,我们将在时钟中断博客中阅读这个函数的代码。

作为对比,我们列出另一个软中断服务程序tasklet_action的代码,对着可以把它与bh_action比较,看看有哪些重要的区别。这个函数的代码定义如下:

struct tasklet_head tasklet_vec[NR_CPUS] __cacheline_aligned;

static void tasklet_action(struct softirq_action *a)
{
	int cpu = smp_processor_id();
	struct tasklet_struct *list;

	local_irq_disable();
	list = tasklet_vec[cpu].list;
	tasklet_vec[cpu].list = NULL;
	local_irq_enable();

	while (list != NULL) {
		struct tasklet_struct *t = list;

		list = list->next;

		if (tasklet_trylock(t)) {
			if (atomic_read(&t->count) == 0) {
				clear_bit(TASKLET_STATE_SCHED, &t->state);

				t->func(t->data);
				/*
				 * talklet_trylock() uses test_and_set_bit that imply
				 * an mb when it returns zero, thus we need the explicit
				 * mb only here: while closing the critical section.
				 */
#ifdef CONFIG_SMP
				smp_mb__before_clear_bit();
#endif
				tasklet_unlock(t);
				continue;
			}
			tasklet_unlock(t);
		}
		local_irq_disable();
		t->next = tasklet_vec[cpu].list;
		tasklet_vec[cpu].list = t;
		__cpu_raise_softirq(cpu, TASKLET_SOFTIRQ);
		local_irq_enable();
	}
}

最后,软中断服务程序,报货bh函数,与常规中断服务程序的分离并不是强制性的,要根据设备驱动的具体情况(也许还有设计人员的水平)来决定。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux内核-软中断与Bottom Half 的相关文章

  • 在远程机器上执行多个命令

    在下面的命令中 我尝试 ssh 命令并执行多个命令 如果任何命令失败 即如果 command1 退出 那么如果 command1 和 commnd 2 退出 否则在远程计算机上执行命令 3 我如何退出 我怎样才能做到这一点 ssh logi
  • 如何判断全屏应用程序是否正在运行?

    python 是否可以判断 linux 上的全屏应用程序是否正在运行 我感觉使用 Xlib 可能是可行的 但我还没有找到方法 EDIT 我所说的全屏是指整个屏幕 除了应用程序之外 没有其他任何内容 例如全屏游戏 如果您有兴趣在支持下运行所有
  • 访问附加到 ELF 二进制文件的数据

    我有一个静态 ELF 二进制文件 它从 zip 文件中读取数据 为了简化分发 我想将 zip 文件附加到二进制文件中 如下所示 cat mydata zip gt gt mybinary 我知道这样做不会损坏 mybinary 但我不知道如
  • 安装Python时出错

    击中后 make install 我收到以下错误 usr bin install cannot create regular file usr local bin python2 6 Permission denied make altbi
  • 使用 libjpeg 编写 jpeg(段错误)

    尝试使用 libjpeg 从一些原始数据写入 jpeg 文件 它会触发分段错误jpeg start compress 这是代码的相关部分 void write sub image char filename int start int en
  • 如何在特定时间后从“std::cin”读取超时

    我写了一个小程序 int main int argc char argv int n std cout lt lt Before reading from cin lt lt std endl Below reading from cin
  • -bash: /usr/bin/virtualenvwrapper.sh: 没有这样的文件或目录

    我无法弄清楚 shell 试图在哪里运行 usr bin virtualenvwrapper sh服务器登录时 我希望 virtualenvwrapper 永久卸载 而不仅仅是从 shell 实例中删除 我以为我卸载了它pip uninst
  • 为什么删除目标文件后写入文件描述符会成功?

    code int main int argc char argv int fd open test txt O CREAT O RDWR 0200 0400 if fd 1 printf failure to oepn exit 1 int
  • 为什么我的 sed 命令在使用变量时失败?

    使用 bash 我尝试插入日期变量并搜索该日期的日志文件 然后将输出发送到文件 如果我像这样对日期进行硬编码 它会起作用 sed n Nov 22 2010 p file gt log file 但如果我这样做就会失败 date Nov 2
  • Eclipse CDT 内置包含目录

    Under Eclipse CDT gt Project Settings gt C C General gt Paths and Symbols gt Includes gt GNU C 有一个包含路径的列表 有些是粗体的并且是特定于项目
  • 调试严重的 SIGILL 崩溃:文本段损坏

    我们的系统是基于 PowerPC 的运行 Linux 的嵌入式系统 我们遇到了随机的 SIGILL 崩溃 这种情况在各种应用程序中都会出现 崩溃的根本原因是将要执行的指令归零 这表明内存中的文本段已损坏 由于文本段是以只读方式加载的 因此应
  • 使用 M1 在 dockerized Linux 上安装节点画布

    我有以下Dockerfile我在 MacBook Air M1 上运行 所以在 docker 中我有带有 M1 的 linux FROM node 16 7 0 WORKDIR work CMD while true do sleep 10
  • 删除损坏的链接 Unix

    我想删除损坏的链接 但在此之前我想确认目录中是否存在链接文件 我们来调用一下链接A if a A then print ya A is ther fi But if A链接已损坏 那么我该如何检查 find L type l找到损坏的符号链
  • 使用Visual C++进行Linux开发时是否可以直接使用linux文件夹/usr/include

    我尝试使用针对 ubuntu 16 04 VM 的 Visual C for Linux Development 插件 与虚拟机的连接以及本地文件传输到远程文件夹 home user projects projectx 均成功 但是 当我尝
  • 如何在 Linux 上使用 Python 导出

    我需要在 Python 中进行这样的导出 export MY DATA my export 我尝试过这样做 python mode coding utf 8 import os os system export MY DATA my exp
  • Ruby - 在 tmp/pids/thin.pid 中找不到 PID (Thin::PidFileNotFound)

    我试图开始精简我的应用程序 但随后pid无法生成 thin C var www project path current config myproject testing yml start 现在我无法阻止它 因为没有 pid thin C
  • 缺少 /var/lib/mysql/mysql.sock 文件

    我正在尝试访问 mysql 当我运行 mysql 命令时 我得到以下信息 root ip 10 229 65 166 tpdatabase 1 8 0 28356 mysql 错误 2002 HY000 无法连接到 通过socket本地My
  • 如何使用AWK从文件中连续输出行

    我有一个多行文件 我想连续输出文件的某些行 比如第一次 从第1行打印到第5行 下次 打印第2行到第6行 依此类推 我发现 AWK 是一个非常有用的函数 我尝试自己编写代码 但它什么也没输出 以下是我的代码 bin bash for n in
  • 如何重新安装最新的cmake版本?

    我想在Linux环境下安装cmake的最新版本 我安装了 cmake 3 5 版 但某些应用程序不支持 我尝试通过卸载当前版本来升级它 但是当我使用 sudo apt get install cmake 重新安装时 我重新安装了相同的版本
  • 如何在 C++ 中检查文件是否已被另一个应用程序打开?

    我知道 有is open C 中的函数 但我希望一个程序检查文件是否尚未被另一个应用程序打开 有没有办法使用标准库来做到这一点 编辑 在答案中澄清这是针对 Linux 应用程序的 不仅标准库没有这个功能 一般来说也是不可能的 你可以 在li

随机推荐

  • SQL中使用IN关键字时,因空数组替换参数导致了SQL语法错误

    场景 在近期的工作中 发现了这么一个问题 在某一搜索页面 需要同时查询多种订单类型 但在页面中未选择任何订单类型 API通过UI端传来的空订单类型数组进行查询时 抛出了SQL的语法错误 原SQL SELECT FROM dbo order
  • go get 下载包时提示 could not read Username

    问题 在下载公司内部的包时 出现如下提示 go get module example com somepkg common git ls remote q origin in somepath xxxxxxxxxxxxxxxxxxxxxxx
  • python 线程锁

    目录 1 线程安全介绍 2 threading5种常见锁 2 1 同步锁 互斥锁 Lock 1 基本介绍及相关方法 2 给案例加lock锁 3 with语句 2 2 递归锁Rlock 1 基本介绍及相关方法 2 给案例加递归锁 3 with
  • tensorflow InceptionNet

    InceptionNet 即 GoogLeNet 诞生于 2015 年 旨在通过增加网络的宽度来提升网络的能 力 与 VGGNet 通过卷积层堆叠的方式 纵向 相比 是一个不同的方向 横向 显然 InceptionNet 模型的构建与 VG
  • react之纯函数、函数组件、类组件、纯组件

    一 纯函数 Pure Function 定义 一个函数的返回结果只依赖于它的参数 并且在执行的过程中没有副作用 我们就把该函数称作纯函数 特点 1 函数的返回结果只依赖与它的参数 同一个输入只能有同一个输出 let foo a b gt a
  • numpy模块中axis的理解——以np.argmax为例

    numpy模块中axis的理解 以np argmax为例 np argmax参数数量及其作用 axis不同情况的示例 np argmax参数数量及其作用 np argmax是用于取得数组中每一行或者每一列的的最大值 常用于机器学习中获取分类
  • 注意力机制的分类

    目录 1 什么是注意力机制 2 注意力机制分类 3 代表算法 1 空间域代表算法 2 通道域代表算法 3 混合域代表算法 DANet CBAM 4 注意力机制的应用 1 什么是注意力机制 注意力机制通俗的讲就是把注意力集中放在重要的点上 而
  • linux内核驱动开发笔试题

    linux内核驱动开发笔试题 一 一些常规中举的C考题 第一题 写出下述程序结果 int m 3 1 4 7 2 5 8 3 6 9 int i j k 2 for i 0 i lt 3 i printf d m k i 问题所在 本题考点
  • redis默认过期时间:redis默认的是永不过期

    今天同事问我redis默认过期时间是多久 突然想起几年前想查一下redis默认过期时间是多久 搜到的博文全是打着 redis默认过期时间是多久 的标题在讲redis过期原理 正好闲来没事 又搜了下 几年过去了 还是一样 哪来那么多文不对题的
  • StackExchange.redis 实现模糊匹配批量查询

    版权声明 本文为博主原创文章 遵循 CC 4 0 BY SA 版权协议 转载请附上原文出处链接和本声明 本文链接 https blog csdn net Helloantoherday article details 81286685 如果
  • 一、C++中queue和deque的区别

    1 先明白队尾和队首 back和front的联系 无论从哪个方向看 插入的地方就是队尾 所有的操作名字都与back有联系 插入端的另一端就是队首 所有的操作名字都与front有联系 其中queue的操作是 queue
  • jdbc连接mysql数据库,设置字符集编码

    jdbc连接mysql数据库 设置字符集编码 1 第一种方法 JDBC连接数据库时常会出现乱码的情况 那是因为我们的字符级与数据库的字符级不一样 我们通过定义url地址的时候定义字符级 sql代表你的数据库名称 所以当这种情况遇到乱码的时候
  • 交叉编译arm Linux环境下的android-tools-adb

    前言 项目使用Rockchip的3399挖掘机demo板 使用官方提供的Debian Linux SDK 官方github源码链接 https github com rockchip linux 进行开发定制 当前需要将Android上的调
  • 阿辉闯编程(Java入门)

    故事背景 hello 大家好 我叫阿辉 在某一个风雨交加的夜晚 我也不知道干了什么惹怒了老天爷 可能是嫉妒我帅气的颜值吧 一道闪电轰然劈下 我便晕死了过去 等我再次睁开眼睛的时候 我发现我来到了一个不知名的地方 于是我问向了一旁的人 这里是
  • 豪华股东阵容加持,九方财富有望成港股“大肉签”

    经过一段时间的盘整 曾经因行业冷却而沉寂的港股打新正在重回投资者偏好之中 2月28日启动正式招股的新股 九方财富 就受到了投资者的广泛关注 自去年底以来 港股随全球资本市场大势好转 逐渐脱离底部 也提升了市场对新股的热情 同时 九方财富与其
  • xposed开发之清除应用数据(研究历程)

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 最近在研究xposed 有个需求要做到清除其他应用数据 xposed功能足够强大 应该可以实现这个功能 下面是基于android4 4 4研究的思路 google只能开放了
  • Mac OS X 下如何使用类似 Linux 下的 /proc/pid/maps 功能

    本文转载至 http stackoverflow com questions 8058005 mac os x equivalent of virtualquery or proc pid maps 一言以概之 使用 vmmap 不过格式和
  • Objective-C语法之代码块(block)的使用

    代码块本质上是和其他变量类似 不同的是 代码块存储的数据是一个函数体 使用代码块是 你可以像调用其他标准函数一样 传入参数数 并得到返回值 脱字符 是块的语法标记 按照我们熟悉的参数语法规约所定义的返回值以及块的主体 也就是可以执行的代码
  • Microsemi Libero使用技巧6——FPGA全局网络的设置

    文章目录 前言 问题描述 问题分析 FPGA全局布线资源简介 Microsemi FPGA的全局布线资源 全局网络改为普通输入 普通输入上全局网络 总结 推荐阅读 交流群 系列教程 Microsemi Libero系列教程 前言 刚开始做M
  • linux内核-软中断与Bottom Half

    中断服务一般都是在将中断请求关闭的条件下执行的 以避免嵌套而使控制复杂化 可是 如果关中断的时间持续太长就可能因为CPU不能及时响应其他的中断请求而使中断 请求 丢失 为此 内核允许在将具体的中断服务程序挂入中断请求队列时将SA INTER