Linux进程原理及系统调用

进程

​ 操作系统作为硬件的使用层,提供使用硬件资源的能力,进程作为操作系统使用层,提供
使用操作系统抽象出的资源层的能力。
​ 进程:是指计算机中已运行的程序。进程本身不是基本的运行单位,而是线程的容器。程
序本身只是指令、数据及其组织形式的描述,进程才是程序(那些指令和数据)的真正运行实
例。
​ Linux内核把进程叫做任务(task) ,进程的虚拟地址空间可分为用户虚拟地址空间和内
核虚拟地址空间,所有进程共享内核虚拟地址空间,每个进程有独立的用户虚拟地址空间。

进程有两种特殊的形式:没有用户虚拟地址空间的进程叫内核线程,共享用户虚拟地址空间的进程叫用户线程、共享同一个用户虚拟地址空间的所有用户叫做线程组。

进程的四要素:

  • 有一段程序代其执行;
  • 有进程专用的系统堆栈空间;
  • 在内核有task_struct数据结构
  • 进程有独立的存储空间,拥有专用的用户空间;

如果具备前面三条的话而缺少第四条,那就称为“线程”。如果完全没有用户空间,就称为“内核线程”;如果共享用户空间则就称为“用户线程”

进程的生命周期

​ Linux操作系统属于多任务操作系统,系统中的每个进程能够分时复用CPU时间片,通过有效的进程调度策略实现多任务并行执行。而进程在被CPU调度运行,等待CPU资源分配以及等待外部事件时会属于不同的状态。进程状态如下:

  • 创建状态:创建新进程;
  • 就绪状态:进程获取可以运作所有资源及准备相关条件;
  • 执行状态:进程正在CPU中执行操作;
  • 阻塞状态:进程因等待某些资源而被跳出CPU;
  • 终止状态:进程消亡。

Linux内核进程状态间关系如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3PbM8F5R-1662568077245)(image/2-1.png)]

Linux内核提供API函数来设置进程的状态:

TASK_RUNNING(可运行状态或者可就绪状态);

TASK_INTERRUPTIBLE(可中断睡眠状态,又叫浅睡眠状态);

TASK_UNINTERRUPTIBLE(不可中断状态,又叫深度睡眠状态);可以通过ps命令查看被标记为D状态的进程。

TASK_STOPPED(终止状态);

EXIT_ZOMBI(僵尸状态);

task_struct数据结构分析

​ 进程是操作系统调度的一个实体,需要对进程所必须资源做一个抽象化,此抽象化为进程控制块(PCB, Process Control Block),在Linux内核里面采用task_struct结构体来描述进程控制块。Linux内核设计进程及进程和程序的所有算法都围绕task_struct的数据结构而建立操纵。具体Linux内核源码task_struct结构体核心成员如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

进程优先级/系统调用

进程优先级

限期进程的优先级比实时进程要高,实时进程的优先级比普通进程要高。

  • 限期进程的优先级是-1;
  • 实时进程的优先级1-99,优先级数值越大,表示优先级越高; .
  • 普通进程的静态优先级为: 100-139, 优先级数值越小,表示优先级越高,可通过修改nice值改变普通进程的优先级,优先级等于120加上nice值。

​ 并非所有进程都具有相同的重要性。除了大多数我们所熟悉的进程优先级之外,进程
还有不同的关键度类别,以满足不同需求。首先进行比较粗糙的划分,进程可以分为实时进程
和非实时进程(普通进程)。
​ 实时进程优先级(0-99)都比普通 进程的优先级(100-139)高。当系统中有实时进程
运行时,普通进程几乎无法分到赶时间片(只能分到5%的CPU时间)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IWVCzFhx-1662568077251)(image/2-13.png)]

Linux进程优先级定义如下:

int				prio;
int				static_prio;
int				normal_prio;
unsigned int			rt_priority;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SZ2DfI26-1662568077252)(image/2-14.png)]

进程系统调用

当运行应用程序的时候,调用fork()/vfork)/clone()函数就是系统调用。 系统调用就是应用程序如何进入内核空间执行任务,程序使用系统调用执行一系列的操作: 比如创建进程、文件IO等等。

