进程状态
一、进程状态就是一个task_struct内的一个整数
二、进程状态
(1)一个进程在CPU上运行,本质是CPU在系统内部维护一个叫调度队列的东西。调度队列里有每一个进程的tast_struct,含有代码和数据的信息。CPU要去选择一个进程去运行,本质是选择一个进程特定的PCB去运行。同时这个队列也是一个双向链表。

(2)状态:
运行:进程在这个调度队列当中就是运行状态。
阻塞:等待某种设备和资源就绪,如键盘、鼠标、显示器。C语言中scanf和C++中的cin,当程序运行到这两条语句时需要等待键盘的输入就会阻塞。
操作系统管理这些硬件也是先描述再组织,例如用结构体struct device描述设备。

同时在该结构体里定义一个struct task_struct* wait_queue的指针,叫做等待队列,这样每个设备都有一个等待队列。假设CPU正在运行进程,需要读取设备,操作系统就会去查询设备的状态,假如不活跃,就无法执行。如果无法执行,操作系统就会把该进程从CPU里面的运行队列移出,再把这块被移出的task_struct链入特定设备的等待队列当中,此时该进程不在调度队列当中,就不会被调度了,此时就处于阻塞状态。于是,从运行到变成阻塞的本质是把PCB链入到不同的队列结构当中。如果设备活跃了,就会把该PCB重新放进调度队列当中,继续运行。
挂起:假如阻塞的进程太多导致运行内存严重不足,操作系统就会在磁盘当中的swap分区存放这些多出来的阻塞进程。此时这些进程的状态就叫阻塞挂起,而这个放入到swap分区的行为叫做唤出,当内存有空间时就会重新把这些阻塞挂起的进程放入等待队列中,这个过程叫做唤入。如果把阻塞的进程挂起内存还是不足就会把调度队列当中的进程唤出,此时这个进程状态就叫做运行挂起。
(3)理解内核链表:
以前创建链表节点时,会让节点的指针指向下一个节点的地址,就是整个节点。但是内核链表里,是把节点单独封装成一个类型,再把这个类型作为目标数据结构的成员。
typedef struct list_head
{struct list_head* next, prev;
}list_head;struct XXX
{int data;list_head links;
};这样子就不让这些XXX结构体里的指针指向下一个结构体,而是指向下一个结构体或上一个结构体里的指针。

但是这样有一个问题,就是无法访问struct XXX里面的其他属性。由于结构体内地址递增,于是就可以将0地址强转为struct XXX类型的,然后再去指向这个结构体里面的links成员去取地址,
&((struct XXX*)0->links),这样就求出了links在这个结构体里的偏移量。有了偏移量就可以用链表节点的links减这个偏移量就可以得到这个结构体struct XXX 了,这样结构体里的属性就都可以访问 了。有了这种方法就允许存在这个links去指向其他的links。那么可以再来好几组links,让这些links去和其他的links以不同的方式去组合,同时形成二叉树、队列、链表。

三、进程状态:
*R运行状态:并不意味着进程一定在运行中,它表明进程要么是在运行中要么是在运行队列里。
*S睡眠状态:意味着进程在等待事件完成
*D磁盘休眠状态:有时候也叫不可中断睡眠状态,在这个状态进程通常会等待IO结束。
*T停止状态:可以通过发送SIGSTOP信号来让进程停止,这个被暂停的进程可以通过发送SIGCONT信号让进程继续运行。
*X死亡进程:这个状态只是一个返回状态,不会在任务列表里看到此状态。
*僵尸进程:在Linux系统里,所有进程都是某一个进程的子进程,创建这些子进程是要去完成某种事情的,比如shell创建子进程是为了执行指定命令,也就是说子进程是为了完成父进程一部分工作的,完成之后就一定要返回工作完成的结果。所以一个子进程结束后不会被立即释放所有资源,必须把自己退出的PCB信息暂时维持住,直到被父进程获取。在这个退出子进程到父进程获取子进程信息的这个过程中,子进程就会维持一个状态,这个状态就叫做Z僵尸状态。如果父进程一直不获取子进程的退出信息,子进程的僵尸状态就会一直维持,PCB一直维护,PCB是task_struct的对象,这样就会造成内存泄漏。
*孤儿状态:如果父进程先退出,子进程就会进入孤儿状态,需要被回收,会被1号进程领养,这个1号进程就是操作系统。
