Linux内核进程管理:进程的“内核栈”、current宏、进程描述符
目录
linux 进程内核栈
概念
thread_info 有什么用?
thread_info 、内核栈、task_struct 关联
current 宏
1、arm
2、ARM64
3、x86
SYSCALL过程调用规范
x86_64进程栈切换
参考
Linux调度——神奇的current
current的作用
current的通用实现方法
current在x86架构上的实现
实验示例
Linux调度——进程描述符
进程描述符:task_struct
推荐阅读
linux 进程内核栈
https://zhuanlan.zhihu.com/p/296750228
概念
在每一个进程的生命周期中,经常会通过系统调用(SYSCALL)陷入内核。在执行系统调用陷入内核之后,这些内核代码所使用的栈并不是原先用户空间中的栈,而是一个内核空间的栈,这个称作进程的“内核栈”。
每个task的栈分成用户栈和内核栈两部分,进程内核栈在kernel中的定义是:
union thread_union {struct thread_info thread_info;unsigned long stack[THREAD_SIZE/sizeof(long)];
};
每个task的内核栈大小THREAD_SIZE :
x86:#define THREAD_SIZE_ORDER 1#define THREAD_SIZE (PAGE_SIZE << THREAD_SIZE_ORDER)因此是8K
x86_64:#define THREAD_SIZE_ORDER (2 + KASAN_STACK_ORDER)#define THREAD_SIZE (PAGE_SIZE << THREAD_SIZE_ORDER)PAGE_SIZE默认4K,KASAN_STACK_ORDER没有定义时为0,因此是16KARM:8k
ARM64:16K
在32位系统是8KB,64位系统里是16KB。
thread_info 有什么用?
进程在内核中相关的主要数据结构有进程描述符task_struct、threadinfo和mm_struct。上面的共同体thread_union 里,就有thread_info。我们都熟悉进程描述符task_struct,那么thread_info有什么用?
实际上在linux kernel中,task_struct、thread_info都用来保存进程相关信息,即进程PCB信息。然而不同的体系结构里,进程需要存储的信息不尽相同,linux使用task_struct存储通用的信息,将体系结构相关的部分存储在thread_info中。这也是为什么struct task_struct在include/linux/sched.h中定义,而thread_info 在arch/ 下体系结构相关头文件里。
thread_info 、内核栈、task_struct 关联
三者都是密切相关的,服务于进程的关键数据结构,在内核中定义截取如下:
struct task_struct {
#ifdef CONFIG_THREAD_INFO_IN_TASKstruct thread_info thread_info;
#endif
… …void *stack;
… …
}/* * */
union thread_union {
#ifndef CONFIG_ARCH_TASK_STRUCT_ON_STACKstruct task_struct task;
#endif
#ifndef CONFIG_THREAD_INFO_IN_TASKstruct thread_info thread_info;
#endifunsigned long stack[THREAD_SIZE/sizeof(long)];
};/* x86 */
struct thread_info {unsigned long flags; /* low level flags */u32 status; /* thread synchronous flags */
};/* ARM */
struct thread_info {unsigned long flags; /* low level flags */int preempt_count; /* 0 => preemptable, <0 => bug */mm_segment_t addr_limit; /* address limit */struct task_struct *task; /* main task structure */
… …
};
根据宏“CONFIG_THREAD_INFO_IN_TASK”的存在与否,三者在内核中存在两种不同关联:
(1)thread_info 结构在进程内核栈中
即当“CONFIG_THREAD_INFO_IN_TASK = N”时,thread_info和栈stack 在一个联合体thread_union内,共享一块内存,即thread_info在栈所在物理页框上。
进程描述符task_struct 中的成员“void *stack”指向内核栈。不同的是,在ARM中,struct thread_info 结构体有成员“struct task_struct *task”指向进程描述符task_struct,而x86文件中没有。实际上早期内核3.X版本中,x86下的 thread_info 里也有task_struct的指针,后续版本被删除,具体原因到后面介绍“current”宏时再详细介绍。
至此三者关系可以描述如下(x86中没有info.task指针这条线):
因为thread_info 结构和stack是 联合体,thread_info的地址就是栈所在页框的基地址。因此当我们获得当前进程内核栈的sp寄存器存储的地址时,根据THREAD_SIZE对齐就可以获取thread_info结构的基地址(后面介绍current宏会详细分析)。
(2)thread_info 结构在进程描述符中(task_struct)
即当“CONFIG_THREAD_INFO_IN_TASK = Y”时,thread_info就是struct task_struct的第一个成员。union thread_union 中只有栈,即栈和thread_info 结构不再共享一块内存。task.stack依旧存在。三者关系可描述为:
图二
(3)有一点需要注意,进程描述符中的 task_struct.stack指针,是指向栈区域内存基地址,即thread_union.stack 数组基地址,既不是栈顶也不是栈底,栈顶存在寄存器rsp中,栈底是task_struct.stack+THREAD_SIZE,代码中引用时需要注意。
current 宏
内核中经常通过current宏来获得当前进程对应的struct task_sturct结构,其原理离不开进程内核栈,在介绍完了thread_info、task_sturct和内核栈关系后,我们来看下current宏的具体实现。由于内核栈和体系结构相关,本文分别摘选x86和ARM的源码进行分析:
1、arm
查看arm架构的源码发现,前面提到的CONFIG_THREAD_INFO_IN_TASK宏是关闭的,且没有提供对外kconfig接口。也就是说在32位 arm架构中,thread_info 结构肯定在进程内核栈中。下面这种current宏适用于所有符合“thread_info 结构在内核栈中”的架构:
//arch/arm/include/asm/thread_info.hregister unsigned long current_stack_pointer asm ("sp");static inline struct thread_info *current_thread_info(void){return (struct thread_info *)(current_stack_pointer & ~(THREAD_SIZE - 1));}//include/asm-generic/current.h#define get_current() (current_thread_info()->task)#define current get_current()
先通过“sp”栈顶寄存器获取到当前进程的栈地址,通过mask计算,根据page对齐原理就可以拿到位于栈内存区域底部的struct thread_info地址。info->task就是当前进程的进程描述符。
2、ARM64
ARM64增加了很多通用寄存器,使用寄存器传递进程描述符显然效率更高。因此在ARM64架构里,current宏不再通过栈偏移量得到进程描述符地址,而是借用专门的寄存器:
//arch/arm64/include/asm/current.hstatic __always_inline struct task_struct *get_current(void){unsigned long sp_el0;asm ("mrs %0, sp_el0" : "=r" (sp_el0));return (struct task_struct *)sp_el0;}#define current get_current()
ARM64使用sp_el0,在进程切换时暂存进程描述符地址。
sp就是堆栈寄存器。在ARM64里,CPU运行在四个级别(或者叫运行空间),分别是el0、el1、el2、el3,el0则就是用户空间,el1则是内核空间。sp_el0就是用户栈,本文不再详细扩展,感兴趣的可以阅读网络博客《ARMv8学习》一文。
3、x86
在早期内核代码中(2.x 3.x),thread_info结构中还有指向struct task_sturct结构的指针成员,在x86上也可以采用和32位ARM类似的获取方式(CONFIG_THREAD_INFO_IN_TASK = N时)。然而在x86体系结构中,linux kernel一直采用的是另一种方式:使用了current_task这个每CPU变量,来存储当前正在使用的cpu的进程描述符struct task_struct。源码如下:
//arch/x86/include/asm/current.hDECLARE_PER_CPU(struct task_struct *, current_task);static __always_inline struct task_struct *get_current(void){return this_cpu_read_stable(current_task);}#define current get_current()
x86上通用寄存器有限,无法像ARM中那样单独拿出寄存器来存储进程描述符task_sturct结构的地址。由于采用了每cpu变量current_task来保存当前运行进程的task_struct,所以在进程切换时,就需要更新该变量。在arch/x86/kernel/process_64.c文件中的__switch_to函数中有如下代码来更新此全局变量:
this_cpu_write(current_task, next_p);
SYSCALL过程调用规范
篇幅有限,本文只选取x86_64架构来分析SYSCALL过程调用和内核栈的结构。内核栈和用户空间的栈帧结构是一样的,可参考之前写的一篇《x86栈帧原理》。
不过由于syscall属于特殊的过程调用,涉及到栈切换,和用户空间过程调用不同之处有:
1)进程内核栈除了需要保存内核空间过程调用外,还需要保存用户空间栈的数据和返回地址,以便 在返回用户空间继续执行。
(2)过程调用中寄存器调用约定不同。用户空间进程过程调用约定在上一篇《x86通用寄存器》。内核SYSCALL 过程调用约定遵循C ABI ,规定如下:
Registers on entry:* rax system call number* rcx return address* r11 saved rflags (note: r11 is callee-clobbered register in C ABI)* rdi arg0* rsi arg1* rdx arg2* r10 arg3 (needs to be moved to rcx to conform to C ABI)* r8 arg4* r9 arg5* (note: r12-r15, rbp, rbx are callee-preserved in C ABI)
主要区别在SYSCALL时,使用rcx寄存器保存 rip的值(即返回地址),第四个参数就用r10 来保存!内核中参数使用例子:
图三
x86_64进程栈切换
前面花了大篇幅介绍thread_info和stack关系、过程调用规范,是为了能更加清晰认识本文的主角:内核栈。进程通过syscall陷入内核时进行栈切换,我们通过分析整个栈切换流程来逐步描绘内核栈结构。
因为进程内核栈和体系结构密切相关,本文只选取x86_64架构来分析内核栈的结构。下面先来介绍一个重要的数据结构:struct pt_regs 。linux kernel 使用它来格式化内核栈:
//arch/x86/include/asm/ptrace.h
struct pt_regs {
/** C ABI says these regs are callee-preserved. They aren't saved on kernel entry* unless syscall needs a complete, fully filled "struct pt_regs".*/unsigned long r15;unsigned long r14;unsigned long r13;unsigned long r12;unsigned long rbp;unsigned long rbx;
/* These regs are callee-clobbered. Always saved on kernel entry. */unsigned long r11;unsigned long r10;unsigned long r9;unsigned long r8;unsigned long ax;unsigned long cx;unsigned long dx;unsigned long si;unsigned long di;unsigned long orig_ax;
/* Return frame for iretq */unsigned long ip;unsigned long cs;unsigned long flags;unsigned long sp;unsigned long ss;
/* top of stack page */
};
内核栈按照这个顺序缓存各个寄存器存储的用户空间数据/地址,下面会结合源码详细分析。
内核SYSCALL 入口代码在entry_64.S中,了解进程栈结构,需要看在陷入内核后,CPU都做了哪些堆栈操作。下面看下入口处部分汇编源码:
//arch/x86/entry/entry_64.S
ENTRY(entry_SYSCALL_64)UNWIND_HINT_EMPTY/* Interrupts are off on entry. */swapgs// 将用户栈偏移保存到 per-cpu 变量 rsp_scratch 中movq %rsp, PER_CPU_VAR(rsp_scratch)// 切换到进程内核栈movq PER_CPU_VAR(cpu_current_top_of_stack), %rsp/* 在栈中倒序构建 struct pt_regs */pushq $__USER_DS /* pt_regs->ss */pushq PER_CPU_VAR(rsp_scratch) /* pt_regs->sp */pushq %r11 /* pt_regs->flags */pushq $__USER_CS /* pt_regs->cs */pushq %rcx /* pt_regs->ip */
GLOBAL(entry_SYSCALL_64_after_hwframe)//rax 保存着系统调用号pushq %rax /* pt_regs->orig_ax */PUSH_AND_CLEAR_REGS rax=$-ENOSYSTRACE_IRQS_OFF/* 保存参数到寄存器,调用do_syscall_64函数 */movq %rax, %rdimovq %rsp, %rsi
call do_syscall_64 /* returns with IRQs disabled */
(1)指令“movq PER_CPU_VAR(cpu_current_top_of_stack), %rsp”使栈顶寄存器载入进程内核栈地址,实现了用户栈到进程内核栈的切换;
(2)后续依次将用户空间寄存器压栈,和上面的数据结构struct pt_regs 成员一一对应(顺序固定且是倒序)。有三点需要注意:
1)%rcx寄存器保存在了pt_regs->ip 位置,是因为根据 Intel SDM,syscall 会将当前 rip 存到 rcx ,然后将 IA32_LSTAR 加载到 rip 。因此用户空间下一条指令就是从%rcx寄存器中获取;
2)系统调用号(sys_call_table索引号)保存在%rax中;
3)PUSH_AND_CLEAR_REGS 宏包含剩余寄存器入栈指令,展开如下:
//arch/x86/entry/calling.h
.macro PUSH_AND_CLEAR_REGS rdx=%rdx rax=%rax save_ret=0.if \save_retpushq %rsi /* pt_regs->si */movq 8(%rsp), %rsi /* temporarily store the return address in %rsi */movq %rdi, 8(%rsp) /* pt_regs->di (overwriting original return address) */.elsepushq %rdi /* pt_regs->di */pushq %rsi /* pt_regs->si */.endifpushq \rdx /* pt_regs->dx */xorl %edx, %edx /* nospec dx */pushq %rcx /* pt_regs->cx */xorl %ecx, %ecx /* nospec cx */pushq \rax /* pt_regs->ax */pushq %r8 /* pt_regs->r8 */xorl %r8d, %r8d /* nospec r8 */pushq %r9 /* pt_regs->r9 */xorl %r9d, %r9d /* nospec r9 */pushq %r10 /* pt_regs->r10 */xorl %r10d, %r10d /* nospec r10 */pushq %r11 /* pt_regs->r11 */xorl %r11d, %r11d /* nospec r11*/
//后面的寄存器是caller-saved,这里可能是空的pushq %rbx /* pt_regs->rbx */xorl %ebx, %ebx /* nospec rbx*/pushq %rbp /* pt_regs->rbp */xorl %ebp, %ebp /* nospec rbp*/pushq %r12 /* pt_regs->r12 */xorl %r12d, %r12d /* nospec r12*/pushq %r13 /* pt_regs->r13 */xorl %r13d, %r13d /* nospec r13*/pushq %r14 /* pt_regs->r14 */xorl %r14d, %r14d /* nospec r14*/pushq %r15 /* pt_regs->r15 */xorl %r15d, %r15d /* nospec r15*/
在x86_64中,在内核栈中,rbx rbp r12 r13 r14 r15不是必须保存的项(为了访问不越界相应空间必须保留),根据需要保存,linux后续版本采取都保存方式;
(3)和IA32相比,x86_64内核栈起始位置没有预留8KB空间(STACK_PADDIN),是因为在x86_64中,SYCALL过程内核栈所有寄存器都由软件压栈保存,不存在硬件可能没有压栈,防止越界预留位置的情况。在这里贴上内核中关于STACK_PADDING定义:
/* x86_64 has a fixed-length stack frame */
#ifdef CONFIG_X86_32
# ifdef CONFIG_VM86
# define TOP_OF_KERNEL_STACK_PADDING 16
# else
# define TOP_OF_KERNEL_STACK_PADDING 8
# endif
#else
# define TOP_OF_KERNEL_STACK_PADDING 0
#endif
在x86_64中,linux内核栈、struct pt_regs、current宏、struct task_struct关系总结如下图:
图四
整个图四就是linux SYSCALL,x86_64栈切换的完整过程。图中表格第一列是数据结构struct pt_regs 逆序成员,第二列是栈切换后,依次压栈的寄存器,第三列是寄存器中存放的数据类型。
参考
本文涉及到的源码均来自linux kernel 4.18.0
Linux调度——神奇的current
http://linux.laoqinren.net/kernel/sched/current/
本文介绍了linux
内核中经常出现的current
宏,并分析其通用的实现方法,以及其在x86-64
下的实现方法。
current的作用
在内核中,访问任务通常需要获得指向其的struct task_struct
指针。实际上,内核中大部分处理进程的代码都是通过struct task_struct
进行的。因此,通过current
宏查找当前正在运行进程的进程描述符就显得尤为重要。硬件体系不同,该宏的实现方式也就不同。有的硬件体系结构可以专门拿出一个寄存器存放指向当前进程的struct task_struct
指针,用于加快访问速度。而有些像x86
这样的体系结构(其寄存器并不富余),就只能在内核栈的底端创建struct thread_info
结构,通过计算偏移间接地查找struct task_struct
结构。
current的通用实现方法
所以通过esp
寄存器的值和内核栈大小,就可以方便的计算出内核栈的栈底地址,该地址其实就是进程对应的struct thread_info
结构的地址。相关代码如下:
#ifndef __ASM_GENERIC_CURRENT_H
#define __ASM_GENERIC_CURRENT_H
#include <linux/thread_info.h>
#define get_current() (current_thread_info()->task)
#define current get_current()
#endif /* __ASM_GENERIC_CURRENT_H */
/* how to get the current stack pointer from C */
register unsigned long current_stack_pointer asm("esp") __used;
/* how to get the thread information struct from C */
static inline struct thread_info *current_thread_info(void)
{return (struct thread_info *)(current_stack_pointer & ~(THREAD_SIZE - 1));
}
current在x86架构上的实现
理解了如上信息后,x86架构进一步对current
宏进行了优化实现:
#ifndef _ASM_X86_CURRENT_H
#define _ASM_X86_CURRENT_H
#include <linux/compiler.h>
#include <asm/percpu.h>
#ifndef __ASSEMBLY__
struct task_struct;
DECLARE_PER_CPU(struct task_struct *, current_task);
static __always_inline struct task_struct *get_current(void)
{return this_cpu_read_stable(current_task);
}
#define current get_current()
#endif /* __ASSEMBLY__ */
#endif /* _ASM_X86_CURRENT_H */
在x86
体系结构中,使用了current_task
这个每CPU变量,来存储当前正在使用cpu
的进程的struct task_struct
。 由于采用了每cpu
变量current_task
来保存当前运行进程的task_struct
,所以在进程切换时,就需要更新该变量。
在arch/x86/kernel/process_64.c
文件中的__switch_to
函数中有如下代码:
this_cpu_write(current_task, next_p);
注意:在早期的内核中,通过current_thread_info()->task
得到struct task_struct
在x86上也是支持的。不过在最新的内核中,该方法已经不支持了。 因为新版本的内核中thread_info
中已经不存在task
这个成员了。
struct thread_info {unsigned long flags;u32 status;
}
SIZE: 16
实验示例
注意:本示例是在x86支持
current_thread_info()->task
的内核上进行的
x86支持current_thread_info()->task
方式
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
static int __init test_thread_info_init(void)
{struct thread_info *ti = NULL;struct task_struct *head = NULL;printk(KERN_ALERT "[Hello] test_thread_info \n");ti = (struct thread_info*)((unsigned long)&ti & ~(THREAD_SIZE - 1));head = ti->task;printk("kernel stack size = %lx\n", THREAD_SIZE);printk("name is %s\n", head->comm);return 0;
}
static void __exit test_thread_info_exit(void)
{printk(KERN_ALERT "[Goodbye] test_thread_info\n");
}
module_init(test_thread_info_init);
module_exit(test_thread_info_exit);
MODULE_LICENSE("GPL");
- 上述模块初始化代码中,
ti
作为局部变量,存储在内核栈中,所以12
行代码可以获取struct thread_info
结构体的地址。 - 插入模块,打印出进程的名称
insmod
,说明结果符合预期。
验证一下task_current
和thread_info
的关系
实验方法:
(1)启动一个stress进程,持续占用CPU。
# stress -c 1
(2)获得stress进程的进程号,使用taskset将其绑定到cpu1上。
# ps aux | grep stress
root 3427 0.0 0.0 7308 424 pts/2 S+ 15:25 0:00 stress -c 1
root 3428 99.9 0.0 7308 100 pts/2 R+ 15:25 6:21 stress -c 1
root 3918 0.0 0.0 112708 968 pts/3 S+ 15:31 0:00 grep --color=auto stress# taskset -p 02 3428
pid 3428's current affinity mask: f
pid 3428's new affinity mask: 2
此时,我们可以通过crash
查看这些数据的关系:
crash> p current_task:1
per_cpu(current_task, 1) = $1 = (struct task_struct *) 0xffff95c498211fc0
crash> task_struct.comm 0xffff95c498211fc0comm = "stress\000\000\060\000\000\000\000\000\000"
crash> task_struct.stack 0xffff95c498211fc0stack = 0xffff95c407c28000
crash> thread_info.task 0xffff95c407c28000task = 0xffff95c498211fc0
cpu1
上正在执行的进程的描述符地址为:0xffff95c498211fc0
。- 其进程名称为我们期望的
stress
。 - 通过描述符的
stack
域,可以得到进程的栈底地址为:0xffff95c407c28000
,其实也就是thread_info
的地址。 - 通过
thread_info
的task
域可以看出,其值和current_task:1
的值一样。
Linux调度——进程描述符
http://linux.laoqinren.net/kernel/sched/task_struct/
在linux
系统中,每个进程都会有自己的进程描述符,它用结构体struct task_struct
来表示,其描述了一个具体进程的所有信息。 本文对进程描述符进行了详细的介绍。
进程描述符:task_struct
struct task_struct
相对较大,在64
位系统上,它大约有4.1KB
。但考虑到该结构体内包含了内核管理一个进程所需的所有信息,那么它的大小也算相当小了。
在内核中,我们需要非常高效的获取进程的struct task_struct
结构体,在现在的内核版本中,会在内核栈底(对于向下增长的栈来说)创建一个新的结构struct thread_info
:
struct thread_info {struct task_struct *task; /* main task structure */struct exec_domain *exec_domain; /* execution domain */__u32 flags; /* low level flags */__u32 status; /* thread synchronous flags */__u32 cpu; /* current CPU */int preempt_count; /* 0 => preemptable,<0 => BUG */mm_segment_t addr_limit;struct restart_block restart_block;void __user *sysenter_return;
#ifdef CONFIG_X86_32unsigned long previous_esp; /* ESP of the previous stack incase of nested (IRQ) stacks*/__u8 supervisor_stack[0];
#endifunsigned int sig_on_uaccess_error:1;unsigned int uaccess_err:1; /* uaccess failed */
};
该结构体中的task
域中存放着指向该任务实际task_struct
的指针。而struct task_struct
中的stack
域指向了该进程的内核栈的栈底(对于向下增长的栈来说)。
task_struct
和内核栈的关系如下图所示:
在x86-64
位系统上,进程内核栈的大小为16KB
,用如下数据结构表示:
union thread_union { struct thread_info thread_info;unsigned long stack[THREAD_SIZE/sizeof(long)];
};
我们可以使用crash
工具查看struct task_struct
的stack
域和进程thread_info
的关系,这里我查看了系统上init
进程(进程号为1
)的信息:
crash> union thread_union
union thread_union {struct thread_info thread_info;unsigned long stack[2048];
}
SIZE: 16384 //这里可以看出内核栈大小为16KB。
crash> task -R stack 1
PID: 1 TASK: ffff95c499450000 CPU: 1 COMMAND: "systemd"stack = 0xffff95c49944c000,
crash> thread_info.task 0xffff95c49944c000task = 0xffff95c499450000
Author laoqinren
LastMod 2019-11-16
推荐阅读
《Linux进程管理:进程和线程基础知识》
《Linux-进程管理》
《C语言进程的内存地址空间分配》
《进程和线程模型》
《(1)Linux进程调度》
《(2)Linux进程调度器-CPU负载》
《(3)Linux进程调度-进程切换》
《(4)Linux进程调度-组调度及带宽控制》
《(5)Linux进程调度-CFS调度器》
《(6)Linux进程调度-实时调度器》
《Linux内核:进程上下文切换》
《REAL-TIME(SCHED_FIFO和SCHED_RR)进程会导致系统LOCKUP吗?》
Linux内核进程管理:进程的“内核栈”、current宏、进程描述符相关推荐
- Linux内核进程管理基本概念-进程、运行队列、等待队列、进程切换、进程调度
下面简述一些基本概念,以及对内核代码做最初步的了解: 一 Linux内核进程管理基础 Linux 内核使用 task_struct 数据结构来关联所有与进程有关的数据和结构,Linux 内核所有涉及到 ...
- Linux内核进程管理专题报告
一.引言 在Linux内核的五大组成部分(进程管理.内存管理.设备驱动.文件系统.网络协议)中,进程管理是非常重要的一部分,它虽然不像内存管理.虚拟文件系统那样复杂,也不像进程间通信那样条理化,但作为 ...
- Linux 内核进程管理之进程ID
Linux 内核使用 task_struct 数据结构来关联所有与进程有关的数据和结构,Linux 内核所有涉及到进程和程序的所有算法都是围绕该数据结构建立的,是内核中最重要的数据结构之一.该数据结构 ...
- 陈老师Linux内核进程管理导学
<Linux内核分析与应用>第三章 : 进程管理 你认识进程么,就相当于问你认识自己一样难于回答,因为进程每一瞬间都是变化的,就像你的思想无时无刻不在变化一样,因此,本章对进程的讲解可以说 ...
- Linux内核进程管理实时调度与SMP
一,实时调度器类 实时调度类有两类进程: 循环进程SCHED_RR:循环进程有时间片,随着进程的运行时间会减少.当时间片用完时又将其置为初值,并将进程置于队列末尾.先进先出SCHED_FIFO:没有时 ...
- 外网访问arm嵌入式linux_嵌入式Linux系统编程——文件读写访问、属性、描述符、API
Linux 的文件模型是从 Unix 的继承而来,所以 Linux 继承了 UNIX 本身的大部分特性,然后加以扩展,本章从 UNIX 系统接口来描述 Linux 系统结构的特性. 操作系统是通过一系 ...
- 查看、修改linux系统的最大链接数限制、文件描述符限制、端口范围限制、虚拟内存等...
一.修改最大连接数 1.查看当前文件描述符的限制数目的命令: ulimit -n 2.修改文件描述符的限制数目 2.1 临时改变当前会话: ulimit -n 65536 2.2 永久变更需要下面两个 ...
- 查看、修改linux系统的最大链接数限制、文件描述符限制、端口范围限制、虚拟内存等
一.修改最大连接数 1.查看当前文件描述符的限制数目的命令: ulimit -n 2.修改文件描述符的限制数目 2.1 临时改变当前会话: ulimit -n 65536 2.2 永久变更需要下面两个 ...
- 4. linux调用文件计算阶乘前5项和_嵌入式Linux系统编程——文件读写访问、属性、描述符、API
Linux 的文件模型是从 Unix 的继承而来,所以 Linux 继承了 UNIX 本身的大部分特性,然后加以扩展,本章从 UNIX 系统接口来描述 Linux 系统结构的特性. 操作系统是通过一系 ...
最新文章
- 2022图机器学习必读的11大研究趋势和方向: 微分方程/子图表示/图谱理论/非对称/动态性/鲁棒性/通用性/强化学习/图量子等...
- 【C 语言】文件操作 ( 按照内存块的方式读写文件 | fread 函数 | fwrite 函数 )
- 常见拒绝服务型攻击原理及行为特征
- 京东2019春招Java工程师编程题题解
- shell 多行注释
- 流和流库[给初学者非常有用]
- 2020 年 3 月全国程序员工资排名!
- BZOJ 1688: [Usaco2005 Open]Disease Manangement 疾病管理
- 一网打尽软件测试面试必问的所有Web测试点,你不知道的这都有!
- 【English】20190313
- There has been an error processing your request[magento1.6]
- 2022最新分布式面试题合集,轻松应对Java面试
- meanshift算法通俗讲解
- 金惟纯《人生只有一件事》读书笔记
- 制作MIcroUSB转TTL下载器CH340
- 2013华为工作之电信客服行
- 命令行 查看自己的系统版本
- 【JAVA】贪吃蛇的初步实现(三)
- 看完这篇 教你玩转渗透测试靶机——Metasploitable2
- UVa-10474-大理石在哪
热门文章
- 27 Python - 数值 日期与时间
- django模型查询
- python-面向对象名词解析(类、实例、属性、方法、对象)
- model数据库orm操作
- 【调研】在总体为n的情况下,多少样本有代表性?
- 阿拉伯数字转换成中文大写
- JAVA的Date类与Calendar类
- 没有该栏目数据 可能缓存文件(data/cache/inc_catalog_base.inc)没有更新请检查是否有写入权限...
- 如何查看PublicKeyToken
- 生成网站缩略图代码(C#)