进程(一)

进程是程序执行时的一个实例。从内核观点看,进程的目的就是担当分配系统资源(CPU时间、内存等)的实体

进程描述符

为了管理进程,内核定义进程描述符(process descriptor)用于描述进程的各种属性。进程描述符都是task_struct类型结构。

在linux内核中task_struct拥有上百的成员变量,是一个相当复杂的结构,它不仅包含了很多进程属性的字段,而且一些字段还包括了指向其他数据结构的指针。下面的代码仅仅列举几个成员:

struct task_struct{
	volatile long state;	/* -1 unrunnable, 0 runnable, >0 stopped */
	void *stack;
	atomic_t usage;
	unsigned int flags;	/* per process flags, defined below */
	unsigned int ptrace;
	...
}

进程状态

进程描述符中的state字段描述了进程当前所处的状态。它由一组标志组成,其中每个标志描述描述一种可能的进程状态。

可运行状态(TASK_RUNNING)

进程要么在CPU上执行,要么准备执行

可中断的等待状态(TASK_INTERRUPTIBLE)

进程被挂起(睡眠),直到某个条件变为真。产生一个硬件中断,释放进程正等待的系统资源,或传递一个信号都是可以唤醒进程的条件(把进程的状态放回到TASK_RUNNING)

不可中断的等待状态(TASK_UNINTERRUPTIBLE)

与可中断的等待状态类似,但有一个例外,把信号传递睡眠进程不能改变它的状态。这种状态很少用到,但在一些特定的情况下,这种状态是很有用的。例如,当进程打开一个设备文件,其相应的设备驱动程序开始探测相应的硬件设备时会用到这种状态。探测完成以前,设备驱动程序不能被中断,否则,硬件设备会处于不可预知的状态。

暂停状态(TASK_STOPPED)

进程的执行被暂停。当进程接收到SIGSTOP、SIGSTP、SIGTTIN或SIGTTOU信号后,进入暂停状态。

跟踪状态(TASK_TRACED)

进程的执行已由debugger程序暂停。当一个进程被另一个进程监控时,任何信号都可以把这个进程置于TASK_TRACED状态。

另外有两个进程状态是既可以存放在进程描述符的state字段中,也可以存放在exit_state字段中。

僵死状态(EXIT_ZOMBIE)

进程的执行被终止,但是,父进程还没有发布wait4()或waitpid()系统调用来返回有关死亡进程的信息。发布wait类系统调用前,内核不能丢弃包含在死进程描述符中的数据,因为父进程可能还需要它。

僵死撤销状态(EXIT_DEAD)

最终状态:由于父进程刚发出wait4()或waitpid()系统调用,因而进程由系统删除。

内核使用set_task_state和set_current_state宏来分别设置指定进程的状态和当前执行进程的状态。

#define mb()	asm volatile ("": : :"memory")
#define set_mb(var, value)  do { var = value;  mb(); } while (0)

#define __set_task_state(tsk, state_value)			do { (tsk)->state = (state_value); } while (0)
#define set_task_state(tsk, state_value)			set_mb((tsk)->state, (state_value))

/*
 * set_current_state() includes a barrier so that the write of current->state
 * is correctly serialised wrt the caller‘s subsequent test of whether to
 * actually sleep:
 *
 *	set_current_state(TASK_UNINTERRUPTIBLE);
 *	if (do_i_need_to_sleep())
 *		schedule();
 *
 * If the caller does not need such serialisation then use __set_current_state()
 */
#define __set_current_state(state_value)				do { current->state = (state_value); } while (0)
#define set_current_state(state_value)			set_mb(current->state, (state_value))

正如注释描述的那样,set_task_state、set_current_state含有内存屏障,它们是严格执行串行化指令,编译器不会进行任何优化

__asm__用于指示编译器在此插入汇编语句,__volatile__用于告诉编译器,严禁将此处的汇编语句与其它的语句重组合优化。

