一.进程
- 管理的本质是对数据进行管理;
- 管理的方法是先描述,再组织;
1.进程概念
进程就是被加载到内存中的程序,是运行起来的程序
·程序本质是存放在磁盘中的一个文件
·根据冯洛伊曼体系结构可知,为了提高计算机整体的效率,CPU不直接与外设交互,只会与内存交互
综上,我们运行一个程序,就必须加载到内存中,因为CPU从内存中读取程序中的代码和数据
程序加载到内存后,操作系统要对其管理,而我们知道,对程序的管理在本质上是通过先描述再组织的管理方法对程序数据进行管理。
如同校长将学生信息抽象成结构化数据,操作系统会从程序中抽象出共有属性来构建一个结构体,然后为每一个进程创建一个结构体对象,然后通过合适的数据结构将这些结构体对象组织起来。对某个进程的管理就转变为了对数据结构中某个节点的管理。
在操作系统中,这个用于描述进程的结构体称之为 进程控制块-PCB。
2.进程描述-PCB
进程控制块PCB (process control block):操作系统中用于描述进程的工具,其中包含的是进程属性的集合;Linux操作系统下的PCB是 task_struct,它是Linux内核的一种数据结构,其内容可以分为如下几类:
标示符: 描述本进程的唯一标示符,用来区别其他进程;
状态: 任务状态,退出代码,退出信号等;
优先级: 相对于其他进程的优先级;
程序计数器: 程序中即将被执行的下一条指令的地址;
内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针;
上下文数据: 进程执行时处理器的寄存器中的数据;
I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表;
记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等;
抽象出来可以用如下结构体来表示 (假设task_struct使用链表进行组织):
struct task_struct {
//进程的所有属性
... ...
//进程对应的代码和数据的地址
... ...
//下一个进程的地址
struct task_struct* next;
};
二.进程的常见操作
1.查看进程
(1)ps axj 指令 + grep 管道查看指定进程
//test.c
#include <stdio.h>
#include <unistd.h>
int main()
{
while(1)
{
printf("hello process!\n");
sleep(1);
}
return 0;
}
(2)在 “/proc” 系统文件夹中查看所有进程
2.结束进程
对于我们自己编写的普通进程,有以下两种方式结束进程
(1)[Ctrl + c] 来结束;
(2)使用 kill 命令,指定 -9 选项来结束;
3.通过系统调用获取进程标示符pid
可以通过使用操作系统给我们提供的系统调用接口 getpid() 与 getppid() 来获取进程id和父进程id (进程ID是一个进程的唯一标识):
4.创建进程
(1)在命令行中启动进程
//test.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
while(1)
{
printf("我是一个进程,我的ID是:%d,我的父进程ID是:%d\n", getpid(), getppid());
sleep(1);
}
return 0;
}
(2)使用代码创建进程
fork函数:
(1)功能:
用于创建一个进程,所创建的进程复制父进程的代码段/数据段/BSS段/堆/栈等所有用户空间信息;在内核中操作系统重新为其申请了一个PCB,并使用父进程的PCB进行初始化;
(2)特性:
fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:
在父进程中,fork返回新创建子进程的进程ID;
在子进程中,fork返回0;
如果出现错误,fork返回一个负值;
因此我们可以通过fork返回的值来判断当前进程是子进程还是父进程。(注: fork 调用生成的新进程与其父进程谁先执行不一定,哪个进程先执行要看系统的进程调度策略)fork函数之前的代码,只有父进程执行;
fork函数之后的代码,父子进程都执行
测试:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
pid_t id = fork();
if(id == 0)
{
while(1)
{
printf("子进程,pid:%d, ppid:%d, id:%d\n", getpid(), getppid(), id);
sleep(1);
}
}
else if(id > 0)
{
while(1)
{
printf("父进程,pid:%d, ppid:%d, id:%d\n", getpid(), getppid(), id);
sleep(1);
}
}
else
{
printf("子进程创建失败!\n");
}
return 0;
}