函数堆栈与进程调度基础

1.创建与使用一个静态链接库
gcc -c -fPIC process.c
gcc -c -fPIC createprocess.c    //-fPIC不可丢
ar cr libstaticprocess.a process.o//打包为一个静态链接库
gcc -o staticprocess createprocess.o -L. -lstaticprocess//使用
2.创建与使用一个动态链接库
gcc -shared -fPIC -o libdynamicprocess.so process.o //创建一个动态库
gcc -o dynamicprocess createprocess.o -L. -ldynamicprocess//使用
3.init进程(/sbin/init)
go@:~$ ls -l /sbin/init 
lrwxrwxrwx 1 root root 20 Sep  5 11:59 /sbin/init -> /lib/systemd/systemd
  • init进程链接到systemd
  • 使用ps -ef命令查看当前系统运行进程,内核态进程带中括号,用户态不带
4.线程的三类数据
  • 线程栈的本地数据
  • 整个进程里的共享数据
  • 线程的私有数据,创建后每一个线程都可以访问,其构造及相关操作见下方:
/*创建一个key值,并指定了析构函数destructor用于释放资源*/
int pthread_key_create(pthread_key_t *key,void(* destructor)(void*));
/*线程可以通过pthread_setspecific函数设置key对应的value*/
int pthread_setspecific(pthread_key_t key,const void * value);
/*使用pthread_getspecific函数获取key对应的值*/
void *pthread_getspecific(pthread_key_t key);
待线程退出时会调用设置的析构函数释放value
4.task_struct结构体
struct task_struct {
        。。。//linux-4.13.16\include\linux\sched.h
    	struct list_head		tasks;//内核任务管理队列指针
    	pid_t		pid;//当前线程id
	    pid_t	   tgid;//进程主线程id
	    ...
	    struct task_struct		*group_leader;//指向主线程

如上所示,基于tgid就可以知道一个task_struct代表什么结构;

	/* -1 unrunnable, 0 runnable, >0 stopped: */
	volatile long			state;//进程运行状态
	int				exit_state;
/* Used in tsk->state: */
#define TASK_RUNNING			0   //常说的就绪态
#define TASK_INTERRUPTIBLE		1   //可中断的进程睡眠,如wait时
#define TASK_UNINTERRUPTIBLE	2   //不可中断只能等待此状态终结,致命信号kill也会被忽略
#define __TASK_STOPPED			4
#define __TASK_TRACED			8
/* Used in tsk->exit_state: */
#define EXIT_DEAD			16  //死亡的进程
#define EXIT_ZOMBIE			32  //刚死的进程设计的,此时为僵尸进程
。。。
#define TASK_KILLABLE	(TASK_WAKEKILL | TASK_UNINTERRUPTIBLE)
/*TASK_UNINTERRUPTIBLE状态的弱化版,可被致命信号唤醒*/

上面大致是一些关于进程的状态码,对于理解清楚进程的状态终于有了进一步的认识;下面继续看及其重要的两个成员变量,他们是实现将用户态和内核态执行串起来的关键:

    struct thread_info thread_info;
    void *stack;
5.理解用户态函数调用堆栈

用户态函数调用堆栈
如上所示,下面贴一个简单的代码,咱们从汇编层来验证64bit下的调用正确性:

int invoke(int a){
	int tmp = a+1;
	return tmp;
}
void main(){
	int res=invoke(1);
}

汇编如下:

invoke:
	pushq	%rbp
	movq	%rsp, %rbp
	movl	%edi, -20(%rbp)
	movl	-20(%rbp), %eax
	addl	$1, %eax
	movl	%eax, -4(%rbp)
	movl	-4(%rbp), %eax
	popq	%rbp
	ret
main:
	pushq	%rbp    //存放栈基地址
	movq	%rsp, %rbp//更新rbp
	subq	$16, %rsp   //后退16字节。。。
	movl	$1, %edi    //传递参数使用edi
	call	invoke      //。。。
	movl	%eax, -4(%rbp)
	nop
	leave
	ret
6.内核态函数栈

内核态函数栈
由上图可知具体内核栈布局,所以说系统调用时的保护上下文就是将相关寄存器变量保存在上图中的pg_regs结构体中,这样当返回时可以恢复现场,接着先前的指令执行下去。
总之,这是一个复杂的过程。

  • 我们可以借助task_struct变量的void *stack;轻松找到内核栈的位置
  • 对于64bit的系统可以在进程运行时分在内核栈中借助per_cpu变量轻松定位task_struct结构体,而per_cpu变量是内核实现同步机制的一种手段。而对于32bit的系统来说,其可以直接通过thread_info;来定位
7.进程调度策略和调度类
  • 实时进程, 需要尽快返回结果
  • 普通进程, 大部分进程皆是普通进程,优先级较低
	unsigned int			policy;//调度策略
	int				prio;   //下面几个是配合调度策略使用的优先级变量
	int				static_prio;
	int				normal_prio;
	unsigned int			rt_priority;
    /*对于实时进程优先级范围是0-99,对于普通进程优先级范围是100-139*/

下面是几种实时进程调度策略:

#define SCHED_FIFO     1    //先来先服务    
#defien SCHED_RR       2    //轮流调度算法
#define SCHED_DEADLINE 6    //选择距离deadline最近的进程调度

下面是几种普通进程调度策略:

#define SCHED_NORMAL    0   //普通进程
#define SCHED_BATCH     3   //后台进程,优先级稍低
#define SCHED_IDLE      5   //空闲进程,优先级最低

好了,以上就是这个进程运行初期就应该确认其中的某一项,然后CPU调度时就会按照某种具体的方法来调度,它主要是通过指定特定的调度类来实现的,linux上就是下面的这个task_struct 成员变量。

//linux-4.13.16\include\linux\sched.h...562 line
	const struct sched_class	*sched_class;

看这个变量名就知道他必定会在初始化task_struct初期就会指定具体的调度类,比若说对于优先级最高的进程就会是stop_sched_class,对于普通进程则是fair_sched_class,而rt_sched_class就对应 RR 算法或者 FIFO 算法的调度策略。

8.完全公平调度算法(CFS)

对于task_struct它有进程相关的统计量,而CFS就是基于进程的vruntime来设计的,对于运行中的进程当CPU触发了一个时钟中断时,Tick一下的时候就会增加vruntime的值,在当前进程时间片结束之后,CPU会结合具体优先级根据算法更新vruntime的值,接下来找到vruntime最小的进程调度。

9.调度队列与调度实体

在这里插入图片描述

如上图所示,每一个CPU都有两个维护调度进程的队列,这个队列是基于红黑树实现的,红黑树上的节点就是一个个的调度实体,每当要调度一个进程时只需要在该红黑树上找到最左边的调度实体就可以了。当然在查找时首先查找实时调度队列RT,若其中没有才去CFS调度队列调度普通队列。

10.CPU调度总结
Created with Raphaël 2.2.0 stop_sched_class dl_sched_class rt_sched_class fair_sched_class idle_sched_class
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值