Linux进程概念

一、冯诺依曼体系结构:

现代计算机的硬件体系结构(规定了现代计算机应该具有那些硬件单元)
包含了五大硬件单元:

  1. 输入设备:鼠标,键盘,麦克风等
  2. 输出设备:显示器,打印机等
  3. 存储器:内存条
  4. 运算器:中央处理器—cpu
  5. 控制器:中央处理器—cpu

在这里插入图片描述
注意:

  • 图中存储器指的是内存
  • 不考虑缓存情况,CPU智能对内存就行读写,不能直接访问输入和输出设备

也就是说,所有的计算机硬件全部围绕存储器工作,存储器在中间起到数据缓冲的作用。

使用内存作为存储器的原因:内存的吞吐量大。

存储器分内存和外存,是因为没有一种内存可以既速度飞快,又保存东西长久,还便宜。所以,用内存和外存的组合来解决这个问题。

  1. 存储器有两个作用,一个是存储文件,关机不丢失,另一个是运行程序,和cpu交换数据。简单讲,二者的区别是,外存是存储东西的,内存是运行程序的。
  2. 硬盘是主要的外存。它完成第一个任务是没问题的,但完成第二个任务不能胜任,因为速度太慢了,起码慢100倍。于是只好用内存来代替它,完成第二个任务。
  3. 但内存虽然速度快,价格却非常贵,而且关机文件就丢失,也不能做成很大的容量,所以,还得硬盘来辅助。所以,就造成了内存结合外存的局面。

二、操作系统

什么是操作系统

操作系统由内核和应用组成,在计算机软硬件架构中,操作系统起到的是管理的作用,负责管理计算机的软硬件资源,也就是说,操作系统是一个稿管理的软件。

用户、操作系统、计算机硬件之间的关系

由于操作系统的内核过于脆弱,所以不允许用户从外部直接操作,于是开发人员在开发操作系统的时候,会给用户提供系统调用接口,用户通过系统调用接口来访问操作系统内核,完成一系列工作。

但是由于系统调用接口有的过于复杂,并不是人人都是程序员,并没有办法很好的使用这些系统调用接口,于是通过把系统调用接口进行封装,可以把多个复杂的系统调用接口封装成一个命令,于是就有了现在的用户操作接口,包括shell命令,库函数等。

用户就可以通过用户操作接口,输入操作指令,就可以实现对操作系统内核的调用。

计算机硬件是通过各自的驱动程序来实现对操作系统的访问的,硬件驱动会自动采集硬件信息,传输给操作系统,完成鼠标,键盘,硬盘等一系列操作。

图示:
在这里插入图片描述

三、进程概念

从用户角度来看:进程就是一个正在运行的程序。

从操作系统的角度来看:操作系统运行一个程序,需要描述这个程序的运行过程,在Linux下,这些描述信息杯保存在一个task_struct结构体中,每一个结构体中都保存着一个程序的运行信息,这些结构体统称为PCB(process control block),所以对于操作系统来说,进程就是PCB
进程的描述信息都包括:进程的标识符PID,进程状态,进程优先级,程序计数器,上下文数据,内存指针,IO信息等。这些都由操作系统进行调度,实现对进程的有序管理。

通过对运行程序的描述,CPU就可以调度哪个程序去占用CPU去运行指令,要运行哪个程序,操作系统则找到对应程序的PCB,在PCB中取出程序运行所需要的信息,把他加载到CPU上,就可以运行这个程序了。

内核源代码路径:/usr/src/kernels/3.10.0-1160.45.1.el7.x86_64/include/linux/sched.h

在这里插入图片描述

查看进程

在Linux输入以下指令,来直接显示进程的状态。

ps -ef 或者ps -aux

在这里插入图片描述

创建进程

在LIinux中,进程就是一个PCB,就是一个task_struct结构体,创建一个进程,就是创建一个task_struct结构体。

Linux中创建子进程的函数——fork()
在Linux中,通过 fork() 这个函数来创建进程进程,这个接口是通过复制调用 fork() 接口的进程,创建一个新的进程。也就是说一个进程(被称为父进程),通过调用fork接口,创建了一个新的进程(子进程),子进程完全复制了父进程的内容

通过代码理解一下

#include<stdio.h>
#include <unistd.h>

