11.OS操作系统学习:进程
引入:我们可以回顾一下杨老师在课上所讲进程~
进程是一个活动的实体,具有指定下一条指令以及执行一组相关资源的程序计数器
一,冯诺依曼体系结构
注意这里的存储器是内存,
体系规定程序的运行必须加载到内存中,cpu执行的代码访问内存中的数据。
IO就是从内存的角度来说的,
比如从键盘这个输入设备到内存叫做input
从内存这个Memory Unit到屏幕这个输出设备叫做output
二,操作系统
什么是系统调用?
操作系统的职能分为四大板块
-
内存管理
-
进程管理
-
文件管理
-
驱动管理
本文讨论进程,故具体讲解进程管理模块的知识
三,进程
**课本观点:**活动的实体,具有指定下一条指令以及执行一组相关资源的程序计数器(A program counter)
PC是cpu的一个寄存器,存放着下一条要执行指令的内存地址
**内核观点:**担当分配系统资源(cpu时间,内存)的实体
进一步讲:进程应该有两个部分:
数据(例如c语言包含着二进制代码和创建修改时间等)和信息(为了方便进行管理)
1.代码与数据
stat xx//来查看文件信息
2.PCB(process control block)
ps ajx//查看进程块包含的信息
PCB包含的内容
3.practice1关于pid
前置知识:通过getpid()函数来获得当前进程的pid值(进程的唯一编号)
注意getppid()就是获得父进程
#include<stdio.h>
#include<unistd.h> //Linux中睡眠函数的头文件
#include<sys/types.h>
int main()
{
int sec = 0;
while(1)
{
printf("这是一个进程,已经运行了%d秒 当前进程的PID为:%zu\n", sec, getpid());
sleep(1); //单位是秒,睡眠一秒
sec++;
}
return 0;
}
程序运行结果:
我们使用下面这串代码查找这个进程
ps ajx | head -1 && ps ajx | grep a.out | grep -v grep
提高:可以使用makefile简化我们的过程
4.top指令查看所有的进程
top
5.fork创建子进程
这个函数有两个返回值
进程创建成功时,给父进程返回子进程的PID,给子进程返回0
创建失败时,返回 -1
int fork(void)
0.原理
1.practice1:子进程引入
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
//pid_t是数据类型,实际上是一个整型
//通过typedef重新定义了一个名字,用于存储进行ID
pid_t pid; //parent pid
pid_t cid; //child pid
// getpid()函数返回当前进程的ID号
printf("Before fork Process id:%d\n", getpid());
/*
fork()函数用于创建一个新的进程,该进程为当前进程的子进程,创建的方法是:
将当前进程的内存内容完整拷贝一份到内存的另一个区域,两个进程为父子关系,他们会同时(并发)执行fork()语句后面的所有语句
fork()的返回值是:
如果成功创建子进程,对于父子进程fork会返回不同的值,对于父进程它的返回值是子进程的进程id值,对于子进程它的返回值是0.
如果创建失败,返回值为-1.
*/
fork();
printf("After fork Process id:%d\n", getpid());
pause();
return 0;
}
会发现执行了两次
2.practice2:交替执行
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{
pid_t cid; //child pid
printf("Before fork Process id:%d\n", getpid());
cid = fork();
if (cid == 0){
printf("Child process id (my parent pid is %d):%d\n", getppid(),getpid());
for(int i = 0; i < 30; i++){
printf("hello\n");
}
}else{
printf("Parent Process id: %d\n",getpid());
for(int i = 0; i < 30; i++){
printf("world\n");
}
wait(NULL);
}
return 0;
}
3.fork()函数工作原理
fork
创建子进程时,会新建一个属于 子进程
的 PCB
,然后把 父进程 PCB
的大部分数据拷贝过来使用,两者共享一份代码和数据。
四,进程状态
在杨老师课程中进程状态就是三个基本状态
- 运行态 2.就绪态 3.等待态
今天我们就要更进一步:
/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
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 */
};
1.阻塞
阻塞
就是进程
因等待某种条件就绪,而导致的一种不推进状态。
进程的阻塞就是不被调度
。
2.挂起
当CPU
资源紧张是,将进程交换至磁盘
挂起,此时内存中只有PCB
。
**挂起**
可以看作一种特殊的阻塞状态。
3.运行R
新建,就绪,运行都可以看作运行R
这个状态
#include<iostream>
using namespace std;
#include<unistd.h>
#include<sys/types.h>
int main()
{
while(1)
{
cout << "I'm a process, my PID is:" << getpid() << endl;
sleep(1);
}
return 0;
}
myProcess:test.cpp
g++ -o myProcess test.cpp
.PHONY:catPI
catPI:
ps ajx | head -1 && ps ajx | grep myProcess | grep -v grep
.PHONY:clean
clean:
rm -r myProcess
利用catPI查看当前进程的状态
会发现处在睡眠 S+
S+表示当前进程在前台运行
为什么不显示运行?
-
我们回顾一下,内部事件(启动IO设备)导致进程离开CPU。
-
我们把打印语句和睡眠语句屏蔽好,就会显示
R+
4.睡眠S
睡眠的本质就是进程阻塞,表示进程因某种资源而暂停运行。
-
可以手动关闭
ctrl
+c
-
kill -9 PID
5.休眠D(disk sleep)
休眠被称为不可中断休眠,无法被kill指令和操作系统杀死,只能默默等待进程阻塞结束,当拿到资源的时候,进程才会中止休眠D状态。
6.暂停T(stopped)
kill -19 PID 暂停进程
kill -18 PID 恢复进程
回复后的进程在后台运行,此时只能通过kill来终止
备注:在gdb
调试代码时,打断点实际上就是使进程在指定行暂停运行
,此时进程处于追踪暂停状态
。
7.死亡X(dead)
当进程被终止以后,就处于死亡X
状态
无法在任务列表观察到,知识一个返回状态
8.僵尸Z (zombie)
8.1理论
僵尸进程如果不被回收,将会导致内存泄漏问题
和标识符占用问题
子进程被终止,没有被父进程回收的时候就会变成僵尸进程
8.2practice
#include<iostream>
using namespace std;
#include<unistd.h>
#include<sys/types.h>
int main()
{
pid_t ret = fork();
if(ret == 0)
{
while(1)
{
cout << "I'm son process, my PID: " << getpid() << " PPID: " << getppid() << endl;
sleep(1);
}
}
else if(ret > 0)
{
while(1)
{
cout << "I'm father process, my PID: " << getpid() << " PPID: " << getppid() << endl;
sleep(1);
}
}
else
{
while(1)
{
cout << "Make son process fail!" << endl;
sleep(1);
}
}
return 0;
}
kill子进程之后
9.孤儿进程
9.1理论
如果子进程不被1号进程领养,
子进程退出时无人收回,称为游离的僵尸
僵尸进程有内存泄露的风险,因此子进程会被OS领养