Linux进程管理(一)进程的创建与销毁

在进程的创建上, Unix采取了一种有趣和少见的处理方法:它将进程的创建和加载一个新二进制镜像分离。Unix提供了两个系统调用fork和exec。

创建进程:

缺省情况下,内核将进程ID的最大值限制为32768,2^15。系统管理员可以设置/proc/sys/kernel/pid_max的值来突破这个缺省的限制,但会牺牲一些兼容性。

创建新进程的那个进程称为父进程,而新进程被称为子进程。每个进程都是由其他进程创建的(除了init进程),因此每个子进程都有一个父进程。这种关系保存在每个进程的父进程ID号(ppid)中。每个进程都被一个用户和组所拥有。这种从属关系是用来实现访问控制的。每个进程都是某个进程组的一部分,它简单的表明了自己和其他进程之间的关系,但是不要和上面的用户、组的概念混淆了。子进程通常属于其父进程所在的那个进程组。进程组使得与管道相关的进程间发送和获取信息变得很容易,这同样也适用于管道中的子进程。从用户的角度来看,进程组更像是一个任务。

在Unix中,载入内存并执行程序映像的操作与创建一个新进程的操作是分离的。Unix有一个系统调用(实际上是一系列系统调用之一)是可以将二进制文件的程序映像载入内存,替换原先进程的地址空间,并开始运行它。这个过程称为运行一个新的程序,而相应的系统调用称为exec系统调用。同时,另一个不同的系统调用是创建一个新的进程,它基本上就是复制父进程。通常情况下新的进程会立刻执行一个新的程序。完成创建新进程的这种行为叫做派生(fork),完成这个功能的系统调用就是fork()。

int execl (const char path, const char arg, ...);
execl()成功的调用不仅仅改变了地址空间和进程的映像,还改变了进程的一些属性:
1,任何挂起的信号都会丢失。
2,捕捉的任何信号会还原为缺省的处理方式,因为信号处理函数已经不存在于地址空间中了。
3,任何内存的锁定(参看第八章)会丢失。
4,多数线程的属性会还原到缺省值。
5,多数关于进程的统计信息会复位。
6,与进程内存相关的任何数据都会丢失,包括映射的文件。
7,包括C语言库的一些特性(例如atexit())等独立存在于用户空间的数据都会丢失。
然而也有很多进程的属性没有改变,例如pid、父进程的pid、优先级、所属的用户和组。
其他五个系统调用:execlp,execle,execv,execvp,execve。字 母 l 和 v 分别表示参数是以列表方式或者数组 (向量)方式提供的。字 母p意味着在用户的PATH环境变量中寻找可执行文件。只要出现在用户的路径中,带p的exec函数可以简单的只提供文件名。最后e表示会提供给新进程以新的环境变量。

pid_t fork (void);
成功的fork()调用会返回0。在父进程中fork()返回子进程的pid。除了必要的一些方面,父进程和子进程之间在每个方面都非常相近:
1,显然子进程的pid是新分配的,它是与父进程不同的。
2,子进程的ppid会设置为父进程的pid。
3,子进程中的资源统计信息(Resource statistics)会清零。
4,任何挂起的信号会清除,也不会被子进程继承(参看第九章)。
5,任何文件锁都不会被子进程所继承。

写时复制是一种采取了惰性优化方式来避免复制时的系统开销。它的前提很简单:如果有多个进程要读取它们自己的那部分资源的副本,那么复制是不必要的。每个进程只要保存一个指向这个资源的指针就可以了。只要没有进程要去修改自己的”副本”,就存在着这样的幻觉:每个进程好像独占那个资源。从而就避免了复制带来的负担。如果一个进程要修改自己的那份资源“副本”,那么就会复制那份资源,并把复制的那份提供给进程。不过其中的复制对进程来说是透明的。这个进程就可以修改复制后的资源了,同时其他的进程仍然共享那份没有修改过的资源。
写时复制在内核中的实现非常简单。与内核页相关的数据结构可以被标记为只读和写时复制。如果有进程试图修改一个页,就会产生一个缺页中断。内核处理缺页中断处理的方式就是对该页进行一次透明复制。这时会清除页面的COW 属性,表示着它不再被共享。

