摘要
本篇基于Linux v2.6.26.8 的源码,对进程模型深入分析,具体要点包括:
- Linux系统简述
- 操作系统如何组织进程
- 进程状态如何转换
- 进程如何调度
一、Linux系统简述
Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和UNIX的多用户、多任务、支持多线程和多CPU的操作系统。它能运行主要的UNIX工具软件、应用程序和网络协议。它支持32位和64位硬件。Linux继承了Unix以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。(内容来自百度百科)
二、操作系统如何组织进程
1、进程的概念
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。程序是指令、数据及其组织形式的描述,进程是程序的实体。狭义上:进程是正在运行的程序的实例。广义上:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。(内容选自百度百科)
简而言之,程序的执行就是进程。可以把进程看成一个独立的程序,在内存中有与之对应的代码空间和数据空间,一个进程所拥有的数据和代码只属于自己。(进程是资源分配的基本单位,也是调度运行的基本单位)
2、操作系统中的进程
关于操作系统引入进程的概念:
①从理论角度看,是对正在运行的程序过程的抽象;
②从实现角度看,是一种数据结构,目的在于清晰地刻画动态系统的内在规律,有效管理和调度进入计算机系统主存储器运行的程序。
进程的特征:动态性、并发性、独立性、异步性。
进程的构成:程序、数据、进程控制块。
3、进程的组织
①程序段
程序段就是能被进程调度程序调度到CPU执行的程序代码段(就是说多个进程可以运行同一个程序)。
②数据段
一个进程的数据段,可以是进程对应的程序加工处理的原始数据,也可以是程序执行时产生的中间或最终结果。
③进程控制块
进程控制块(PBC),是操作系统中的一种数据结构,其作用是使一个在多道程序环境下不能独立运行的程序(含数据),成为一个能独立运行的基本单位或与其它进程并发执行的进程。PBC是系统感知进程存在的唯一标志,系统可以利用PCB来控制和管理进程。
Linux中通过task_struct结构体来控制和管理进程,在/include/linux/sched.h下可以找到task_struct的定义。
其中代码及注释如下:
struct task_struct { volatile long state; /* 描述进程的状态:-1就绪,0运行,>0则停止 */ void *stack; /* 内存指针 */ atomic_t usage; /* 有几个进程正在使用此结构 */ unsigned int flags; /* 每个进程的标志 */ unsigned int ptrace; int lock_depth; /* 锁的深度 */ #ifdef CONFIG_SMP #ifdef __ARCH_WANT_UNLOCKED_CTXSW int oncpu; #endif #endif int prio, static_prio, normal_prio; /* 表示进程的运行优先级 */ const struct sched_class *sched_class; /* 调度器的指针 */ struct sched_entity se; /* 调度器 实例化的对象 */ struct sched_rt_entity rt; /* 实时调度器的一个对象 */ #ifdef CONFIG_PREEMPT_NOTIFIERS /* 有限占有链表 */ struct hlist_head preempt_notifiers; #endif /* FPU仅仅使用一段时间 */ unsigned char fpu_counter; /* 定义 fpu_count */ s8 oomkilladj; #ifdef CONFIG_BLK_DEV_IO_TRACE unsigned int btrace_seq; #endif unsigned int policy; /* 进程调度策略 */ cpumask_t cpus_allowed; #ifdef CONFIG_PREEMPT_RCU int rcu_read_lock_nesting; int rcu_flipctr_idx; #endif #if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT) struct sched_info sched_info; /* 调度器的状态 */ #endif struct list_head tasks; struct list_head ptrace_children; struct list_head ptrace_list; /* mm_struct 结构体记录了进程内存使用的相关情况,是虚拟地址空间的结构体 */ struct mm_struct *mm, *active_mm; /* 进程状态 */ struct linux_binfmt *binfmt; int exit_state; int exit_code, exit_signal; /* 退出码 信号处理 */ int pdeath_signal; /*父进程终止时,向子进程发送的信号*/ unsigned int personality; unsigned did_exec:1; pid_t pid; /*这个是进程号*/ pid_t tgid; /*这个是进程组号*/ #ifdef CONFIG_CC_STACKPROTECTOR unsigned long stack_canary; #endif struct task_struct *real_parent; /* 初始父进程 */ struct task_struct *parent; /* 现在的父进程 */ struct list_head children; /* 子进程链表 */ struct list_head sibling; /* 兄弟进程链表 */ /*主线程的进程描述符,linux并没有单独实现线程的相关结构体,只是用一个进程来代替线程 */ struct task_struct *group_leader; /* PID与PID散列表的联系 */ struct pid_link pids[PIDTYPE_MAX]; /* 该进程所有线程的链表 */ struct list_head thread_group; struct completion *vfork_done; int __user *set_child_tid; int __user *clear_child_tid; unsigned int rt_priority; /* 该进程使用cpu时间的信息,utime是在用户态下执行的时间,stime是在内核态下执行的时间 */ cputime_t utime, stime, utimescaled, stimescaled; cputime_t gtime; cputime_t prev_utime, prev_stime; unsigned long nvcsw, nivcsw; /* 上下文切换计数*/ struct timespec start_time; /* 单调时间 */ struct timespec real_start_time; /* 基于引导的时间 */ unsigned long min_flt, maj_flt; cputime_t it_prof_expires, it_virt_expires; unsigned long long it_sched_expires; struct list_head cpu_timers[3]; /* 进程身份凭证 */ /* uid,gid为运行该进程的用户的用户标识符和组标识符 */ /* euid,egid为有效uid,gid */ /* fsuid,fsgid为文件系统uid,gid */ uid_t uid,euid,suid,fsuid; gid_t gid,egid,sgid,fsgid; struct group_info *group_info; /* 进程的权能,分别是有效位集合,继承位集合,允许位集合 */ kernel_cap_t cap_effective, cap_inheritable, cap_permitted, cap_bset; unsigned securebits; struct user_struct *user; #ifdef CONFIG_KEYS struct key *request_key_auth; /* 假定请求密钥权限 */ struct key *thread_keyring; /* 这个线程的私有密钥 */ unsigned char jit_keyring; /* 默认键环将请求的密钥附加到 */ #endif char comm[TASK_COMM_LEN]; /* 保存该进程名字的字符数组 */ /* 文件系统信息计数 */ int link_count, total_link_count; #ifdef CONFIG_SYSVIPC /* 内部过程通信 */ struct sysv_sem sysvsem; #endif #ifdef CONFIG_DETECT_SOFTLOCKUP unsigned long last_switch_timestamp; unsigned long last_switch_count; #endif /* 该进程在特定CPU下的状态 */ struct thread_struct thread; /* 文件系统信息 */ struct fs_struct *fs; /* 打开的文件信息 */ struct files_struct *files; /* 命名空间 */ struct nsproxy *nsproxy; /* 信号相关信息的句柄 */ struct signal_struct *signal; struct sighand_struct *sighand; /* 进程当前要阻塞的信号,每个信号对应一位 */ sigset_t blocked, real_blocked; sigset_t saved_sigmask; /* 如果使用了set_restore_sigmask(),则恢复 */ struct sigpending pending; /* 进程上是否有待处理的信号 */ unsigned long sas_ss_sp; size_t sas_ss_size; int (*notifier)(void *priv); void *notifier_data; sigset_t *notifier_mask; #ifdef CONFIG_SECURITY void *security; #endif struct audit_context *audit_context; #ifdef CONFIG_AUDITSYSCALL uid_t loginuid; unsigned int sessionid; #endif seccomp_t seccomp; /* 线程组跟踪 */ u32 parent_exec_id; u32 self_exec_id; spinlock_t alloc_lock; /* PI数据结构的保护 */ spinlock_t pi_lock; #ifdef CONFIG_RT_MUTEXES struct plist_head pi_waiters; /* 死锁检测与优先级继承处理 */ struct rt_mutex_waiter *pi_blocked_on; #endif #ifdef CONFIG_DEBUG_MUTEXES /* 互斥死锁检测 */ struct mutex_waiter *blocked_on; #endif #ifdef CONFIG_TRACE_IRQFLAGS unsigned int irq_events; int hardirqs_enabled; unsigned long hardirq_enable_ip; unsigned int hardirq_enable_event; unsigned long hardirq_disable_ip; unsigned int hardirq_disable_event; int softirqs_enabled; unsigned long softirq_disable_ip; unsigned int softirq_disable_event; unsigned long softirq_enable_ip; unsigned int softirq_enable_event; int hardirq_context; int softirq_context; #endif #ifdef CONFIG_LOCKDEP # define MAX_LOCK_DEPTH 48UL u64 curr_chain_key; int lockdep_depth; struct held_lock held_locks[MAX_LOCK_DEPTH]; unsigned int lockdep_recursion; #endif /* 日志文件系统信息 */ void *journal_info; /* 堆叠块设备信息 */ struct bio *bio_list, **bio_tail; /* 虚拟机状态 */ struct reclaim_state *reclaim_state; struct backing_dev_info *backing_dev_info; struct io_context *io_context; unsigned long ptrace_message; siginfo_t *last_siginfo; /* 用于pTrace的使用 */ #ifdef CONFIG_TASK_XACCT /* I/O计数器 */ u64 rchar, wchar, syscr, syscw; #endif struct task_io_accounting ioac; #if defined(CONFIG_TASK_XACCT) u64 acct_rss_mem1; /* 累积RSS使用 */ u64 acct_vm_mem1; /* 累积虚拟内存使用 */ cputime_t acct_stimexpd; /* 自上次更新以来的时间 */ #endif #ifdef CONFIG_NUMA struct mempolicy *mempolicy; short il_next; #endif #ifdef CONFIG_CPUSETS nodemask_t mems_allowed; /* 标志内存是否允许访问 */ int cpuset_mems_generation; int cpuset_mem_spread_rotor; #endif #ifdef CONFIG_CGROUPS /* CSSH SETIZOLD保护的控制组信息 */ struct css_set *cgroups; /* cg_list由css_set_lock和tsk->alloc_lock保护 */ struct list_head cg_list; #endif #ifdef CONFIG_FUTEX struct robust_list_head __user *robust_list; #ifdef CONFIG_COMPAT struct compat_robust_list_head __user *compat_robust_list; #endif struct list_head pi_state_list; struct futex_pi_state *pi_state_cache; #endif atomic_t fs_excl; /* 保持FS专有资源 */ struct rcu_head rcu; struct list_head *scm_work_list; struct pipe_inode_info *splice_pipe; #ifdef CONFIG_TASK_DELAY_ACCT struct task_delay_info *delays; #endif #ifdef CONFIG_FAULT_INJECTION int make_it_fail; #endif struct prop_local_single dirties; #ifdef CONFIG_LATENCYTOP int latency_record_count; struct latency_record latency_record[LT_SAVECOUNT]; #endif };
三、进程状态如何转换
Linux对进程的状态解释有以下三种:
①运行态:进程在CPU上运行;
②就绪态:进程已经具备运行条件,等待CPU分配过来;
③阻塞态:进程暂时不能运行。
进程转换图如下:
(图片引自与https://blog.csdn.net/kklvsports/article/details/52268085)
四、进程如何调度
Linux进程调度采用的是抢占式多任务处理,由调度程序来决定什么时候停止一个进程的运行,同时使其他进程能够提到得到执行机会。
Linux中,在每个进程的task_struct结构中有以下四项:policy、priority、counter、rt_priority。这四项是选择进程的依据,其中:
①policy是进程的调度策略,用来区分实时进程和普通进程(实时进程优先于普通进程运行);
②priority是进程(包括实时和普通)的静态优先级;
③counter是进程剩余的时间片,它的起始值就是priority的值,counter可以看作是进程的动态优先级;
④rt_priority是实时进程特有的,用于实时进程间的选择。(部分引自百度百科)
调度器:调度器可以临时分配一个任务在CPU上执行。调度器使得我们同时执行多个程序成为可能,因此可以与具有各种需求的用户共享CPU。因此,调度器的任务是:a、分配时间给进程;b、上下文切换。
调度方法:
Linux在进行进程调度的时候把进程分为两种:a.实时进程、b.普通进程 。其中实时进程的优先级永远比普通进程的优先级高。
注:实时进程根据实时优先级决定调度权值,分时进程则通过nice和counter值决定权值,nice越小,counter越大,被调度的概率越大,也就是曾经使用了cpu最少的进程将会得到优先调度。
五、结尾
linux作为开源的操作系统十分适合初学者作为学习借鉴的材料,通过本次学习加深了我对进程的理解,收获颇多。