多个用户进程同时进行

我们以三个用户进程(str1、str2、str3)为例,来看看多个进程是如何运行的,他们又是如何切换的。

进程的源代码如下,str1、str2、str3三者代码一样。

#include <stdio.h>

int foo(int n)
{
	char text[2048];
	if(n==0)
		return 0;
	else
	{
		int i = 0;
		for(i; i<2048; i++)
			text[i] = ‘\0‘;
		sleep(5);
		foo(n - 1);
	}
}

int main(int argc, char **argc)
{
	foo(6);
	return 0;
}

1、假设现在创建(fork)了三个进程,并执行(execve)对应的程序,他们的进程号是5、6、7,它们的线性地址空间的位置应该依次是4*64 ~ 5*64MB,5*64 ~ 6*64MB,6*64 ~ 7*64MB。假设三个进程此时都处于就绪态,也就是如下图:

图  1

2、假设现在轮到str1进程执行。str1开始执行foo函数调用,就需要压栈(这是从汇编的角度看的,局部变量需要压栈),于是产生了缺页中断。在缺页中断中,内核为str1进程申请了空闲物理页面,并将其映射到str1进程的线性地址空间。之后进程再对text数组进行设置,内容就被写在了刚分配的物理页面上了。执行效果如下图:

图  2

3、str1在执行过程中,无轮在0特权级还是3特权级,每10毫秒就会产生一次时钟中断,会调用do_timer,这样就会消减它的时间片,直到消减到0,如果此时进程处于3特权级,就执行schedule。

do_timer如下:

void do_timer(long cpl)
{
	......
	if ((--current->counter)>0) return;//判断时间片是否削减为0
	current->counter=0;
	if (!cpl) return;//只有在3特权级下才能切换,0特权级下不能切换
	schedule();
}

我们假设切换到str2进程,进程str2也执行同样逻辑的程序,值得注意的是,当设置text数组时,屏幕打印的逻辑地址与当时进程str1的地址相同。但他们的线程地址不同,物理内存中进程str2也并没有于str1重叠。如下图:

图  3

4、str2无轮在0特权级还是3特权级,每10毫秒就会产生一次时钟中断,会调用do_timer,这样就会消减它的时间片,直到消减到0,如果此时进程处于3特权级,就执行schedule,切换到str3执行,它也要压栈,str3开始执行,执行的代码于进程str2相同,也是压栈,并设置text。str3程序执行压栈的效果如下:

图  4

5、str3执行一段时间后,时间片也用完了。这样三个用户进程虽然还需要继续执行,但时间片都用完了。当再发生时钟中断时,do_timer()函数调用schedule()函数进行进程切换,这时,内核会为他们重新分配时间片。

内核从task[]的末端开始重新给当前系统的所有进程(包括处于睡眠的进程,但进程0除外)分配时间片,时间片的大小为couter/2 + priority。priority是进程的优先级,所以进程的优先级越高,分配到的时间片就越多。然后根据此时时间片的情况重新选择进程运行,如此反复。

执行的代码如下:

void schedule(void)
{
	.....
/* this is the scheduler proper: */

	while (1) {
		c = -1;
		next = 0;
		i = NR_TASKS;
		p = &task[NR_TASKS];
		while (--i) {
			if (!*--p)
				continue;
			if ((*p)->state == TASK_RUNNING && (*p)->counter > c)
				c = (*p)->counter, next = i;
		}
		if (c) break;
		for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
			if (*p)
				(*p)->counter = ((*p)->counter >> 1) +
						(*p)->priority;
	}
	switch_to(next);
}

6、接着他们都会继续不断地压栈,这三个用户进程在线性地址空间内压入他们各自的栈中的数据都是连续的,但是在物理空间内压栈的数据却是完全”交错“分布的。

三个程序执行一段时间后,压入他们各自的栈中的数据在主内存中的分布如下图:

图 5

时间: 2024-10-09 20:12:25

多个用户进程同时进行的相关文章

Linux内存点滴:用户进程内存空间

原文出处:PerfGeeks 经常使用top命令了解进程信息,其中包括内存方面的信息.命令top帮助文档是这么解释各个字段的.VIRT , Virtual Image (kb)RES, Resident size (kb)SHR, Shared Mem size (kb)%MEM, Memory usage(kb)SWAP, Swapped size (kb)CODE, Code size (kb)DATA, Data+Stack size (kb)nFLT, Page Fault countn