在实现写时复制之前, Unix 的设计者们就一直很关注在fork 后立刻执行exec所造成的地址空间的浪费。vfork()会挂起父进程直到子进程终止或者运行了一个新的可执行文件的映像。通过这种方式,vfork()避免了地址空间的按页复制。实际上vfork()只完成了一件事:复制内部的内核数据结构。因此,子进程也就不能修改地址空间中的任何内存。

终止进程
进程成功的退出时,只需要简单的写上:exit(EXIT_SUCCESS);
在终止进程之前,C语言函数执行以下关闭进程的工作:
1,以在系统中注册的逆序来调用由atexit()或on_exit()注册的函数。
2,空所有已打开的标准I/O流。
3,删除由tmpfile()创建的所有临时文件。
这些步骤完成了在用户空间中所需要做的事情,这样exit()就可以调用_exit()来让内核来处理终止进程的剩余工作了。
内核会清理进程所创建的、不再用到的任何资源。这包括:申请的内存、打开的文件和SystemV的信号量。清理完成后,内核摧毁进程,并告知父进程其子进程的终止。
C 语言中在main()函数返回时,编译器会简单的在最后的代码中插入一个_exit()。 shell会根据这个返回值来判断命令是否成功的执行,在 main()函数返回时明确给出一个状态值 , 或者调用exit() , 这是一个良好的编程习惯。

atexit()的成功调用会把指定的函数注册(无参数的,无返回值)到进程正常结束(例如一个进程以调用exit()或者从main()中返回的方式终止自己)时调用的函数中。如果进程调用了exec , 所注册的函数列表会被清除(因为这些函数不存在于新进程的地址空间中)。如果进程是通过信号而结束的,这些注册的函数也不会被调用。
函数调用的顺序是和注册的顺序相反的。也就是这些函数存储在栈中,以后进先出的方式调用(LIFO) 。注册的函数不能调用exit() , 否则会引起无限的递归调用。

SunOS 4定义自己的一个和atexit()等价的函数on_exit(),这个函数的工作方式和atexit()—样,只是注册的函数原型不同。

当一个进程子进程终止时,内核会向其父进程发送SIGCHILD信号。缺省情况下会忽略此信号量,父进程也不会有任何的动作。进程也可通过signal()或sigaction()系统调用来有选择的处理这个信号。通常情况下,父进程都希望能更多的了解到子进程的终止,或者显式的等待子进程的终止。

用户和组
用户和组:Linux中通过用户和组进行认证,每个用户和唯一的正整数关联,称为用户ID(uid)。每一个进程与一系列用户ID关联:
真实uid(real uid):每一个进程与一个用户ID关联,用来识别运行这个进程的用户。用于辨识进程的真正所有者,且会影响到进程发送信号的权限。没有超级用户权限的进程仅在其RUID与目标进程的RUID相匹配时才能向目标进程发送信号,例如在父子进程间,子进程从父进程处继承了认证信息,使得父子进程间可以互相发送信号。
有效UID(effective uid):在创建与访问文件的时候发挥作用。具体来说,创建文件时,系统内核将根据创建文件的进程的EUID与EGID设定文件的所有者/组属性,而在访问文件时,内核亦根据访问进程的EUID与EGID决定其能否访问文件。
保留uid(saved uid):于以提升权限运行的进程暂时需要做一些不需特权的操作时使用,这种情况下进程会暂时将自己的有效用户ID从特权用户(常为root) 对应的UID变为某个非特权用户对应的UID,而后将原有的特权用户UID复制为SUID暂存;之后当进程完成不需特权的操作后,进程使用SUID的值重 置EUID以重新获得特权。在这里需要说明的是,无特权进程的EUID值只能设为与RUID、SUID与EUID(也即不改变)之一相同的值。
文件系统uid(filesystem uid):在Linux中使用,且只用于对文件系统的访问权限控制,在没有明确设定的情况下与EUID相同(若FSUID为root的UID,则SUID、RUID与EUID必至少有一亦为root的UID),且EUID改变也会影响到FSUID。设立FSUID是为了允许程序(如NFS服务器)在不需获取向给定UID账户发送信号的情况下以给定UID的权限来限定自己的文件系统权限。