int main ()
{
	fork();   //创建子进程
	printf("hello world\n");
	return 0;
}

运行的结果
在这里插入图片描述
可以发现hello world 被打印了两遍,这是因为,在父进程中,调用了fork接口创建了一个子进程,子进程完全复制的是父进程的信息,所以子进程运行了一遍printf函数,父进程也运行了一次printf函数。
如果我们在创建子进程前面再加上一个printf函数

#include<stdio.h>
#include <unistd.h>

int main ()
{
	printf("hello\n");
	fork();   //创建子进程
	printf("hello world\n");
	return 0;
}

在这里插入图片描述
可以发现 hello 只之打印了一遍,hello world 打印了两遍,通过这个运行结果对比我们可以知道,子进程虽然是完全复制的父进程的所有信息,但是子进程只会从调用 fork 这个接口之后的语句开始运行。

使用 getpid() 接口获取调用这个接口的额PID可以更直观的看出

#include<stdio.h>
#include <unistd.h>

int main ()
{
	printf("hello\n");
	fork();   //创建子进程
	printf("hello world: %d\n",getpid()); //获取父、子进程的PID
	return 0;
}

在这里插入图片描述
这两次输出所运行的printf函数并不是由同一个进程运行的。再使用ps -ef 可以查看这两个进程。在这里插入图片描述

四、进程状态

进程状态主要是作用于操作系统对于进程的管理,让操作系统知道该进程处于什么样的状态,就会自动对不同状态的进程进行不同的操作。

Linux下的进程状态

  1. 运行态(R):正在运行,或者随时可以运行的进程,统称为运行态,只有属于运行态的进程才能够被操作系统调度在CPU上进行运行。
  2. 可中断休眠态(S):可以被终端的休眠态,在满足唤醒条件,或者休眠被中断的情况下可以进入运行态。
  3. 不可中断休眠态(D):不能被中断的休眠状态,就是说只有满足唤醒条件后才会进入运行态。
  4. 暂停态(T):程序停止运行,什么都不做。
  5. 僵尸态(Z):进程已经推出不在调度了,但是这个进程的资源还没有被完全释放,等待处理的一种状态。

代码演示:

可中断休眠态:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main ()
{
	int a = 0;
	sleep(5);
	while (1)
	{
		a++;
	}
	return 0;
}

程序运行后我们可以查看当前进程
在这里插入图片描述
开始程序会进行5秒的睡眠,此时处于可中断休眠态(S),五秒后休眠状态被打断,进入运行态。

僵尸态:
处于僵尸态的进程,成为僵尸进程,是一种已经退出了,但是资源没有被释放的进程。

  • 产生原因:子进程先于父进程释放,但是父进程没有关注到子进程的退出,在子进程退出后,在进程PCB中保存了自己退出的返回值,在父进程没有关注处理的情况下,PCB资源是不会被释放的,因此系统并不会完全释放子进程的全部资源,这个进程就成为了僵尸进程。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main ()
{
	pid_t pid = fork();
	if (pid < 0)
	{
		//创建子进程失败
	}
	else if(pid == 0)
	{
		//子进程运行以下代码,对于子进程来说,返回值是0
		printf("子进程的PID:%d\n",getpid());
		sleep(5);
		exit(0);
	}
	else
	{
		//父进程运行一下代码,对于父进程来说,返回值是子进程的PID
		printf("父进程的PID:%d\n",getpid());
	}
	while(1)
		sleep(1);
	return 0;
}

运行后我们发现,程序在前五秒睡眠期,属于可中断休眠态,五秒后,执行 exit 指令,子进程先于父进程推出,但父进程并没有关注到子进程的退出,释放子进程的资源,所以子进程成为僵尸进程。
在这里插入图片描述
孤儿进程:
父进程先于子进程退出,这个子进程就成为了孤儿进程,这个子进程的父进程成为1号进程,并且这个孤儿进程会一直运行在后台,不占据终端。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(char argc, char* argv)
{
    pid_t pid = fork();
     if(pid < 0)
     {
         printf("for error\n");
     }
     else if(pid == 0)
     {
         printf("child-%d",getpid());
         sleep(15);
     }else
     {
         printf("parents-%d\n",getpid());
         sleep(3);
         exit(0);
     }
     return 0;
}

在父进程退出后,子进程成为孤儿进程,父进程成为一号进程。
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值