Linux 学习笔记。
linux内核栈大小:在x86上,栈的大小是在编译时配置,可以是4KB,也可以是8KB。从历史上说,内核栈的大小是两页,这意味着32位机内核栈是8kb,而64位机是16kb。
threadinfo 在内核栈的栈底,其中有指针指向task-struct,threadinfo中包含的是和体系相关的,部分,task_struct中包含的是公共部分。
内核时同步和并发的重要性:
同步的必要性:1.任务调度。2多处理器系统smp.3中断异步到来。4linux内核抢占。
进程中 通过修改/proc/sys/kernel/pid_max可以修改pid的最大数目,默认最大值时32768。
写时拷贝时非常重要的。可以减少系统开销。
fork() vfork() __clone()都是调用clone()。然后clone调用do_fork。do_fork()完成创建的大部分工作。其定义在kernel/fork.c中。
do_fork调用copy_process。
copy_process内容:dup_task_struct 创建内核栈,thread_info 结构体和task_struct。
vfork 和fork的区别,vfork不拷贝父进程的页表项。
o1调度器对于对于大服务器的工作负载很理想,但是对于响应时间敏感的程序却有一些先天不足。
2.6内核取而代之的是cfs,完全公平调度算法。
进程分为IO消耗型和cpu消耗型,但这种划分并非绝对。
进程优先级:1/nice值越高,优先级越低。2.实时优先级越高,优先级越高,而且实时进程的优先级高于普通进程。ps -oe state,uid,pid,ppid,rtprio,time,comm 可以查看系统优先级,其中rtprio栏是优先级。
完全公平调度CFS,是一个针对普通的进程的调度类。其算法实现在sched_fair.c中。
nice值不能直接映射到时间片,原因很多:1/多个低优先级进程会使得进程切换变快。而且低优先级往往是后台进程,占的时间反而少了。2/nice值减少1,所带来的效果却决于其初值。3/必须要将时间片与定时器节拍匹配。这样系统定时器限制了两个时间片之间的差异。4/可能会为了实时进程而破坏公平原则,给特殊用例一个开后门的机会。玩弄调度器。以上问题可以通过改造解决,但是无法解决实质问题:分配绝对的时间片引发的固定的切换频率,给公平性造成了很大的变数。CFS采用的方法是对时间片分配方式进行根本性重新设计。摈弃时间片,二四号给处理器使用比重。
CFS中不在依靠nice值计算时间片,而是计算处理器运行比权重。
最小粒度:每个进程获得时间片的底线。默认情况下这个值是1ms。
目标延迟:无限小调度周期。
//
struct sched_entity { CFS调度去使用此实体结构进行记账,调度器实体结构所谓一个名为se的成员变量,嵌入进程描述符struct task_struct中。
/* For load-balancing: */
struct load_weight load;
struct rb_node run_node;
struct list_head group_node;
unsigned int on_rq;
u64 exec_start;
u64 sum_exec_runtime;
u64 vruntime; //记录了一个程序已经运行了多长时间,以及它还能再运行多久。它是经过了所有可运行的进程总数的标准化(加权)的,以ns为单位。
u64 prev_sum_exec_runtime;
u64 nr_migrations;
struct sched_statistics statistics;
#ifdef CONFIG_FAIR_GROUP_SCHED
int depth;
struct sched_entity *parent;
/* rq on which this entity is (to be) queued: */
struct cfs_rq *cfs_rq;
/* rq "owned" by this entity/group: */
struct cfs_rq *my_q;
#endif
#ifdef CONFIG_SMP
/*
* Per entity load average tracking.
*
* Put into separate cache line so it does not
* collide with read-mostly values above.
*/
struct sched_avg avg ____cacheline_aligned_in_smp;
#endif
};
///
cfs调度:选择红黑树的最左边的叶节点,其存储于cfs_rq->tasks_timeline的rb_leftmost中。当此叶节点为空时,则表示树是空的,也就是进入idle。
struct sched_entity *__pick_first_entity(struct cfs_rq *cfs_rq)
{
struct rb_node *left = rb_first_cached(&cfs_rq->tasks_timeline);
if (!left)
return NULL;
return rb_entry(left, struct sched_entity, run_node);
}
__enqueue_entity:在rbtree中找到合适的位置,根据k值,将进程插入其中
dequeue_entity删除rbtree中的进程,发生在进程堵塞或者终止时,变为不可运行状态或者结束运行。
进程的5中状态。
TASK_RUNNING 运行。进程可执行。它正在执行或者在运行队列中等待执行。
TASK_INTERRUPTIBLE(可中断)-进程正在睡眠,阻塞。等待某条件达成。进程可接受信号被提前唤醒
TASK_UNINTERRUPTIBLE (不可中断)除了就算是接收信号也不会被唤醒或者准备投入运行外,这个状态与可打断状态相同。
_TASK_TRACED 被其他进程跟踪的进程,例如通过ptrace 对调试程序进行跟踪。
_TASK_STOPPED(停止) 进程停止执行,进程没有投入运行,也不能投入运行。通常是接收到SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU.等信号的时候。此外,在调试期间接受到的任何信号,都会使进程进入到这种装太。
arm中通过内核栈寻找current的方法:
register unsigned long current_stack_point asm("sp") 定义变量current_stack_point 等于栈指针。
static inline struct thread_info *current_thread_info(void){
return (struct thread_info *)
(current_stack_point & ~(THREAD_SIZE -1)) //因为栈指针和threadinfo和一个联合体,theadinfo处于栈空间的地址最低处。将栈指针的 THREAD_SIZE的二进制位清0.即可得到其最低地址。刚好是threadinfo的地址。即使sp不是在栈顶,也无所谓。其前部地址刚好时不变的啦。
}
进程exit时需要释放的资源:
1.定时器。2.BSD记账信息。3。exit__mm 如果内存独占,那么释放之。4。信号,ipc信号。5.文件描述符,文件系统数据的引用计数。7.设置exit_code。8.exit_notify 通知父进程,及给子进程寻找养父。8调用schedule 调度。9由父进程释放内核栈,thread_info task_struct。