聊聊内核中关键的概念之上篇

2022-08-17 13:11:37 浏览数 (1)

虚拟地址空间

  • 内核在进程和物理内存之间提供了一层抽象层,这一层是虚拟地址空间,进程的内存视图是通过虚拟地址空间构建的。虚拟地址空间为进程提供了假象(虚拟内存),每个进程以为自己在执行过程中独占了整个内存,虚拟内存是由内核的内存管理和CPU的MMU协调实现的。每个进程都会有32位或者64位的地址空间,这个地址空间范围是由系统结构所限定的。每个进程通过MMU加载到虚拟地址空间中,任何进程尝试方位其边界之外的地址空间都会触发硬件故障,从而使内存管理器能够检测和终止违反的进程(Segment Fault错误)。

用户空间和内核空间

  • Linux操作系统将整个内存分割为两个逻辑分区:用户空间和内核空间。这种分开设计确保所有分配有地址空间的进程都映射到内存的用户空间,而内核数据和服务都在内核空间中运行。内核通过与硬件协调实现这种保护。当应用程序发起syscall(系统调用),CPU切换为特权模式,从用户空间切换到内核空间,所请求的服务完成后,内核使用了另外一组CPU指令将进程从内核空间切换到用户空间。

上下文切换

  • 当一个进程通过系统调用请求内核服务时,内核将代表调用进程来执行,此时内核被认为是进程上下文中执行,上下文切换是指进程存储一个进程或者线程的状态,然后完成某种服务后恢复和唤醒进程的执行点,这个上下文切换是由时间开销的,保存旧进程的状态(拷贝当前进程涉及到的寄存器的状态到PCB),加载已经保存进程状态的新进程(从进程的PCB中拷贝到寄存器中),上下文切换时间时依赖于硬件。

进程描述符

  • 进程从创建到退出过程都是有内核的进程管理子系统进行管理。一个进程在内存中还被分配一个称为描述符的数据结构,内核用进程描述符来识别、管理和调度进程。内核定义了struct task_struct类型描述一个进程的实例,包括进程所有的属性。标识的详细信息、资源分配的情况。
  • 一个进程从产生到退出一直出于不同的状态中,这个也叫做进程状态,它们定义了进程当前不同的状态。TASK_RUNNING是进程正在执行或在调度器运行队列中的抢占CPU;TASK_INTERRUPTIBLE是进程出于可中断的等待状态,它依旧出于等待状态知道所有等待条件为真,例如互斥锁调用、IO准备等。TASK_KILLABLE这个于TASK_INTERRUPTIBLE类似,不同的TASK_KILLABLE只会发生在致命信号上。TASK_UNINTERRUPTIBLE是进程处于不可中断的等待状态,进程处于这种状态不会被产生的信号唤醒,只有它正在等待的事件发生时候进程才转换为TASK_RUNNING状态。TASK_STOPPED是进程已经收到终止的信号,进程终止;TASK_TRACED是一个进程可能被一个调试器进行检查,这时候可认为进程出于这个状态;TASK_ZOMBIE是进程碑额终止,但是进程的资源尚未回收;EXIT_DEAD是父进程使用wait方法收集子进程的退出状态后,子进程终止,并且释放它的所有资源.
代码语言:javascript复制
struct task_struct {
#ifdef CONFIG_THREAD_INFO_IN_TASK

	// 保存处理器特定的状态信息,并且它是任务结构体的关键信息 
	struct thread_info		thread_info;
#endif
	/* -1 unrunnable, 0 runnable, >0 stopped: */
	volatile long			state;

	/*
	 * This begins the randomizable portion of task_struct. Only
	 * scheduling-critical items should be added above here.
	 */
	randomized_struct_fields_start

	void				*stack;
	atomic_t			usage;
	// 进程相应的各种属性,字段中每一位对应于一个进程生命周期中的各个字段。
	unsigned int			flags;
	unsigned int			ptrace;

#ifdef CONFIG_SMP
	struct llist_node		wake_entry;
	int				on_cpu;
#ifdef CONFIG_THREAD_INFO_IN_TASK
	/* Current CPU: */
	unsigned int			cpu;
#endif

	// prio和static_prio,prio帮助确定调度进程的优先级,如果进程被分配了实时调度策略,则噶字段保存静态优先级,范围是1~99.对于正常进程,这个字段保存了由nice的值得来的动态优先级
	int				prio;
	int				static_prio;
	int				normal_prio;
	// 指定实时调度策略的进程优先级
	unsigned int			rt_priority;

	const struct sched_class	*sched_class;
	// se/rt/dk 是每个任务属于的调度实体,因为调度是在每个实体级别的完成的。se用于所有正常进程;rt是用于实时进程;dl用于截止期进程。
	struct sched_entity		se;
	struct sched_rt_entity		rt;
#ifdef CONFIG_CGROUP_SCHED
	struct task_group		*sched_task_group;
#endif
	struct sched_dl_entity		dl;

#endif
	// 该字段保存和进程调度策略相关信息
	unsigned int			policy;
	int				nr_cpus_allowed;
	// 该字段制定了进程的CPU掩码,在多处理器系统中,进程允许在哪个CPU上进行调度
	cpumask_t			cpus_allowed;

	// pid是进程的唯一标识符,它是pid_t类型,可以通过/proc/sys/kernel/pid_max接口制定默认的最大值,默认最大值是2的32次方大约400万
	pid_t				pid;
	// 保存线程组id,假设创建一个新进程,它的pid和tgid是相同的。当进程产生一个新线程时候,新的子进程将获得唯一的pid
	pid_t				tgid;

	// 对于正常进程,real_parent和parent两个指针指向同一个task_struct,它们区别仅在于使用posix线程实现的多线程进程,对于这种情况,real_parent指向父进程的结构,parent指向收到SIGCHLD信号的进程任务的结构体
	struct task_struct __rcu	*real_parent;
	struct task_struct __rcu	*parent;
	// 指向子任务结构体链表的指针
	struct list_head		children;
	// 指向兄弟任务结构体链表的指针
	struct list_head		sibling;
	// 指向进程组组长的结构体
	struct task_struct		*group_leader;

	// 存储了文件系统信息
	struct fs_struct		*fs;

	// 文件描述符保存了一些指针,这些指针指向进程为了执行各个操作而打开的所有文件
	struct files_struct		*files;

	/* Namespaces: */
	struct nsproxy			*nsproxy;

	/* CPU-specific state of this task: */
	struct thread_struct		thread;


};

0 人点赞