讨论fork和exec系列系统调用的实现。通常这些调用不是由应用程序直接发出的,而是通过一个中间层调用,即负责与内核通信的C标准库。从用户状态切换到核心态的方法,依不同的体系结构而各有不同。

进程复制

传统的UNIX中用于复制进程的系统调用是fork。但它并不是Linux为此实现的唯一调用,实际上Linux实现了3个。
(1) fork是重量级调用,因为它建立了父进程的一个完整副本,然后作为子进程执行。为减少与该调用相关的工作量,Linux使用了写时复制(copy-on-write)技术。
(2) vfork类似于fork,但并不创建父进程数据的副本。相反,父子进程之间共享数据。这节省了大量CPU时间(如果一个进程操纵共享数据,则另一个会自动注意到)。
(3) clone产生线程,可以对父子进程之间的共享、复制进行精确控制。

【写时复制】

内核使用了写时复制(Copy-On-Write,COW)技术,以防止在fork执行时将父进程的所有数据复制到子进程。在调用fork时,内核通常对父进程的每个内存页,都为子进程创建一个相同的副本。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5cYaYmKT-1662568077252)(image/2-16.png)]

问题?如果主进程修改其中页z的数据?此时就会发生父子进程在内存分离。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u7t888ck-1662568077253)(image/2-17.png)]

​ 只有在不得不复制数据内容时才去复制数据内容,这是就是写时复制核心思想,可以看到因为修改页z导致子进程不得不去复制原页Z来保证父子进程互干扰。

​ 内核只为新生成的子进程创建虚拟空间结构,它们来复制于父进程的虚拟结构,但是不为这些段分配物理内存,它们共享父进程的物理空间,当父进程中有更改相应段的行为发生时,再为子进程相应段分配物理空间。

【执行系统调用】

fork、vfork和clone系统调用的入口点分别是sys_fork、sys_vfork和sys_clone函数。其定义依赖于具体的体系结构,因为在用户空间和内核空间之间传递参数的方法因体系结构而异。

【do_fork实现】

所有3个fork机制最终都调用kernel/fork.c中的do_fork(一个体系结构无关的函数),其代码流程如图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xgdQumqH-1662568077253)(image/2-15.png)]

内核线程

​ 内核线程是直接由内核本身启动的进程,与普通用户进程区别在于内核线程没有独立的进程地址空间,task _struct数据结构里面有一个成员指针mm设置为NULL,只能运行在内核空间。内核线程实际上是将内核函数委托给独立的进程,与系统中其他进程"并行"执行(实际上,也并行于内核自身的执行)。内核线程经常称之为(内核)守护进程。它们用于执行下列任务。

  • 周期性地将修改的内存页与页来源块设备同步(例如,使用mmap的文件映射) ;
  • 如果内存页很少使用,则写入交换区;
  • 管理延时动作 (deferred action) ;
  • 实现文件系统的事务日志。

Linux内核实现如下:

pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
	struct kernel_clone_args args = {
		.flags		= ((lower_32_bits(flags) | CLONE_VM |
				    CLONE_UNTRACED) & ~CSIGNAL),
		.exit_signal	= (lower_32_bits(flags) & CSIGNAL),
		.stack		= (unsigned long)fn,
		.stack_size	= (unsigned long)arg,
	};

	return _do_fork(&args);
}

退出进程

​ 进程主动终止:从main()函数返回,链接程序会自动添加到exit()系统调用;主动调用exit()系统函数。
​ 进程被动终止:进程收到一个自己不能处理的信号;进程收到SIGKILL等终止信息

Linux内核实现如下:

SYSCALL_DEFINE1(exit, int, error_code)
{
	do_exit((error_code&0xff)<<8);
}

到exit()系统调用;主动调用exit()系统函数。
​ 进程被动终止:进程收到一个自己不能处理的信号;进程收到SIGKILL等终止信息

Linux内核实现如下:

SYSCALL_DEFINE1(exit, int, error_code)
{
	do_exit((error_code&0xff)<<8);
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值