Linux——进程的概念

task_struct

task_struct 是linux下管理进程的结构,称为PCB,进程控制块。linux所有的指令本质上都是一个进程。进程 = task_struct + 进程的数据、代码、可执行程序,有属性、有内容。
进程是系统的工作单元。系统由多个进程组成,包括操作系统进程(执行系统代码)、用户进程(执行用户代码)。这些进程都会并发执行,可以在单 CPU 上采用多路复用来实现。

内容描述
标示符(PID)进程的唯一标示符
状态状态,退出码,退出信号等
优先级进程的优先级
程序计数器程序下一条指令的地址(PC指针)
内存指针程序代码和进程数据的指针,还有和其他进程共享的内存块的指针
上下文数据进程执行时处理器的寄存器中的数据
I/O状态信息包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
记账信息包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。

操作系统不直接管理对象,而是管理对象的数据。数据由驱动从硬件获取,反馈给操作系统。如何管理大量的数据?提取对象的数据,用一个数据类型来描述,task_struct。一切皆对象,抽取对象的属性,来描述具体的对象。从管理对象->管理数据->管理数据结构

进程的属性用tast_struct描述并组织,进程对应的代码、数据、资源(可执行程序)同样加载在内存中。

进程的pid和ppid

  • pid是每一个进程在系统中的唯一标识符
  • ppid是该进程的父进程id

每次在命令行启动进程,操作系统都会为进程分配随机的pid,但是父进程的id是不变的。
为什么?因为命令行上启动的进程的父进程都是bash。

bash进程

Linux下每一个已登录的用户都有bash进程,称为一个会话。当一个用户在终端上面登录后,Linux系统就会给用户提供命令行服务,执行的命令都是这个bash进程的程序替换后的子进程,因此Bash是大部分命令行程序的父进程。

/*
 * pointers to (original) parent process, youngest child, younger sibling,
 * older sibling, respectively. 
 */
struct task_struct *real_parent; /* real parent process */
struct task_struct *parent; /* recipient of SIGCHLD, wait4() reports */
/*
 * children/sibling forms the list of my natural children
 */
struct list_head children;  /* list of my children */
struct list_head sibling;   /* linkage in my parent's children list */
struct task_struct *group_leader;   /* threadgroup leader */

进程的状态

操作系统层面

  • 运行态:在运行队列中排队的进程称为运行态。不是正在运行,而是表示准备就绪,等待CPU调度。
  • 阻塞态:进程不止在申请CPU资源,每一种资源都在操作系统中有自己的等待队列,例如磁盘、网卡等。 当资源没有准备好,操作系统会把CPU的runqueue中的进程交给对应资源的等待队列wait_queue。CPU才可以执行其他的任务。当资源的读写资源就绪才会重新把这个进程放回CPU的运行队列中。类似一边下东西一边看在线视频会很卡,这就是进程阻塞。
  • 终止态:进程还在,但是不会再被运行,随时等待被释放。进程终止了,应该把资源如结构体、内存等释放掉。
  • 挂起态:挂起的进程不会申请CPU资源。操作系统会把短期内不会被调度(在队列中排的比较靠后)的进程的代码和数据置换到磁盘的swap分区上,而不是在内存里占着,浪费别的进程的资源。例如电脑换了固态硬盘运行会比较流畅。
    #swap分区

进程都终止了,为什么不立马释放,而是要维护一个终止态?
因为进程终止后需要释放资源,如关闭文件描述符,回收内存等,这些工作需要一些时间,需要等待操作系统空闲的时候来清理。

Linux系统层面

static const char *task_state_array[] = {
    "R (running)",      /*  0 */
    "S (sleeping)",     /*  1 */
    "D (disk sleep)",   /*  2 */
    "T (stopped)",      /*  4 */
    "T (tracing stop)", /*  8 */
    "Z (zombie)",       /* 16 */
    "X (dead)"          /* 32 */
};
状态描述
R(TASK_RUNNING)进程正在执行或准备就绪、等待调度
S(TASK_INTERRUPTIBLE)当进程等待I/O操作时,会进入可中断的睡眠状态。
D(TASK_UNINTERRUPTIBLE)当进程等待磁盘操作完成时,会进入不可中断的睡眠状态。
T(TASK_STOPPED或TASK_TRACED)当进程被暂停或跟踪时,当进程被调试器暂停时,它会进入暂停状态。
Z(TASK_DEAD - EXIT_ZOMBIE)当子进程已经退出但尚未被父进程回收称为僵尸进程。
X(TASK_DEAD - EXIT_DEAD)这种情况下,进程即将被销毁。当进程已经退出并且已被父进程回收时,它的状态是
运行状态


这个程序在死循环打印,但是进程状态却是S。程序是在死循环,但是代码很少,CPU运行很快,大部分时间都在等待外设就绪,因为显示器作为硬件外设速度太慢了。
状态后面带+表示这是一个前台进程。可以在前台用ctrl+c杀掉进程。
#前台进程

僵尸进程
什么是僵尸进程?

