Linux内核学习-2。进程管理与调度(要点)内核任务进程管理与调度进程优先级进程优先级大致分为实时进程和非实时进程进程:硬实时进程:有严格的时间限制/Linux没有supporthardreal-time/somelinuxsidebranchversionRTLinux支持hardreal-time,主要原因是调度器没有内置到内核中,内核作为一个独立的进程来处理一些不仅必要的任务。Linux任务优先考虑吞吐量,因此进程调度被削弱。但是近年来,人们在降低内核延迟方面也做了大量的研究,比如提出了抢占机制、实时互斥内核锁、完全公平的调度器等。软实时过程:类似于刻录光盘的过程,即使刻录过程暂时中断,也不会造成停机等冒险操作。普通流程:没有具体的时间约束限制,但会分配优先级以区分重要性。进程的生命周期分为:运行、等待、休眠和终止。进程的状态机由调度程序更改。不在循环范围内的进程:“僵尸进程”:子进程被KILL信号(SIGTERM和SIGKILL)杀死,父进程没有调用wait4()。正常进程是子进程被KILL,父进程调用wait4系统调用通知内核子进程死掉。僵尸进程状态的进程:占用资源很少,重启后也会在进程表中删除(ps/top可以刷新进程)原因是内存地址区访问划分不同。进入内核态的方法一:如果用户态进程进入内核态:要访问共享数据和文件系统空间,必须通过系统调用才能进入内核态。进入内核态方法二:中断触发进入内核态。中断触发和系统调用可以使用户态进入内核态,但是用户态是主动调用的,中断是外部触发的。UserModestcucttask_struct{//非常多的成员};成员很多,很难搞清楚,可以归类为:状态和执行信息,如挂起信号、进程的二进制格式类型、进程PID、父进程地址、优先级和CPU时间。分配的虚拟内存信息。身份凭证,如用户ID、组ID、权限等。使用过的文件(包括程序代码二进制文件)线程信息记录,CPU运行时数据。有关与其他进程通信的信息。此进程用于响应传入信号的信号处理程序。Processstatemachinestcucttask_struct{volatilelongstate;};TASK_RUNNING:处于runnable状态,不一定处于CPUcovertime,也可能是在等待scheduler。TASK_INTERRUPTIBLE:设置为某个时间或其他资源的休眠进程。TASK_UNINTERRUPTIBLE:内核指令禁用的休眠进程。它不能从外部唤醒,只能由内核本身唤醒。TASK_STOPPED:进程有意停止运行,例如,由调度程序停止。TASK_TRACED:本来就不是进程状态,用来stop的进程(ptrace机制)区别于正常的stop进程。EXIT_ZOMBIE:僵尸状态EXIT_DEAD:wait系统调用已经发出,僵尸状态解除。进程资源限制structrlimit{unsignedlongrlim_cur;unsignedlongrlim_max;}structtask_struct{structrlimititelimit;};rlim_cur:进程当前的资源限制,成为软限制(softlimit)rlim_max:限制的最大允许值,硬限制(hardlimit)user进程通过setrlimit()系统调用增减限流,最大值不能超过rlim_max;getrlimits()用于检查当前限制。那么什么是set资源呢?cat/proc/self/limits查看当前系统设置进程的资源限制。Linux系统启动时,会设置当前资源限制的属性,在include/asm-generic/resource.h中定义进程的资源限制,并在linux启动时通过init进程完成配置。在init_task.h中挂载数组。)然后会在每个进程中使用:进程类型进程是由(二进制代码应用)、(单线程)、资源(内存、文件)分配给应用程序的。使用(fork)或(exec)系统调用产生新进程。fork生成当前进程的一个副本,称为子进程,被复制到两个相同的独立进程中。资源分离,资源两份,在系统上不做任何关联。exec从可执行二进制文件加载另一个应用程序,替换当前正在运行的进程。exec不是创建一个新进程,而是先使用fork复制一个旧程序,然后调用exec在系统上创建一个应用程序。老版本的clone调用原理和fork一样,可以共享父进程的部分资源。namespaceLinux的全局管理特性,如PID、UID、系统调用uname返回系统信息等,都是全局调用。因此,这就导致了资源和复用的问题,这是虚拟化亟待解决的问题。通过命名空间对全局资源进行抽象,划分不同的命名空间对应不同的资源分配,可以实现虚拟化环境。父命名空间生成多个子命名空间,子命名空间与父命名空间有映射关系。命名空间数据结构#includestructnsproxy{atomic_tcount;结构uts_namespace*uts_ns;//uts:内核名称、版本、底层架构structipc_namespace*ipc_ns;//ipc:进程间通信信息structmnt_namespace*mnt_ns;//挂载文件系统视图stcuctmnt_namespacestructpid_namespace*pid_ns;//有关进程ID的信息structpid_namespacestructuser_namespace*user_ns;//保存限制每个用户资源使用的信息structnet*net_ns;//包含所有与网络相关的命名空间参数};新建一个进程使用fork创建新的命名空间,所以fork时需要标记创建新命名空间的类别。#include#defineCLONE_NEWUTS0x04000000/*创建一个新的utsname组*/#defineCLONE_NEWIPC0x08000000/*创建一个新的IPC命名空间*/#defineCLONE_NEWUSER0x10000000/*创建一个新的用户命名空间*/#defineCLONE_NEWPID0x20000000/*创建一个新的PID命名空间*/#defineCLONE_NEWNET0x40000000/*创建一个新的网络命名空间*/task_struct中有命名空间的定义:structtask_struct{//...structnsproxy*nsproxy;//...};)stcutnsproxy的指针挂在了structtask_struct中,只要挂了不同命名空间的指针,就认为是不同的命名空间。注意:要支持命名空间,必须在编译时启动Generalsetup->Namespacessupport,并且必须一一指定需要支持的命名空间。如果不支持在编译内核时指定命名空间,则默认命名空间的作用类似于不启用命名空间,所有属性等同于全局。UTS命名空间和用户命名空间structnsproxyinit_nsproxy=INIT_NSPROXY(init_nsproxy);#defineINIT_NSPROXY(nsproxy){\.pid_ns=&init_pid_ns,\.count=ATOMIC_INIT(1),\.uts_ns=&init_uts_ns,\.mnt_ns=NULL,\INIT_NET_NS(net_ns)\INIT_IPC_NS(ipc_ns)\.user_ns=&init_user_ns,\}UTSNamespaceUTS命名空间是Linux内核Namespace(命名空间)的一个子系统,主要用于完成容器HOSTNAME和domain的隔离,同时保存内核名称、版本、和底层架构类型。structuts_namespace{structkrefkref;结构new_utsname名称;};//在procstructnew_utsname{charsysname[65];中可以看到这些值//cat/proc/sys/kernel/ostypechar节点名[65];字符释放[65];//cat/proc/sys/kernel/osrelease字符版本[65];//cat/proc/sys/kernel/versioncharmachine[65];字符域名[65];//cat/proc/sys/kernel/domainname};init/version.cstructuts_namespaceinit_uts_ns={....name={.sysname=UTS_SYSNAME,.nodename=UTS_NODENAME,.release=UTS_RELEASE,.version=UTS_VERSION,.machine=UTS_MACHINE,.domainname=UTS_DOMAINNAME,},};只需一个名称和kref,引用计数器,您就可以跟踪在内核中使用了多少个structuts_namespace实例。用户命名空间用户命名空间在数据结构管理上类似于UTS:当需要创建新的用户命名空间时,会生成当前用户命名空间的副本,并关联到当前进程的nsproxy实例。但是用户命名空间本身的表示稍微复杂一些:structuser_namespace{structkrefkref;structhlist_headuidhash_table[UIDHASH_SZ];结构user_struct*root_user;};staticstructuser_namespace*clone_user_ns(structuser_namespace*old_ns){structuser_namespace*ns;结构user_struct*new_user;...ns=kmalloc(sizeof(structuser_namespace),GFP_KERNEL);...ns->root_user=alloc_uid(ns,0);/*用新的替换current->user*/new_user=alloc_uid(ns,current->uid);switch_uid(新用户);返回ns;}