会话和进程组
每个进程都属于某个进程组。进程组是由一个或多个相互间有关联的进程组成的,它的目的是为了进行作业控制。进程组的主要特征就是信号可以发送给进程组中的所有进程:这个信号可以使同一个进程组中的所有进程终止、停止或者继续运行。每个进程组都由进程组ID(pgid)唯一的标识,并且有一个组长进程。进程组ID就是组长进程的pid。只要在某个进程组中还有一个进程存在,则该进程组就存在。即使组长进程终止了,该进程组依然存在。
当有新的用户登陆计算机,登陆进程就会为这个用户创建一个新的会话。这个会话中只有用户的登陆shell—个进程。登陆shell做为会话首进程(session leader)。会话首进程的pid就被作为会话的ID。一个会话就是一个或多个进程组的集合。会话囊括了登陆用户的所有活动,并且分配给用户一个控制终端(controling terminal)。控制终端是一个用于处理用户I/O的tty设备。因此,会话的功能和shell差不多。没有谁刻意去区分它们。
会话中的进程组分为一个前台进程组和零个或多个后台进程组。当用户退出终端时,向前台进程组中的所有进程发送SIGQUIT信号。当出现网络中断的情况时,向前台进程组中的所有进程发送SIGHUP信号。当 用户敲入了终止键(一般是Ctrl+C) ,向前台进程组中的所有进程发送SIGINT信号。

相关系统调用:setsid,getsid,setpgid,getpgid。

特殊进程:
Init 进程
Idle进程
空闲进程,当没有其他进程在运行时,内核所运行的进程—它的pid是0。
init进程,在启动后,内核运行的第一个进程称为init进程,它的pid是1。除非用户显式地指定内核所要运行的程序(通过内核启动的init参数),否则内核依次寻找一个init程序,第一被发现的就会当做init运行。如果所有的都失败了,内核就会发出panic,挂起系统。在内核交出控制后,init会接着完成后续的启动过程。典型的情况是init会初始化系统,启动各种服务和启动登陆进程。

Orphan Process孤儿进程

Zombie Process僵尸进程
等待终止的子进程
用信号通知父进程是可以的,但是很多的父进程想知道关于子进程终止的更多信息——例如子进程的返回值。如果在终止过程中,子进程完全消失了,就没有给父进程留下任何可以来了解子进程的东西。Unix的设计者们做出了这样的决定:如果一个子进程在父进程之前结束,内核应该把子进程设置为一个特殊的状态。处于这种状态的进程叫做僵死(zombie)进程。进程只保留最小的概要信息一一些保存着有用信息的内核数据结构(进程号,退出状态,运行时间等)。僵死的进程等待这父进程来查询自己的信息(这叫做在僵死进程上等待)。只要父进程获取了子进程的信息,子进程就会消失,否则一直保持僵死状态。
为避免僵死进程,如进程可以显示等待子进程结束、处理或者忽略SIGCHLD信号。

pid_t wait (int status);
pid_t waitpid (pid_t pid, int
status, int options);
int waitid (idtype_t idtype, id_t id, siginfo_t *infop, int options);
wait()返回已终止子进程的pid , 或者返回-1表示出错。如果没有子进程终止,调用者会被阻塞,直到一个子进程终止。

int system (const char *command);
ANSI和 POSIX 都定义了一个用于创建新进程并等待它结束的函数—— 可以把它想象成是同步的创建进程。

只要有进程结束了,内核就会遍历它的所有子进程,并且把它们的父进程重新设为init进程(pid为1的那个进程)。这保证了系统中不存在没有父进程的进程。init进程会周期性的等待所有子进程,确保不会有长时间存在的僵死进程。