通常情况下,父进程会使用 waitwaitpid 函数回收退出之后的子进程的资源,并获得子进程的退出状态。

  • 如果子进程先于父进程退出,同时父进程太忙了,无瑕回收子进程的资源,子进程不会直接变为X状态,资源不会立刻被回收。而是变为Z状态,僵尸状态
为什么有僵尸状态?

在进程执行完之后,要把进程的执行结果通过进程等待返回给父进程或操作系统。以便操作系统或父进程作二次决策。但是父进程还没退出,就需要一直保持这个PCB,等待父进程回收后才能删除。如果父进程完了没有回收资源就退出了,子进程就变成一个孤儿,这时候就被1号进程init领养,由init完成资源的回收工作。
例如main函数的返回值,返回后就给到了PCB中的退出信息。

僵尸进程的危害?

如果父进程一直不回收,那僵尸进程就一直存在,进程是加载到内存中的,需要操作系统维护PCB,就会有内存资源的浪费。

如何杀死僵尸进程?

我们可以在命令行上用kill命令,通过发送信号来杀死一个进程。但是无法杀死一个僵尸进程,即使是kill -9。因为进程已经终止了,但是资源还在进程的PCB中保存着,需要被回收。
但是可以尝试杀掉僵尸进程的父进程,让僵尸进程变成孤儿进程。

孤儿进程
  • 如果父进程先于子进程结束,则子进程成为孤儿进程。孤儿进程将被 init (1号)进程领养,并由 init 进程回收该孤儿进程的资源。
    但是ps查看进程状态的时候,父进程没有进入Z状态?
    因为命令行上启动的进程的父进程是bash,bash会自动回收他的子进程。
睡眠状态

S(Sleeping)和D(Disk Sleeping)都表示阻塞状态。

  • S:浅度睡眠,可中断睡眠,一直在等待资源,可以被唤醒也可以被杀死。
  • D:不可中断睡眠,等待磁盘资源。
    进程如果等待IO的时候被杀掉,就不能跟资源正确完成交互,可能会造成数据丢失。因此 Linux 引入了D状态,这个状态的进程不会被操作系统杀掉。
    要杀死D状态,只能拔电源,如果太多D状态的进程,可能无法关机。只能让进程磁盘交互结束。
进程暂停

进程可以暂停,例如播放音乐和视频,还有类似调试代码的情况。分为stopped和tracing stop。T表示直接暂停,t表示追踪暂停。当进程被调试的时候,遇到断点处的状态。

进程优先级

优先级表明进程获取资源的优先顺序。为什么要排队?系统资源不够。

[chfens@VM-12-8-centos processTest]$ ps -l
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
0 S  1002  3310  3309  0  80   0 - 29477 do_wai pts/24   00:00:00 bash
0 S  1002  3368  3310  0  80   0 -  1868 do_wai pts/24   00:00:00 scl
0 S  1002  3369  3368  0  80   0 - 28584 do_wai pts/24   00:00:00 bash
0 S  1002  3379  3369  0  80   0 - 29596 do_wai pts/24   00:00:00 bash
0 R  1002  3471  3379  0  80   0 - 38595 -      pts/24   00:00:00 ps

pri表示priority,ni表示nice。nice是进程优先级的修正数据。要改优先级得改nice。但是进程的优先级被修改会打破调度器平衡,因此要root才能修改。
但是优先级高的怎么打断低的呢?这可是一个运行队列,只能先进先出?
根据不同的优先级,将指定进程放入不同的队列中。比如1级优先级一条队列,2级进程一条队列。比如每次都是从0级队列开始找,空即找下一个优先级的队列。但实际中是用哈希和位图。

抢占优先级

优先级高的会抢占优先级低的进程,抢占后需要用寄存器保存被抢占进程的上下文信息。
调度复杂度O(1)

进程的特性

独立性

各个进程不会相互干扰。进程本质是内核数据结构+代码和数据。多个进程在系统中运行不等于进程在同时运行。写时拷贝

竞争性

进程存在抢占优先级,低优先级进程会被高优先级的进程打断。调度器会直接把进程从CPU上剥离,让优先级更高的运行。

并行

多个进程在多个CPU下分别同时运行

串行

操作系统大多数是分时的,分时操作系统是基于时间片轮转而调度的。
比如有10个进程,在一段时间段内,给1号进程1ms的时间运行,1ms到了就切换到2号进程,并把原来的1号进程放在运行队列的尾端。


进程的当前路径


a.out用于创建一个log.txt,发现bash在哪个路径下调用程序,就会在哪里创建文件。

查看这个进程的信息,cwd表示current working directory,就是testCode,因为是在testCode下加载的进程,而exe实际是指向别的地方的a.out。
表明进程的当前路径是进程被调用的路径。

fork

本来是单执行流,之后分叉变成两个执行流,形如叉子一样的创建子进程的函数称为fork。表现在操作系统中多了一个PCB,子进程继承自父进程。fork函数之后的父子进程代码共享,但是数据独立。可以通过判断返回值区分父子进程,执行不同功能。

fork的返回值

fork会给子进程返回0;给父进程返回子进程的pid
为什么有两个返回值?
一个父进程可以有多个子进程,父进程必须能描述各个子进程。因此fork之后给父进程返回子进程的pid,给子进程返回0。
为什么会返回两次?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值