memory 强制gcc 编译器假设RAM 所有内存单元均被汇编指令修改,这样cpu 中的registers 和cache 中已缓存的内存单元中的数据将作废。cpu 将不得不在需要的时候重新读取内存中的数据。这就阻止了cpu 又将registers,cache 中的数据用于去优化指令,而避免去访问内存。

一般来说,能被独立调度的每个执行上下文都必须拥有它自己的进程描述符;因此,即使共享内核大部分数据结构的轻量级进程,也有它们自己的task_struct结构。

一个线程组中的所有线程使用和该线程组的领头线程(thread group leader)相同的PID,也就是该组中第一个轻量级进程的PID,它被存入进程描述符的tgid字段中。getpid()系统调用返回当前进程的tgid值而不是pid的值,因此,一个多线程应用的所有线程共享相同的PID。

对每个进程来说,Linux都把两个不同的数据结构紧凑地放在一个单独为进程分配的存储区域:一个是内核的进程堆栈,另一个是紧挨着进程描述符的小数据结构thread_info,叫做线程描述符,这块存储区的大小通常为8192个字节(两个页框)。考虑到效率的因素,内核让这8K空间占据连续的两个页框并让第一个页框的起始地址是2^13的倍数。

C语言使用下列的联合结构方便地表示一个进程的线程描述符和内核栈:

union thread_union {
	struct thread_info thread_info;
	unsigned long stack[THREAD_SIZE/sizeof(long)];
};

进程间的关系

程序创建的进程具有父子关系。如果进程创建多个子进程时,则子进程具有兄弟关系。在进程描述符中引入几个字段来表示这些关系。

real_parent   指向创建了P的进程描述符,如果P的父进程不再存在,就指向进程1(init)的描述符(因此,如果用户运行一个后台进程而且推出了                   shell,后台进程就会成为init的子进程)

parent        指向P的当前父进程(这种进程的子进程终止时,必须向父进程发信号)。它的值通常与real_parent一致。

children      链表的头部,链表中的所有元素都是P创建的子进程

sibling       指向兄弟进程链表中的下一个元素或前一个元素的指针,这些兄弟进程的父进程都是P

group_leader  P所在进程组的领头进程的描述符指针

tgid          P所在线程组的领头进程的PID

ptraced       该链表包含所有被debugger程序跟踪的P的子进程

ptrace_entry  指向所跟踪进程其实际父进程链表的前一个或者下一个元素(用于P被跟踪的时候)

/include/linux/sched.h

struct task_struct *real_parent; /* real parent process */
	struct task_struct *parent; /* recipient of SIGCHLD, wait4() reports */
	/*
	 * children/sibling forms the list of my natural children
	 */
	struct list_head children;	/* list of my children */
	struct list_head sibling;	/* linkage in my parent‘s children list */
	struct task_struct *group_leader;	/* threadgroup leader */

	/*
	 * ptraced is the list of tasks this task is using ptrace on.
	 * This includes both natural children and PTRACE_ATTACH targets.
	 * p->ptrace_entry is p‘s link on the p->parent->ptraced list.
	 */
	struct list_head ptraced;
	struct list_head ptrace_entry;

等待队列

等待队列在内核中有很多用途,尤其用在中断处理、进程同步及定时。如,等待一个磁盘操作的终止,等待释放系统资源,或等待时间经过固定的时间间隔。等待队列实现了在事件上的条件等待:希望等待特定事件的进程把自己放在合适的等待队列,并放弃控制权。因此,等待队列代表一组睡眠的进程,当某条件变为真时,由内核唤醒它们。

struct __wait_queue {
	unsigned int flags;
#define WQ_FLAG_EXCLUSIVE	0x01
	struct task_struct * task;
	wait_queue_func_t func;
	struct list_head task_list;
};

struct __wait_queue_head {
	spinlock_t lock;
	struct list_head task_list;
};

