💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤
📃个人主页 :阿然成长日记 👈点击可跳转
📆 个人专栏: 🔹数据结构与算法🔹C语言进阶🔹C++🔹Liunx
🚩 不能则学,不知则问,耻于问人,决无长进
🍭 🍯 🍎 🍏 🍊 🍋 🍒 🍇 🍉 🍓 🍑 🍈 🍌 🍐 🍍
一、进程的基本概念
【课本概念】:程序的一个执行实例,正在执行的程序等
【内核观点】:担当分配系统资源(CPU时间,内存)的实体
下面我们就来Windows系统中看看进程
- 由冯诺依曼体系可知,进程的本质其实就是一个可执行文件,本来存储在外存上,当把它加载内存中来的时候,就形成一个进程。
进程的特性:
动态性
:进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生,动态消亡的。
并发性
:任何进程都可以同其他进程一起并发执行
独立性
:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位;
异步性
:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推 进
结构特征
:进程由程序、数据和进程控制块三部分组成;
什么是进程?
狭义定义
:进程就是一段程序的执行过程。
广义定义
:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。
二、PCB引入
上面说到将一个可执行程序加载到内存,它的组成就是
代码+数据
,因为它没有名字,这就导致它只有它自己认识自己。我们的操作系统想要找到它就需要它的一个标识,于是我们的PCB就闪亮登场了。在加载可执行程序形成进程的同时,会创建一个描述该进程的结构体,这个结构体就叫做PCB
,在linux中是一个名字为【task_struct
】的结构体。
💭简单来说操作系统对一个进程会做如下事情
- 为该进程创建对应的PCB对象
- 将该进程的代码和数据加载到内存中
一个完整的task_struct如下代码:
Linux中task_struct用来控制管理进程,结构如下:
struct task_struct
{
//说明了该进程是否可以执行,还是可中断等信息
volatile long state;
//Flage 是进程号,在调用fork()时给出
unsigned long flags;
//进程上是否有待处理的信号
int sigpending;
//进程地址空间,区分内核进程与普通进程在内存存放的位置不同
mm_segment_t addr_limit; //0-0xBFFFFFFF for user-thead
//0-0xFFFFFFFF for kernel-thread
//调度标志,表示该进程是否需要重新调度,若非0,则当从内核态返回到用户态,会发生调度
volatile long need_resched;
//锁深度
int lock_depth;
//进程的基本时间片
long nice;
//进程的调度策略,有三种,实时进程:SCHED_FIFO,SCHED_RR, 分时进程:SCHED_OTHER
unsigned long policy;
//进程内存管理信息
struct mm_struct *mm;
int processor;
//若进程不在任何CPU上运行, cpus_runnable 的值是0,否则是1 这个值在运行队列被锁时更新
unsigned long cpus_runnable, cpus_allowed;
//指向运行队列的指针
struct list_head run_list;
//进程的睡眠时间
unsigned long sleep_time;
//用于将系统中所有的进程连成一个双向循环链表, 其根是init_task
struct task_struct *next_task, *prev_task;
struct mm_struct *active_mm;
struct list_head local_pages; //指向本地页面
unsigned int allocation_order, nr_local_pages;
struct linux_binfmt *binfmt; //进程所运行的可执行文件的格式
int exit_code, exit_signal;
int pdeath_signal; //父进程终止是向子进程发送的信号
unsigned long personality;
//Linux可以运行由其他UNIX操作系统生成的符合iBCS2标准的程序
int did_exec:1;
pid_t pid; //进程标识符,用来代表一个进程
pid_t pgrp; //进程组标识,表示进程所属的进程组
pid_t tty_old_pgrp; //进程控制终端所在的组标识
pid_t session; //进程的会话标识
pid_t tgid;
int leader; //表示进程是否为会话主管
struct task_struct *p_opptr,*p_pptr,*p_cptr,*p_ysptr,*p_osptr;
struct list_head thread_group; //线程链表
struct task_struct *pidhash_next; //用于将进程链入HASH表
struct task_struct **pidhash_pprev;
wait_queue_head_t wait_chldexit; //供wait4()使用
struct completion *vfork_done; //供vfork() 使用
unsigned long rt_priority; //实时优先级,用它计算实时进程调度时的weight值
//it_real_value,it_real_incr用于REAL定时器,单位为jiffies, 系统根据it_real_value
//设置定时器的第一个终止时间. 在定时器到期时,向进程发送SIGALRM信号,同时根据
//it_real_incr重置终止时间,it_prof_value,it_prof_incr用于Profile定时器,单位为jiffies。
//当进程运行时,不管在何种状态下,每个tick都使it_prof_value值减一,当减到0时,向进程发送
//信号SIGPROF,并根据it_prof_incr重置时间.
//it_virt_value,it_virt_value用于Virtual定时器,单位为jiffies。当进程运行时,不管在何种
//状态下,每个tick都使it_virt_value值减一当减到0时,向进程发送信号SIGVTALRM,根据
//it_virt_incr重置初值。
unsigned long it_real_value, it_prof_value, it_virt_value;
unsigned long it_real_incr, it_prof_incr, it_virt_value;
struct timer_list real_timer; //指向实时定时器的指针
struct tms times; //记录进程消耗的时间
unsigned long start_time; //进程创建的时间
//记录进程在每个CPU上所消耗的用户态时间和核心态时间
long per_cpu_utime[NR_CPUS], per_cpu_stime[NR_CPUS];
//内存缺页和交换信息:
//min_flt, maj_flt累计进程的次缺页数(Copy on Write页和匿名页)和主缺页数(从映射文件或交换
//设备读入的页面数); nswap记录进程累计换出的页面数,即写到交换设备上的页面数。
//cmin_flt, cmaj_flt, cnswap记录本进程为祖先的所有子孙进程的累计次缺页数,主缺页数和换出页面数。
//在父进程回收终止的子进程时,父进程会将子进程的这些信息累计到自己结构的这些域中
unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap;
int swappable:1; //表示进程的虚拟地址空间是否允许换出
//进程认证信息
//uid,gid为运行该进程的用户的用户标识符和组标识符,通常是进程创建者的uid,gid
//euid,egid为有效uid,gid
//fsuid,fsgid为文件系统uid,gid,这两个ID号通常与有效uid,gid相等,在检查对于文件
//系统的访问权限时使用他们。
//suid,sgid为备份uid,gid
uid_t uid,euid,suid,fsuid;
gid_t gid,egid,sgid,fsgid;
int ngroups; //记录进程在多少个用户组中
gid_t groups[NGROUPS]; //记录进程所在的组
//进程的权能,分别是有效位集合,继承位集合,允许位集合
kernel_cap_t cap_effective, cap_inheritable, cap_permitted;
int keep_capabilities:1;
struct user_struct *user;
struct rlimit rlim[RLIM_NLIMITS]; //与进程相关的资源限制信息
unsigned short used_math; //是否使用FPU
char comm[16]; //进程正在运行的可执行文件名
//文件系统信息
int link_count, total_link_count;
//NULL if no tty 进程所在的控制终端,如果不需要控制终端,则该指针为空
struct tty_struct *tty;
unsigned int locks;
//进程间通信信息
struct sem_undo *semundo; //进程在信号灯上的所有undo操作
struct sem_queue *semsleeping; //当进程因为信号灯操作而挂起时,他在该队列中记录等待的操作
//进程的CPU状态,切换时,要保存到停止进程的task_struct中
struct thread_struct thread;
//文件系统信息
struct fs_struct *fs;
//打开文件信息
struct files_struct *files;
//信号处理函数
spinlock_t sigmask_lock;
struct signal_struct *sig; //信号处理函数
sigset_t blocked; //进程当前要阻塞的信号,每个信号对应一位
struct sigpending pending; //进程上是否有待处理的信号
unsigned long sas_ss_sp;
size_t sas_ss_size;
int (*notifier)(void *priv);
void *notifier_data;
sigset_t *notifier_mask;
u32 parent_exec_id;
u32 self_exec_id;
spinlock_t alloc_lock;
void *journal_info;
};
是不是吓到自己了👀,它居然有这么多的代码,我们目前只需要了解它的最主要的几行即可。
标示符PID : | 描述本进程的唯一标示符,用来区别其他进程 |
---|---|
状态: | 任务状态,退出代码,退出信号等。 |
优先级: | 相对于其他进程的优先级。 |
程序计数器`: | 程序中即将被执行的下一条指令的地址。 |
内存指针 : | 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针 |
上下文数据: | 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。 |
I/O状态信息: | 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。 |
记账信息: | 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。 |
其他信息 |
那么现在,我们的进程就从【程序+代码】进化为
- 进程=【
程序
+代码
+PCB
】
三、进程组织
有了PCB之后,我们操作系统在管理一个进程时,就不需要每次得到整个进程,而是只需拿到它的PCB,通过这个PCB对象就可以找到相对应的代码和数据。这样会大大节省空间提升效率。
例如:大学的校园我们都知道,比中学的大多了。但是呢在学校里每个学生他的人身是自由的,不可能每个人拿着一个牌子,上面自己写上个人的基本信息,这是不现实的。比方说学院的辅导员想要找一个学生的话,一定是通过在学籍系统中找到这个学生的所在行记录,才可以对其进行相关的操作,读者可以认为这个信息记录即为我们在上面所讲到的【PCB对象】
还有一个更大的好处,就是在很多个进程运行时,需要进入等待队列进行排队,这个队列是位于内存空间的,内存空间是小于外存的,所以,只需将进程的PCB放入队列排队即可,它的代码+数据依然在外存中,这样大大节省了内存空间我们的内存空间.
四、查看进程
1.ps指令
ps命令是一个用于查看进程状态的命令,常用于系统管理和故障排除。使用该命令可以列出当前系统中正在运行的进程,以及它们的PID、CPU利用率、内存占用等信息。
ps aux / ajx
a:显示终端上的所有进程,包括其他用户的进程
u:显示进程的详细信息
x:显示没有控制终端的进程
j:列出与作业控制相关的信息
指令:
ps ajx | head -1 && ps ajx | grep proc
1️⃣ ps -ajx:查看当前系统中所有进程
2️⃣ head -1 —— 获取第一行
3️⃣grep proc: —— 过滤只带【proc】的进程
带了head -1 —— 获取第一行,会有字段名显示。
📑什么在过滤进程的时候会有grep --color=auto proc这个东西呢?
- 当grep在进行过滤的时候自己也要变成一个进程,也可以看到他们使用grep命令的时候也带【proc】关键字的,所以在过滤的时候把自己也过滤出来了。这也侧面证明了所有指令在运行的时候都是进程
但如果我们不想看到这个也是有办法的,那就是在【grep】命令后面加个-v grep把其过滤掉即可
ps ajx | head -1 && ps ajx | grep proc | grep -v grep
2.top
3.ls /proc
我们的所有进程都会在Linux 下的这个【proc】目录中
我们需要根据进程的标识符【PID】来查找。
格式如下:
ls /proc/PID
五、总结
总的来讲:可执行程序被加载到内存,加载的是程序的代码和数据,可是操作系统并不认识哪些代码和数据对应哪些进程。所以,操作系统在加载可执行程序形成进程的同时,会创建一个描述该进程的结构体,称为PCB(process control block,进程控制块),linux操作系统下称之为task_struct。操作系统通过PCB中的各种属性信息(状态,优先级,内存指针字段,标识符…),就能够了解到这个进程的属性信息,进而控制进程的运行。此后,操作系统对于进程的管理,就转换成了对于PCB链表的增删查改。进一步的,我们可以了解到,进程就等于内核数据结构加可执行程序。未来,所有对进程的操作和控制,都只和进程的PCB有关,和进程的可执行程序没有关系。进程的查看指令ps,top等