⭐️这篇博客就要开始聊一聊进程相关的内容了,这聊这个之前,我们还需要了解一下操作系统相关和管理的概念,这样更加有助于我们了解进程的内容了。
冯·诺伊曼体系结构是现代计算机的基础,现在大多计算机仍是冯·诺伊曼计算机的组织结构。(下面是一张冯若依曼体系结构的图片)
操作系统是什么?
概念: 管理计算机硬件与软件资源的计算机程序。英文名称是Operator System(简称OS)
为什么要有操作系统?
大体分为两点原因:
如何管理?
管理: 管理就是对被管理对象进行先描述,再组织这么两个步骤的操作。
系统调用和库函数的概念
进程: 计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
根据上面对管理的介绍,我们知道,要想了解操作系统如何管理进程,我们应该先对进程进行描述。
如何描述进程?
简单的语言描述进程
进程 = 程序文件内容 + 由操作系统自动创建的相关数据结构
task_struct内容有哪些?
优先级: 程序被CPU执行的顺序(后面会单独介绍)
程序计数器: 一个寄存器中存放了一个pc指针,这个指针永远指向即将被执行的下一条指令的地址
内存指针: 包含程序代码和进程相关的数据的指针,还有和其它进程共享的内存快的指针。这样就可以PCB找到进程的实体
上下文数据: 在单核CPU中,进程需要在**运行队列(run_queue)**中排队,等待CPU调度,每个进程在CPU中执行时间是在一个时间片内的,时间片到了,就要从CPU上下来,继续去运行队列中排队
I/O状态信息: 包括显示的I/O请求,分配给进程的I/ O设备和被进程使用的文件列表
记账信息: 能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等
其他信息
组织进程
在内核源代码中发现,所有运行在系统里的进程都以task_struct链表形式存在内核中。
下面是两个系统调用的函数:
#include
#include
#include
int main()
{
printf("pid:%d ppid:%d\n", getpid(), getppid());
return 0;
}
代码运行结果如下:
注意: 普通进程的父进程基本都是bash
先看下面一段代码:
#include
#include
#include
int main()
{
pid_t ret = fork();
printf("pid:%d, ppid%d\n", getpid(), getppid());
sleep(1);
return 0;
}
代码运行结果如下:
这里只用一条输出语句,但是却打印了两句话,这是为什么?
这里父进程创建子进程成功后,两个进程具有独立性,会分别执行fork后面的代码,完成打印,所以有两条语句。
分析下面几个问题:
再看一个实例 父子进程实现分流,同时进入if和else的两个分支
#include
#include
#include
int main()
{
pid_t ret = fork();
if (ret < 0)
{
perror("fork");
return 1;
}
else if (ret == 0)// 子进程
{
printf("I am child-pid:%d, ppid:%d\n", getpid(), getppid());
sleep(1);
}
else if (ret > 0)// 父进程
{
printf("I am parent-pid:%d, ppid:%d\n", getpid(), getppid());
sleep(1);
}
sleep(1);
return 0;
}
进程状态
下面是进程状态在源码中的定义
static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};
介绍:
S睡眠状态: 进程处于等待队列中,在等待时间完成,这里的睡眠是可以中断的,也叫浅睡眠
D磁盘休眠状态: 这种休眠是不可以被中断的,这个时候的进程通常是在等待IO结束(此时在和磁盘进行IO,以免被OS误杀进程)
X死亡状态: 这个状态只是一个返回状态,任务列表中是看不到的
注意: kill的1-31是普通信号,34-64属于实时信号
进程状态的查看
有下面两种命令(前者查看所用进程的名字,后者可以查看进程的父子关系):
ps aux/ps axj
僵死状态(Zombies) 是一个比较特殊的状态。当进程退出并且父进程(没有读取到子进程退出的返回代码时就会产生僵死(尸)进程
特征:
实例演示:
#include
#include
#include
#include
int main()
{
pid_t ret = fork();
if (ret < 0)
{
perror("fork");
return 1;
}
else if (ret == 0)// 子进程
{
printf("I am child-pid:%d, ppid:%d\n", getpid(), getppid());
exit(0);// 子进程退出
}
else if (ret > 0)// 父进程
{
printf("I am parent-pid:%d, ppid:%d\n", getpid(), getppid());
sleep(10);
}
sleep(1);
return 0;
}
下面是检测进程状态的运行脚本:
while :; do ps axj | head -1 && ps axj | grep test | grep -v grep; sleep 1; echo "############"; done
孤儿进程: 父进程先退出,子进程就称之为“孤儿进程”。孤儿进程会被1号systemed进程领养
如果进程没有父进程,且当前进程退出,那么当前进程进入僵死状态,该进程资源无法被回收造成内存泄漏,但是OS考虑了这个问题,孤儿进程是会被领养的
实例演示:
#include
#include
#include
#include
int main()
{
pid_t ret = fork();
if (ret < 0)
{
perror("fork");
return 1;
}
else if (ret == 0)// 子进程
{
printf("I am child-pid:%d, ppid:%d\n", getpid(), getppid());
}
else if (ret > 0)// 父进程
{
printf("I am parent-pid:%d, ppid:%d\n", getpid(), getppid());
sleep(10);
exit(0);
}
sleep(1);
return 0;
}
概念:
PRI 和NI
修改进程优先级
进入top后按“r”–>输入进程PID–>输入nice值
几个概念:
以上就是进程相关的一些基本介绍,这块的内容需要自己不断用代码验证和自己理解,这样更有助于后面的学习。喜欢的话,欢迎点赞支持和关注~