因为等待队列是由中断处理程序和主要内核函数修改的,因此必须对其双向链表进行保护,以免对其访问的同时会导致不可预测的后果。同步时通过等待队列头中的lock自旋锁达到的。

等待队列链表中的每个元素代表一个睡眠进程。该进程等待某一事件的发生;然而,要唤醒等待队列中所有睡眠的进程有时并不方便。例如,如果两个或多个进程在等待互斥访问某一要释放的资源,仅唤醒等待队列的一个进程才有意义。这个进程占有资源,而其他进程继续睡眠。因此,有两种睡眠进程:互斥进程(等待队列元素的flags字段为1)由内核有选择的唤醒,而非互斥进程(flags值为0)总是由内核在事件发生时唤醒。

要等待特定条件的进程可以调用如下列表中的任何一个函数

1、sleep_on()该函数把当前进程的状态设置为TASK_UNINTERRUPTIBLE,并把它插入到特定的等待队列。然后调用调度程序,而调度程序重新开始另外一个程序的执行。当睡眠进程被唤醒时,调度程序重新开始执行sleep_on()函数,把该进程从等待队列中删除。

2、interruptible_sleep_on()与sleep_on()函数是一样的,但稍有不同,前者把当前进程状态设置为TASK_INTERRUPTIBLE而不是TASK_UNINTERRUPTIBLE,因此,接收一个信号可以唤醒当前进程。

3、sleep_on_timeout()和interruptible_sleep_on_timeout()与前面函数类似,但它允许调用者定义一个时间间隔,过了这个间隔以后,进程有内核唤醒。为了做到这一点,它们调用schedule_timeout()函数而不是schedule();

4、函数prepare_wait()和prepare_to_wait_exclusive()用传递的第三个参数进程的状态,然后把等待队列元素的互斥标志flag分别设置为0或1,最后,把等待元素wait插入到以wq为头的等待队列的链表中。进程一旦被唤醒就执行finish_wait()函数,它把进程的状态再次设置为TASK_RUNNING,并从等待队列中删除等待元素。

5、wait_event和wait_event_interruptible宏使它们的调用进程在等待队列上睡眠,一直到修改了给定条件为止。

sleep_on()函数在以下条件不能使用,那就是必须测试条件并且当条件还没有得到验证时又紧接着让进程去睡眠;此外,为了把一个互斥进程插入等待队列,内核必须用prepare_wait_exclusive()函数。所有其他相关函数把进程当作非互斥进程来插入。

进程(一)

时间: 2024-11-07 16:27:53

进程(一)的相关文章

C#:多进程开发,控制进程数量

正在c#程序优化时,如果多线程效果不佳的情况下,也会使用多进程的方案,如下: System.Threading.Tasks.Task task=System.Threading.Tasks.Task.Factory.StartNew( (object mystate) => { Process process = Process.Start("AutoCollectMrMultipleProcess.exe", mystate.ToString()); process.WaitF

C# 远程服务器 安装、卸载 Windows 服务,读取远程注册表,关闭杀掉远程进程

这里安装windows服务我们用sc命令,这里需要远程服务器IP,服务名称.显示名称.描述以及执行文件,安装后需要验证服务是否安装成功,验证方法可以直接调用ServiceController来查询服务,也可以通过远程注册表来查找服务的执行文件:那么卸载文件我们也就用SC命令了,卸载后需要检测是否卸载成功,修改显示名称和描述也用sc命令.至于停止和启动Windows服务我们可以用sc命令也可以用ServiceController的API,当停止失败的时候我们会强制杀掉远程进程,在卸载windows

【转】linux下杀死进程(kill)的N种方法

转载一篇,最原始的出处已不可考,望见谅! 常规篇: 首先,用ps查看进程,方法如下: $ ps -ef --smx       1822     1  0 11:38 ?        00:00:49 gnome-terminalsmx       1823  1822  0 11:38 ?        00:00:00 gnome-pty-helpersmx       1824  1822  0 11:38 pts/0    00:00:02 bashsmx       1827    

