[fw]LINUX中断描述符初始化
LINUX中断描述符初始化
@CopyLeft by ICANTH,I Can do ANy THing that I CAN THink!~
Author: WenHui, WuHan University,2012-6-4
硬件产生中断之后,需要通过门描述符来寻找中断的处理程序入口。门描述符和段描述符一样,8个字节。门描述符大体分为:段偏移、段选择子以及DPL。段选择子用于在GDT中寻找到门段基址,DPL用于控制当前进程中断或异常访问权限。当发生中断时,将门描述符所指向的段基地放入%cs,将段偏移放入%eip,转入相应服务。
门描述符结构如下:
任务门描述符:用于在发生中断时调度相应进程
中断门描述符:描述中断处理程序所在段选择子和段内偏移值。据此修改EIP及关闭中断Eflags的IT标识位
陷阱门描述符:与中断门描述符一样,但不关中断
中断描述符表IDT(Interrupt Descriptor Table)用于存放256个门描述符,对应256个中断向量。其中IA32规定前0~31号固定向量用于异常。寄存器idtr为门描述符表IDT的物理地址。根据向量寻找基对应门描述符,并将中断或异常程序加载过程下图所示:
LINUX初始化中断描述符表分两步:初步初始化,以及最终初始化。初步初始化在head.S文件:http://os1a.cs.columbia.edu/lxr/source/arch/x86/kernel/head_32.S
中断描述符表初步初始化
1)声明256个门描述符的IDT表空间。
701 idt_descr:
702 .word IDT_ENTRIES*8-1 # idt contains 256 entries
703 .long idt_table
2)设置指向IDT表地址的寄存器ldtr。
425 lgdt early_gdt_descr
426 lidt idt_descr
427 ljmp $(__KERNEL_CS),$1f
428 1: movl $(__KERNEL_DS),%eax # reload all the segment registers
2)初始化256个门描述符。对于每个门描述符,段选择子都指向内核段,段偏移都指向igore_int函数,该函数只打印一句话:“哥们,别急,俺还没被真正初始化勒!~”。
490 /*
491 * setup_idt
492 *
493 * sets up a idt with 256 entries pointing to
494 * ignore_int, interrupt gates. It doesn't actually load
495 * idt - that can be done only after paging has been enabled
496 * and the kernel moved to PAGE_OFFSET. Interrupts
497 * are enabled elsewhere, when we can be relatively
498 * sure everything is ok.
499 *
500 * Warning: %esi is live across this function.
501 */
502 setup_idt:
503 lea ignore_int,%edx
504 movl $(__KERNEL_CS << 16),%eax
505 movw %dx,%ax /* selector = 0x0010 = cs */
506 movw $0x8E00,%dx /* interrupt gate - dpl=0, present */
507
508 lea idt_table,%edi
509 mov $256,%ecx
510 rp_sidt:
511 movl %eax,(%edi)
512 movl %edx,4(%edi)
513 addl $8,%edi
514 dec %ecx
515 jne rp_sidt
中断描述符表最终初始化
中断描述符表最终初始化分为两部分:异常和中断,分别在函数trap_init(void)和init_IRQ(void)中实现,都由系统初始化入口函数start_kernel()所调用。对于特定异常,其处理异常函数预先已经设定好;但对于特定异步中断,由于需要捕获设备I/O中断的程序数目不定,所以得采用特定数据结构来对irq及其action进行描述。
trap_init:http://os1a.cs.columbia.edu/lxr/source/arch/x86/kernel/traps.c#830
do_IRQ:
1)异常部分最终初始化
由于IA32规定异常所对应的固定向量,所以直接调用set_trap_gate()、set_intr_gate()、set_system_gate()、set_system_intr_gate()、set_task_gate()、set_intr_gate_ist()设置异常处理函数、初始相应异常。而这些函数,都只是_set_gate宏的封装而已。异常处理函数由宏定义DO_ERROR生成。其调用关系如下:
在trap_init(void)函数中,调用在set_intr_gate_ist()设置“stack exception”的12号中断门描述符。set_intr_gate_ist是设置中断描述符函数_set_gate的一层封装,给_set_gate传入异常处理函数stack_segment作为门描述符的偏移地址,传入__KERNEL_CS作为段选择子,传入GATE_INTERRUPT表示中断门描述符类型,传入DPL = 0表示只能由内核态访问、而不能由用户调用。
_set_gate函数中,其调用pack_gate()组装成一个门描述符格式,并调用write_idt_entry写入IDT表中相应描述符。stack_segment函数压入do_stack_segment函数地址并跳转到error_code汇编代码进行处理。具体代码(/arch/x86/include/asm/desc.h)如下:
830 void __init trap_init(void)
831 {
857 set_intr_gate_ist(12, &stack_segment, STACKFAULT_STACK);
898 x86_init.irqs.trap_init(); /* 初始化该平台x86的特定设备 */
899 }
383 static inline void set_intr_gate_ist(int n, void *addr, unsigned ist)
384 {
385 BUG_ON((unsigned)n > 0xFF);
386 _set_gate(n, GATE_INTERRUPT, addr, 0, ist, __KERNEL_CS);
387 }
312 static inline void _set_gate(int gate, unsigned type, void *addr,
313 unsigned dpl, unsigned ist, unsigned seg)
314 {
315 gate_desc s;
316 pack_gate(&s, type, (unsigned long)addr, dpl, ist, seg);
317 /*
318 * does not need to be atomic because it is only done once at
319 * setup time
320 */
321 write_idt_entry(idt_table, gate, &s);
322 }
064 static inline void pack_gate(gate_desc *gate, unsigned char type,
065 unsigned long base, unsigned dpl, unsigned flags,
066 unsigned short seg)
067 {
068 gate->a = (seg << 16) | (base & 0xffff);
069 gate->b = (base & 0xffff0000) |
070 (((0x80 | type | (dpl << 5)) & 0xff) << 8);
071 }
115 static inline void native_write_idt_entry(gate_desc *idt, int entry,
116 const gate_desc *gate)
117 {
118 memcpy(&idt[entry], gate, sizeof(*gate));
119 }
然异常12号处理者stack_segment,何许人也?在/arch/x86/kernel/entry_32.S中定义如下:
1014 ENTRY(stack_segment)
1015 RING0_EC_FRAME
1016 pushl $do_stack_segment
1017 CFI_ADJUST_CFA_OFFSET 4
1018 jmp error_code
1019 CFI_ENDPROC
1020 END(stack_segment)
有的异常,例如stack_segment,系统由硬件产生错误码并压入栈中。另外一些异常,系统将不产生异常,例如overflow,为了统一中断、产生错误码的异常和不产生错误码的异常的栈内存布局,故“人为地”pushl $0。
958 ENTRY(overflow)
959 RING0_INT_FRAME
960 pushl $0
961 CFI_ADJUST_CFA_OFFSET 4
962 pushl $do_overflow
963 CFI_ADJUST_CFA_OFFSET 4
964 jmp error_code
965 CFI_ENDPROC
966 END(overflow)
在error_code中,首先保存中断寄存器上下文等,其次调用do_stack_segment进行处理,最后调用ret_from_exception进行善后工作,代码如下:
1291 error_code:
1292 /* the function address is in %gs's slot on the stack */
1293 pushl %fs
1294 CFI_ADJUST_CFA_OFFSET 4
1295 /*CFI_REL_OFFSET fs, 0*/
1296 pushl %es
1297 CFI_ADJUST_CFA_OFFSET 4
1298 /*CFI_REL_OFFSET es, 0*/
1299 pushl %ds
1300 CFI_ADJUST_CFA_OFFSET 4
1301 /*CFI_REL_OFFSET ds, 0*/
1302 pushl %eax
1303 CFI_ADJUST_CFA_OFFSET 4
1304 CFI_REL_OFFSET eax, 0
1305 pushl %ebp
1306 CFI_ADJUST_CFA_OFFSET 4
1307 CFI_REL_OFFSET ebp, 0
1308 pushl %edi
1309 CFI_ADJUST_CFA_OFFSET 4
1310 CFI_REL_OFFSET edi, 0
1311 pushl %esi
1312 CFI_ADJUST_CFA_OFFSET 4
1313 CFI_REL_OFFSET esi, 0
1314 pushl %edx
1315 CFI_ADJUST_CFA_OFFSET 4
1316 CFI_REL_OFFSET edx, 0
1317 pushl %ecx
1318 CFI_ADJUST_CFA_OFFSET 4
1319 CFI_REL_OFFSET ecx, 0
1320 pushl %ebx
1321 CFI_ADJUST_CFA_OFFSET 4
1322 CFI_REL_OFFSET ebx, 0
1323 cld
1324 movl $(__KERNEL_PERCPU), %ecx
1325 movl %ecx, %fs
1326 UNWIND_ESPFIX_STACK /* 用于处理系统栈不是32位的情况 */
1327 GS_TO_REG %ecx /* 把%gs存入%ecx中 */
1328 movl PT_GS(%esp), %edi # get the function address
1329 movl PT_ORIG_EAX(%esp), %edx # get the error code
1330 movl $-1, PT_ORIG_EAX(%esp) # no syscall to restart
1331 REG_TO_PTGS %ecx /* 把%gs的值存入栈的%gs中,即处理代码指针位置 */
1332 SET_KERNEL_GS %ecx
1333 movl $(__USER_DS), %ecx
1334 movl %ecx, %ds
1335 movl %ecx, %es
1336 TRACE_IRQS_OFF
1337 movl %esp,%eax # pt_regs pointer
1338 call *%edi
1339 jmp ret_from_exception
1340 CFI_ENDPROC
1341 END(page_fault)
在error_code中执行在1338行call *edi之前,栈的内存布局如下图。
在error_code处理“栈异常”处理时,call *edi = call do_stack_segment,do_stack_segment函数由DO_ERROR宏生成。error_code给do_stack_segment函数传入regs参数时,由ABI规范规定,eax为第一个参数 struct pt_regs regs。pt_regs与栈内存布局相同,其结构体如下:
021 struct pt_regs {
022 long ebx;
023 long ecx;
024 long edx;
025 long esi;
026 long edi;
027 long ebp;
028 long eax;
029 int xds;
030 int xes;
031 int xfs;
032 int xgs; /* 对应%gs,对于异常而言是处理器代码 */
033 long orig_eax; /* 对应于内存布局中的error code或vector */
034 long eip;
035 int xcs;
036 long eflags;
037 long esp;
038 int xss;
039 };
stack_segment函数是“栈异常”的异常处理函数,由DO_ERROR宏产生:
215 #ifdef CONFIG_X86_32
216 DO_ERROR(12, SIGBUS, "stack segment", stack_segment)
217 #endif
183 #define DO_ERROR(trapnr, signr, str, name) \
184 dotraplinkage void do_##name(struct pt_regs *regs, long error_code) \
185 { \
186 if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \
187 == NOTIFY_STOP) \
188 return; \
189 conditional_sti(regs); \
190 do_trap(trapnr, signr, str, regs, error_code, NULL); \
191 }
可见,do_stack_segment最后调用do_trap进行异常处理。
2)中断描述符表中断部分最终初始化
由start_kernel()调用init_IRQ(void)进行中断最终初始化。其操作流程如下:
1) 将CPU0 IRQ0…IRQ15的vectors设置为0…15。对于CPU0的vecotr_irq而言,若其IRQ是PIC中断,则其vector早已由PIC固定,所以设置其IRQ0…IRQ15的vector为0…15。若是I/O APIC,由于其vector分配是可由OS动态设置,所以vector 0…15可能会被重新分配;
2) 本质上调用native_init_IRQ()函数对irq_desc、以及中断部分门描述符进行初始化,并针对CONFIG_4KSTACKS配置、协处理器模拟浮点运算等进行配置;
a、 调用init_ISA_irqs()初始化8259A可断控制器,并对相应中断请求线IRQ进行初始化、使其对应中断控制器irq_desc的操作函数为8259A操作接口函数;
b、 调用apic_intr_init()函数针对采取I/O APIC中断处理器的情况,对APIC中断处理器进行初始化工作;
c、 将调用set_intr_gate为系统中每根中断请求线IRQ地应的中断向量号设置了相应的中断门描述门,其中断处理函数定义在interrupt数组中;
d、 在PC平台下set_irq(2, &rq2)对从8259A中断控制器的第三根IRQ请求做特殊处理;
e、 irq_ctx_init函数用于在配置CONFIG_4KSTACK的情况下配置当前CPU的中断栈相关项。LINUX内核在未配置CONFIG_4KSTACK时,共享所中断进程的内核栈,内核栈为两页,即8K。在2.6版本时,增加了CONFIG_4KSTACK选项,将栈大小从两页减小至一页,为了应对栈的减少,故中断处理程序拥有自己的中断处理程序线,为原先共享栈的一半,即4K,每个CPU拥有一个中断栈。
125 void __init init_IRQ(void)
126 {
127 int i;
128
129 /*
130 * On cpu 0, Assign IRQ0_VECTOR..IRQ15_VECTOR's to IRQ 0..15.
131 * If these IRQ's are handled by legacy interrupt-controllers like PIC,
132 * then this configuration will likely be static after the boot. If
133 * these IRQ's are handled by more mordern controllers like IO-APIC,
134 * then this vector space can be freed and re-used dynamically as the
135 * irq's migrate etc.
136 */
137 for (i = 0; i < legacy_pic->nr_legacy_irqs; i++)
138 per_cpu(vector_irq, 0)[IRQ0_VECTOR + i] = i;
/* intr_init()本质上调用native_init_IRQ()初始化。用x86_init.irqs.intr_init()函数对irq_desc、以及中断部分门描述符进行初始化。x86_init是x86_init_ops类型,集成针对x86特定平台的各种初始化工作,包含初始化PCI总线、中断、异常等,其中irqs就是针对中断进行初始化操作。*/
140 x86_init.irqs.intr_init();
141 }
/arch/x86/include/asm/x86_init.h
115 /**
116 * struct x86_init_ops - functions for platform specific setup
117 *
118 */
119 struct x86_init_ops {
122 struct x86_init_irqs irqs;
124 struct x86_init_paging paging;
125 struct x86_init_timers timers;
127 struct x86_init_pci pci;
128 };
047 /**
048 * struct x86_init_irqs - platform specific interrupt setup
049 * @pre_vector_init: init code to run before interrupt vectors
050 * are set up.
051 * @intr_init: interrupt init code
052 * @trap_init: platform specific trap setup
053 */
054 struct x86_init_irqs {
055 void (*pre_vector_init)(void);
056 void (*intr_init)(void);
057 void (*trap_init)(void);
058 };
/arch/x86/kernel/x86_init.c
029 /*
030 * The platform setup functions are preset with the default functions
031 * for standard PC hardware.
032 */
033 struct x86_init_ops x86_init __initdata = {
051 .irqs = {
052 .pre_vector_init = init_ISA_irqs,
053 .intr_init = native_init_IRQ,
054 .trap_init = x86_init_noop,
055 },
082 };
235 void __init native_init_IRQ(void)
236 {
237 int i;
238
239 /* Execute any quirks before the call gates are initialised: */
/* 调用init_ISA_irqs()初始化irq_desc[0…NR_IRQS] = {.status = IRQ_DISABLED,
.action = NULLL, .depth = 1}。并且对于irq0 … 15,
将其handler = &i8259A_irq_type*/
240 x86_init.irqs.pre_vector_init();
241
242 apic_intr_init();
243
244 /*
245 * Cover the whole vector space, no vector can escape
246 * us. (some of these will be overridden and become
247 * 'special' SMP interrupts)
248 */
/* 调用set_intr_gate()为每根IRQ对应的中断向量号设置了相应的中断门描述符 */
249 for (i = FIRST_EXTERNAL_VECTOR; i < NR_VECTORS; i++) {
250 /* IA32_SYSCALL_VECTOR could be used in trap_init already. */
251 if (!test_bit(i, used_vectors))
252 set_intr_gate(i, interrupt[i-FIRST_EXTERNAL_VECTOR]);
253 }
/* 系统若非I/O APIC,则IRQ3用于8259A从片的级联,故进行特殊处理 */
255 if (!acpi_ioapic)
256 setup_irq(2, &irq2);
257
258 #ifdef CONFIG_X86_32
259 /*
260 * External FPU? Set up irq13 if so, for
261 * original braindamaged IBM FERR coupling.
262 */
/* 在处理器集成协处理器,而该协处理器没有浮点处理单元的情况下设置针对浮点计算异常的处理函数fpu_irq,该函数用软件模拟的方法来进行浮点数运算 */
263 if (boot_cpu_data.hard_math && !cpu_has_fpu)
264 setup_irq(FPU_IRQ, &fpu_irq);
/* 在内核选中CONFIG_4KSTACKS时,为系统中每一CPU设置中断、异常处理函数所需的内核态栈;在没有选中该选项的情况下,irq_ctx_init是个空语句 */
266 irq_ctx_init(smp_processor_id());
267 #endif
268 }
101 void __init init_ISA_irqs(void)
102 {
103 int i;
/* 若存在LOCAL_APIC,则对Local APIC模块进行设置 */
105 #if defined(CONFIG_X86_64) || defined(CONFIG_X86_LOCAL_APIC)
106 init_bsp_APIC();
107 #endif
/* 初始化中断控制器8259A */
108 legacy_pic->init(0);
109
110 /*
111 * 16 old-style INTA-cycle interrupts:
112 */
/* 系统中断老式处理方式是由两片8259A级联而成,每片可有8个IRQ,故两片最多初始化16个IRQ,但由于IRQ 2用于级联,故实际仅有15个有效IRQ。 */
113 for (i = 0; i < legacy_pic->nr_legacy_irqs; i++) {
114 struct irq_desc *desc = irq_to_desc(i);
115
116 desc->status = IRQ_DISABLED;
117 desc->action = NULL;
118 desc->depth = 1;
119
120 set_irq_chip_and_handler_name(i, &i8259A_chip,
121 handle_level_irq, "XT");
122 }
123 }
interrupt数组
interrupt数组符号由汇编代码定义,其源码如下:
/arch/x86/kernel/entry_32.S
819 /*
820 * Build the entry stubs and pointer table with some assembler magic.
821 * We pack 7 stubs into a single 32-byte chunk, which will fit in a
822 * single cache line on all modern x86 implementations.
823 */
824 .section .init.rodata,"a"
825 ENTRY(interrupt)
826 .text
827 .p2align 5
828 .p2align CONFIG_X86_L1_CACHE_SHIFT
829 ENTRY(irq_entries_start)
830 RING0_INT_FRAME
831 vector=FIRST_EXTERNAL_VECTOR /* =0x20,0~31号内部中断 */
832 .rept (NR_VECTORS-FIRST_EXTERNAL_VECTOR+6)/7
833 .balign 32 /* 32字节对齐 */
834 .rept 7
835 .if vector < NR_VECTORS
836 .if vector <> FIRST_EXTERNAL_VECTOR
/* 按照CFA规则修改前一个offset,以达4字节对齐。CFA标准(Call Frame Information), help a debugger create a reliable backtrace through functions. */
837 CFI_ADJUST_CFA_OFFSET -4
838 .endif
839 1: pushl $(~vector+0x80) /* Note: always in signed byte range ,[-256 ~ -1] */
840 CFI_ADJUST_CFA_OFFSET 4 /* 按CFA规则4字节对齐 */
841 .if ((vector-FIRST_EXTERNAL_VECTOR)%7) <> 6
842 jmp 2f
843 .endif
844 .previous
845 .long 1b
846 .text
847 vector=vector+1
848 .endif
849 .endr /* end of rep 7 */
850 2: jmp common_interrupt
851 .endr /* end of rep (NR_VECTORS – FIRST_...) */
852 END(irq_entries_start)
853
854 .previous
855 END(interrupt)
ENTRY(interrupt)通过伪指令.rept、.endr,将在代码段产生(NR_VECTORS - FIRST_EXTERNAL_VECTOR)个跳转到common_interrupt的汇编代码片段,起始地址是irq_entries_start;在数据段产生一个中断数组的符号interrupt,用于记录产生代码段中每个中断向量处理的汇编代码片段地址,在C语言中将interrupt符号作为中断数组变量导入:
132 extern void (*__initconst interrupt[NR_VECTORS-FIRST_EXTERNAL_VECTOR])(void);
ENTRY(interrupt)编译之后,所生成的代码段和数据段内存布局如下:
ENTRIY(interrupt)汇编代码段主要由两个rept构成,外层rept循环(NR_VECTORS-FIRST_EXTERNAL_VECTOR+6)/7次,而每次内层rept循环7次,内层循环所产生的代码以32字节对齐,内层rept循环产生的代码如上图irq_entries_start中以粗黑方框表示。
以两层rept循环生成jmp common_interrupt的目的在于:
在内循环内,前6次循环产生的代码指令为:push和short jmp,而第7次产生的代码指令为:push和long jmp。push占2字节,short jmp占2字节,long jmp占5字节,故采取此种方式内层rept循环7次产生的代码大小为:6 * (2 + 2) + 2 + 5 = 31 字节。而外层循环以32字节对齐,相比于老版本每次都为push和long jmp而言,所以利用short jmp节省了内存开销。参见:http://didat.sprg.uniroma2.it/la/docs/la12-10.pdf
每个中断门描述符在将vector压入后都跳转到common_interrupt进行处理。common_interrupt在保存中断现场之后,跳转到do_IRQ进行中断函数处理,最后调用iret_from_intr进行中断返回、恢复中断上下文。
863 common_interrupt:
864 addl $-0x80,(%esp) /* Adjust vector into the [-256,-1] range */
865 SAVE_ALL /* 宏定义,负责完成宏定义中断现场的保护工作 */
866 TRACE_IRQS_OFF
867 movl %esp,%eax
868 call do_IRQ
869 jmp ret_from_intr
870 ENDPROC(common_interrupt)
195 .macro SAVE_ALL
196 cld /* 清除系统标志寄存器EFLAGS中的方向标志位DF,使%si、%di寄存器的值在每次字符串指令操作后自动+1,使自字符串从低地址到高地址方向处理 */
197 PUSH_GS
198 pushl %fs
199 CFI_ADJUST_CFA_OFFSET 4
200 /*CFI_REL_OFFSET fs, 0;*/
201 pushl %es
202 CFI_ADJUST_CFA_OFFSET 4
203 /*CFI_REL_OFFSET es, 0;*/
204 pushl %ds
205 CFI_ADJUST_CFA_OFFSET 4
206 /*CFI_REL_OFFSET ds, 0;*/
207 pushl %eax
208 CFI_ADJUST_CFA_OFFSET 4
209 CFI_REL_OFFSET eax, 0
210 pushl %ebp
211 CFI_ADJUST_CFA_OFFSET 4
212 CFI_REL_OFFSET ebp, 0
213 pushl %edi
214 CFI_ADJUST_CFA_OFFSET 4
215 CFI_REL_OFFSET edi, 0
216 pushl %esi
217 CFI_ADJUST_CFA_OFFSET 4
218 CFI_REL_OFFSET esi, 0
219 pushl %edx
220 CFI_ADJUST_CFA_OFFSET 4
221 CFI_REL_OFFSET edx, 0
222 pushl %ecx
223 CFI_ADJUST_CFA_OFFSET 4
224 CFI_REL_OFFSET ecx, 0
225 pushl %ebx
226 CFI_ADJUST_CFA_OFFSET 4
227 CFI_REL_OFFSET ebx, 0
228 movl $(__USER_DS), %edx
229 movl %edx, %ds
230 movl %edx, %es
231 movl $(__KERNEL_PERCPU), %edx
232 movl %edx, %fs
233 SET_KERNEL_GS %edx
234 .endm
common_interrupt在调用do_IRQ之前中断栈内存布局如下:
[fw]LINUX中断描述符初始化的更多相关文章
- Linux kernel的中断子系统之(三):IRQ number和中断描述符
返回目录:<ARM-Linux中断系统>. 总结: 二描述了中断处理示意图,以及关中断.开中断,和IRQ number重要概念. 三介绍了三个重要的结构体,irq_desc.irq_dat ...
- linux kernel的中断子系统之(三):IRQ number和中断描述符【转】
转自:http://www.wowotech.net/linux_kenrel/interrupt_descriptor.html 一.前言 本文主要围绕IRQ number和中断描述符(interr ...
- Linux中断 - IRQ number和中断描述符
一.前言 本文主要围绕IRQ number和中断描述符(interrupt descriptor)这两个概念描述通用中断处理过程.第二章主要描述基本概念,包括什么是IRQ number,什么是中断描述 ...
- 玩转Linux文件描述符和重定向
本文介绍linux中文件描述符与重定向的相关知识,文件描述符是与文件输入.输出相关联的整数,它们用来跟踪已打开的文件.有需要的朋友参考下. 原文出处:http://www.jbxue.com/arti ...
- Linux 文件描述符详解
Overview 了解Linux怎样处理输入和输出是非常重要的.一旦我们了解其原理以后,我们就可以正确熟练地使用脚本把内容输出到正确的位置.同样我们也可以更好地理解输入重定向和输出重定向. Linux ...
- [转帖]linux文件描述符文件/etc/security/limits.conf
linux文件描述符文件/etc/security/limits.conf https://blog.csdn.net/fanren224/article/details/79971359 需要多学习 ...
- linux文件描述符、软硬连接、输入输出重定向
引用链接:https://blog.csdn.net/qq769651718/article/details/79459346 文件描述符的作用: 文件描述符是linux操作系统中特有的概念.其相当于 ...
- Linux进程描述符task_struct结构体详解--Linux进程的管理与调度(一)【转】
Linux内核通过一个被称为进程描述符的task_struct结构体来管理进程,这个结构体包含了一个进程所需的所有信息.它定义在include/linux/sched.h文件中. 谈到task_str ...
- Linux文件描述符与打开文件之间的区别(转载)
转载请说明出处:http://blog.csdn.net/cywosp/article/details/38965239 1. 概述 在Linux系统中一切皆可以看成是文件,文件又可分为: ...
随机推荐
- NodeJs的CommonJS模块规范
前言 本人记忆力一般,为了让自己理解<深入浅出Node.js-朴灵>一书,会在博客里记录一些关键知识,以后忘了也可以在这里找到,快速回想起来 Node通过require.exports.m ...
- openstack--all-in-one部署
安装过程 本次宿主机(即安装OpenStack的机器)的操作系统是CentOS 7.5.安装的OpenStack是目前最新的rocky版本,官方文档建议机器至少有16 GB的内存,处理器硬件虚拟化扩展 ...
- 消息队列之AciveMQ
activemq安全设置 设置admin的用户名和密码
- BZOJ3622 已经没有什么好害怕的了 二项式反演+DP
题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=3622 题解 首先显然如果 \(n - k\) 为奇数那么就是无解.否则的话,"糖果& ...
- springmvc中拦截器配置格式
对于springmvc,有两种方式配置拦截器. 一是实现HandlerInterceptor接口,如 public class MyInterceptor1 implements HandlerInt ...
- 如何在Sketch 54 for mac创建符号?
Sketch 54 for mac是Mac系统平台上一个出色的数字设计绘图软件,小巧而不失功能齐全, 简约而不失强大!从最初的想法到最终的艺术品,可以通过Sketch 54 for mac来实现!现本 ...
- 通俗理解BiLSTM-CRF命名实体识别模型中的CRF层
虽然网上的文章对BiLSTM-CRF模型介绍的文章有很多,但是一般对CRF层的解读比较少. 于是决定,写一系列专门用来解读BiLSTM-CRF模型中的CRF层的文章. 我是用英文写的,发表在了gith ...
- npm和gem
https://blog.csdn.net/u011099640/article/details/53083845
- IDEA使用中的快捷键
项目与项目之间的跳转: Ctrl+Alt+] 下一个窗口. Ctrl+Alt+[ 跳转回上一个窗口. 文件之间的跳转: Ctrl+E. ...
- 超全Altium Designer16 总结--Altium Designer
原址:http://blog.csdn.net/qq_29350001/article/details/52199356 以前是使用DXP2004来画图的,后来转行.想来已经有一年半的时间没有画过了. ...