Android之——系统进程与用户进程分离

转载请注明出处:http://blog.csdn.net/l1028386804/article/details/47282031 在上一篇博文<Android之--杀死用户选中的进程优化>一文中,我向大家介绍了如何优化用户的体验,那么这篇博文中,我将向大家介绍如何进行系统进程与用户进程的分离操作.同样,这篇博文是基于上一篇博文改进的.如果大家还没有阅读上一篇博文,请大家先阅读上一篇博文<Android之--杀死用户选中的进程优化>一文.好了,咱们直接进入主题吧. 一.原理 老规矩

【UNIX】内核对内核空间内存的管理以及对用户进程的堆内存分布

常用的内核内存分配函数 1)_get_free_pages是最原始的内存分配方式,至二级从伙伴系统中获取原始页框,返回值为第一个页框的起始地址._get_free_pages在实现上只是封装了alloc_pages函数,而alloc_pages分配的长度为1< 2)Kmem_cache_alloc是基于slab分配器的一种内存分配方式,适用于反复分配释放同一大小内存的场合.首先用Kmem_cache_alloc从该高速缓存区域中获取新的内存块.在2.6内核中一次最多能申请1<< 5*4K

内核创建的用户进程printf不能输出一问的研究

转载:http://blog.chinaunix.net/uid-20543183-id-1930830.html 一:前言 上个星期同事无意间说起,在用核中创建的用户空间进程中,使用printf不能显示的问题.这个问题我当时一时半会没有解释清楚.现在就从linux kernel的源代码的角度来分析该问题的原因所在. 二:fork()与execve()中stderr,stdio.stdout的继承关系 其实用继承这个词好像不太准确,要准确一点,可能复制更适合. 首先有二点: 1:父进程fork出

系统进程和用户进程通信

我的用户进程需要和我的服务进程通信,通过文件映射.事件的方式通信时需要注意: 1.需要低权限 var SecMem: SECURITY_ATTRIBUTES; aSD: SECURITY_DESCRIPTOR;begin FExistsAlready := False; InitializeSecurityDescriptor(@aSD, SECURITY_DESCRIPTOR_REVISION); SetSecurityDescriptorDacl(@aSD, True, nil, False

修改用户进程可打开文件数限制(转)

1.修改用户进程可打开文件数限制 在Linux平台上,无论编写客户端程序还是服务端程序,在进行高并发TCP连接处理时,最高的并发数量都要受到系统对用户单一进程同时可打开文件数量 的限制(这是因为系统为每个TCP连接都要创建一个socket句柄,每个socket句柄同时也是一个文件句柄).可使用ulimit命令查看系统允许 当前用户进程打开的文件数限制: [[email protected] ~]$ ulimit -n 1024 这表示当前用户的每个进程最多允许同时打开1024个文件,这1024个

[Linux] 关于Centos6中ulimit nproc用户进程数的限制

一.缘由: 在启动mongodb的时候,有Warning提示soft rlimits too low,就是用户使用进程数过小,遂调高系统资源关于用户最大进程数的限制ulimit -u. 先暂时使设置生效,ulimit -u 102400,提示: ulimit: max user processes: cannot modify limit: Operation not permitted. 修改/etc/security/limits.conf 为 * soft nproc 102400,退出s

linux用户进程分析

       经过实验3的介绍,我们需要来点实在的,所以将我们理解的流程用于linux系统的分析.换句话说,通过类比的方式去进行描述与理解linux相关的部分.本节的内容很详实,而且也分析了很久,所以长话短说,静静的去感受与理解linux内核代码的实现.当然,我们实验的系统代码很简单而且直接,但是linux内核经过20多年的发展,更有成千上万的开发者共同维护,所以对于代码的书写会更加精练,对于基础相对薄弱的程序员去理解有一些障碍,但是反过来说,更有利于我们语言知识的提高. 如下的介绍方法是对

Kernel与用户进程通信

测试IPv6 ready logo   rfc 3315的时候,遇到一个问题,要求在收到ICMPv6 RA的时候,DHCPv6  Client要发Solicit消息.在平常的应用中,都是启动DHCPv6  Client后,client每隔一段时间就会发送Solicit,而并不是要在收到RA的时候发送.于是就修改了下kernel和client的代码,使得在kernel收到RA消息时,发送消息给DHCPv6  Client,使client发送Solicit. 思路为:使用netlink_kernel