os -- 进程的控制

os -- 进程的控制 新建 模板 小书匠 参考 <计算机操作系统>(第四版) 汤小丹等编著 概念引入 进程控制 进程控制是最基本的功能,负责创建进程.结束进程等功能,一般由 OS 内核中的原语来实现 原语 所谓原语,就是由若干条指令组成的,用于完成一定功能的一个过程,算是不可分割的.最基本的操作 Note 接下来原语基本用于所有的操作 操作系统内核 操作系统将一些常用或者运行频率较高的模块(如时钟管理.进程调度等)常驻内存,这些就被称为 OS 内核 两大功能 支撑功能 中断处理 时钟管理 原

linux进程管理

进程管理 进程 Process 某应用程序打开的进程 PID Process ID 类型: 用户空间进程 内核空间进程 静态查看进程的状态 # ps [[email protected] ~]# ps >>>>查看本终端的进程 PID TTY          TIME CMD 4206 pts/0    00:00:00 bash 4378 pts/0    00:00:00 ps 选项的使用方式: BSD风格:选项没有横线- ps aux SysV风格:选项需要带有横线-  

linux基本命令整理(三):进程和vim

linux基本命令整理(三) -----------进程和vim 一.进程 1.查看进程 ps:将某个时间点的程序运行的状况截取下来 a:所有的进程 x:后台进程 u:有效的使用者相关的进程(常用组合aux) -IA:也能观察系统所有的数据 axjf:连同部分的程序树状态 -I:今查看和自己bash相关的程序 top:动态的观察进程的变化 -d:后面接描述,就是整个页面刷新的时间:默认是5秒 -b:以批次的方式执行top -n:与-b搭配使用,意义是需要进行几次top的输出结果 如:top -b

内存池、进程池、线程池

首先介绍一个概念"池化技术 ".池化技术 一言以蔽之就是:提前保存大量的资源,以备不时之需以及重复使用. 池化技术应用广泛,如内存池,线程池,连接池等等.内存池相关的内容,建议看看Apache.Nginx等开源web服务器的内存池实现. 起因:由于在实际应用当中,分配内存.创建进程.线程都会设计到一些系统调用,系统调用需要导致程序从用户态切换到内核态,是非常耗时的操作.           因此,当程序中需要频繁的进行内存申请释放,进程.线程创建销毁等操作时,通常会使用内存池.进程池.

Linux进程的睡眠和唤醒

1   Linux进程的睡眠和唤醒 在Linux中,仅等待CPU时间的进程称为就绪进程,它们被放置在一个运行队列中,一个就绪进程的状态标志位为TASK_RUNNING.一旦一个运行中的进程时间片用完, Linux内核的调度器会剥夺这个进程对CPU的控制权,并且从运行队列中选择一个合适的进程投入运行. 当然,一个进程也可以主动释放CPU的控制权.函数schedule()是一个调度函数,它可以被一个进程主动调用,从而调度其它进程占用CPU.一旦这个主动放弃CPU的进程被重新调度占用CPU,那么它将从

C#杀掉进程的方法

1 private static string CmdName = "cmd"; 2 /// <summary> 3 /// 关闭进程 4 /// </summary> 5 /// <param name="processName">进程名</param> 6 private void KillProcess(string processName) 7 { 8 Process[] myproc = Process.Ge

Linux进程管理与调度-之-目录导航【转】

转自:http://blog.csdn.net/gatieme/article/details/51456569 版权声明:本文为博主原创文章 && 转载请著名出处 @ http://blog.csdn.net/gatieme 目录(?)[-] 项目链接 进程的描述 进程的创建 进程的加载与运行 进程的退出 进程的调度 调度普通进程-完全公平调度器CFS 日期 内核版本 架构 作者 GitHub CSDN 2016-07-21 Linux-4.6 X86 & arm gatieme