Daemon Process守护进程
守护进程运行在后台,不与任何控制终端相关联。守护进程通常在系统启动时就运行,它们以root用户运行或者其他特殊的用户(例如apache和postfix),并处理一些系统级的任务。习惯上守护进程的名字通常以d结尾(就像crond和sshd),但这不是必须的,甚至不是通用的。对于守护进程有两个基本要求:它必须是init进程的子进程,并且不与任何控制终端相关联。

一般来讲,进程可以通过以下步骤成为守护进程:
1,调用fork(),创建新的进程,它会是将来的守护进程。
2,在守护进程的父进程中调用exit()。这保证了守护进程的祖父进程确认父进程已经结束。还保证了父进程不再继续运行,守护进程不是组长进程。最后一点是顺利完成以下步骤的前提。
3,调用setsid(),使得守护进程有一个新的进程组和新的会话,两者都把它作为首进程。这也保证它不会与控制终端相关联(因为进程刚刚创建了新的会话,同时也就不会为其关联一个控制终端)。
4,用chdir()将当前工作目录改为根目录。因为前面调用fork()创建了新进程,它所继承来的当前工作目录可能在文件系统中任何地方。而守护进程通常在系统启动时运行,同时不希望一些随机目录保持打开状态,也就阻止了管理员卸载守护进程工作目录所在的那个文件系统。
5,关闭所有的文件描述符。不需要继承任何打开的文件描述符,对于无法确认的文件描述符,让它们继续处于打开状态。
6,打开0、1和2号文件描述符(标准输入、标准输出和标准错误),把它们重定向到/dev/null。

原文地址:http://blog.51cto.com/13376824/2064104

时间: 2024-08-03 15:23:39

Linux进程管理(一)进程的创建与销毁的相关文章

Python学习第六天----Linux内存管理、进程管理、RPM包安装管理及源码安装软件

Linux内存管理.进程管理.RPM包安装管理及源码安装软件 一.交换分区     交换分区其实就相当于Windows系统下的虚拟内存的概念,当物理内存不够用的时候,由操作系统将硬盘的一块区域划分出来作为内存使用.具体使用方法如下:      [[email protected] ~]# fdisk -l 磁盘 /dev/sdb:16.1 GB, 16106127360 字节,31457280 个扇区 Units = 扇区 of 1 * 512 = 512 bytes 扇区大小(逻辑/物理):5

linux进程管理(5)---进程消亡

一.目的 本文将讲述进程是如何消亡的.一个进程既有父进程又有子进程,因此进程消亡时,既要通知父进程,也要安排好子进程. 当前进程消亡时主要做了三件大事:释放当前进程占用的资源:为当前进程的子进程重新寻找"养父":通知当前进程的父进程,释放当前进程剩下的资源. 当前进程释放掉大多数进程资源后,只保留内核栈.structtask_struct数据结构:剩下的资源由父进程负责释放. linux调用sys_exit().sys_wait4()实现进程的消亡,代码可以在kernel/exit.c

Linux 内核进程管理之进程ID

Linux 内核进程管理之进程IDLinux 内核使用 task_struct 数据结构来关联所有与进程有关的数据和结构,Linux 内核所有涉及到进程和程序的所有算法都是围绕该数据结构建立的,是内核中最重要的数据结构之一.该数据结构在内核文件 include/linux/sched.h 中定义,在Linux 3.8 的内核中,该数据结构足足有 380 行之多,在这里我不可能逐项去描述其表示的含义,本篇文章只关注该数据结构如何来组织和管理进程ID的. 进程ID类型要想了解内核如何来组织和管理进程

Linux 内核进程管理之进程ID 。图解

http://www.cnblogs.com/hazir/tag/kernel/ Linux 内核进程管理之进程ID Linux 内核使用 task_struct 数据结构来关联所有与进程有关的数据和结构,Linux 内核所有涉及到进程和程序的所有算法都是围绕该数据结构建立的,是内核中最重要的数据结构之一.该数据结构在内核文件 include/linux/sched.h 中定义,在Linux 3.8 的内核中,该数据结构足足有 380 行之多,在这里我不可能逐项去描述其表示的含义,本篇文章只关注

进程管理(六)-进程的描述

进程管理(六)-进程的描述 进程控制块:进程的PCB 是系统感知进程的唯一实体 程序段:描述进程所要完成的功能 程序操作的数据集:程序在执行时必不可少的工作区和操作对象 PCB PCB是什么 PCB包含有关进程的描述信息.控制信息以及资源信息,是进程动态特征的集中反映. PCB作用 1.系统根据PCB感知进程的存在和通过PCB中所包含的各项变量的变化,掌握进程所处的状态以达到控制进程活动的目的. 2.一个进程的PCB结构都是全部或部分常驻内存的. PCB的生命周期 在创建一个进程时,首先创建其

进程管理(十)-进程通信

进程管理(十)-进程通信 什么是进程通信 通信(communication)意味着在进程间传送数据. 低级通信VS高级通信 控制信息的传送.进程间控制信息的交换称为低级通信. 大批量数据传送.进程间大批量数据的交换称为高级通信. 进程的通信的四种方式 主从式 特点: 1.主进程可自由地使用从进程的资源或数据 2.从进程的动作受主进程的控制 3.主进程和从进程的关系是固定的 例子:主从式通信系统的典型例子是终端控制进程和终端进程 会话式 会话方式中,通信进程双方可分别称为使用进程和服务进程.其中,

进程管理(九)-进程互斥

进程管理(九)-进程互斥 临界区 什么是临界区 不允许多个并发进程交叉执行的一段程序称为临界部分(critical section )或临界区(critical region) 临界区不可能用增加硬件的方法来解决.因此,临界区也可以被称为访问公用数据的那段程序. 为什么要设置临界区 防止进程并发执行的时候,出现冲突,或者由于两个并发进程处于竞争状态,进而导致两者执行顺序不固定,导致程序执行出错. 例子 设计算进程PA,PB共享内存MS. MS分为系统区.进程工作区和数据区.数据区划分成大小相等的

Linux进程管理及进程管理工具的使用

Linux的进程管理 进程:简单来说就是一个运行中的程序的副本,是进程的一个实例,它是有生命周期的 进程的创建:进程都是由其父进程创建的,最开始的进程就是init. 进程优先级:0-139 实时优先级:1-99: 静态优先级:100-139,对于静态来说数字越小优先级越高, 优先级还有一个Nice值,值为-20(对应100),19(对应19),可以通过调整Nice值来调整优先级. 进程分为守护进程和前台进程,守护进程就是在系统引导过程中启动的进程,跟终端无关的进程:前台进程跟终端有关,通过终端启

【进程管理】进程(线程)创建

本节主要研究进程(线程)创建的过程,下文将不区分进程和线程: 基本知识 在linux系统中,第一个进程是系统固有的,是由内核的设计者安排好的:一个新的进程一定要由一个已存在的进程复制出来,而不是创造出来的,其实linux系统并不提供直接创建进 程的方法:创建了子进程以后,父进程可以继续走自己的路,与子进程分道扬镳,但是如果子进程先行exit(),那么将要向父进程发一个信号:父进程也可以选择睡眠,等子进程 exit()以后再去世,然后父进程再继续执行,可使用wait3()某个特定的子进程,wait

linux下的进程管理(进程的基本了解及查看pstree,ps,pgrep命令)

Linux下的进程管理 1.什么是进程?程序是静态的文件进程是运行中的程序的一个副本进程存在生命周期(准备期,运行期,终止期)2.进程状态 状态 定义 R(TASK_RUNNING) . 可执行状态(RUNNING,READY)running:正在被处理 ready: 在排队, cpu处理进程个数有限 S(TASK_INTERRUPTIBLE) 可唤醒状态 :在cpu上使用的时间超时,此时被打入休眠状态,随着程序的调用会被唤醒 D(TASK_UNINTERRUPTIBLE) 不可唤醒状态 T(T