linux进程的创建、执行和消亡

在linux系统中,第一个进程是系统固有的,与生俱来的或者说是由内核的设计者安排好了的,内核在引导并完成了基本的初始化以后,就有了系统第一进程(实际上是内核线程)。除此之外,所有其他的进程和内核线程都有这个原始进程或其子孙进程所创建,都是这个原始进程的后代。

linux将进程的创建和执行分成两步。

第一步是从已存在的“父进程”中像细胞分裂一样地复制出一个“子进程”。复制出来的子进程有自己的task_struct结构和系统空间堆栈,单与父进程共享其他所有的资源。例如要是父进程打开了五个文件,那么子进程也有五个打开的文件,而且这些文件的当前读写指针也停在相同的地方,所以这一步所做的是“复制”。为此,linux系统提供了两个系统调用。

1)fork():此调用是全部复制,父进程所有的资源全部通过数据结构的复制“遗传给”子进程,并且fork()调用为无参调用。

2)clone():此调用可以将资源有选择地复制给子进程,而没有复制的数据结构则可以通过指针的复制让子进程共享。极端情况下,一个进程可以通过clone()创建出一个线程。既然是有选择复制,clone()是有参调用。

有一个和fork()相似的调用vfork(),同样不带参数,但是除了task_struct和系统空间堆栈以外的资源全部通过数据结构指针的复制“遗传”。所以和clone()的极端情况一样,vfork出来的是线程而不是进程(注意分清在linux系统中进程和线程的区别:对于各个用户线程来说,有独自的task_struct和系统空间堆栈,但是都共享3G用户空间;对于各个用户进程来说,有独自的task_struct、系统空间堆栈以及3G用户空间)。

第二步是目标程序的执行,一般来说 ,一个进程的创建,是因为有不同的目标程序要让新的程序去执行()。所以创建出的子进程要与父进程“分道扬镳”,走自己的路,linux为此提供了一个系统调用execve(),使得一个进程可以执行以文件形式存在的可执行程序的映像。

创建了子进程以后,父进程有三个选择。第一是继续走自己的路,与子进程分道扬镳。但是如果子进程先于父进程“去世”,则由内核给父进程发送一个报丧的信号。第二是停下来,进入睡眠状态,等待子进程完成其使命至最终去世,然后再由父进程继续执行。系统提供了两个系统调用,wait4()和wait3()。两个系统调用基本相同,wait4()等待某个特定的子进程去世,wait3()则等待任何一个子进程去世。第三种选择是“自行退出历史舞台”,结束自己的生命。linux为此设置了一个系统调用exit()。第三种选择是第一种选择的特例。

下面程序演示进程的“生命周期”:

 1 #include<stdio.h>
 2
 3 int main()
 4 {
 5     int child;
 6     char *args[] = {"/bin/echo", "hello", "world!", NULL};
 7
 8     if(!(child = fork()))
 9     {
10         /*child*/
11         printf("pid %d: %d is my father\n", getpid(), getppid());
12         execve("/bin/echo", args, NULL);
13         printf("pid %d: I am back, something is wrong!\n", getpid());
14     }
15     else
16     {
17         int myself = getpid();
18         printf("pid %d: %d is my son\n", myself, child);
19         wait4(child, NULL, 0, NULL);
20         printf("pid %d: done\n", myself);
21     }
22     return 0;
23 }

这里,进入main()的进程为父进程,它在第8行执行了系统调用fork()创建了一个进程,也就是复制了一个子进程。子进程复制出来以后,就像其父进程一样地接受内核的调度,而且具有相同的返回地址。所以当父进程和子进程受调度继续运行而从内和空间返回时都返回到同一点上。一千多的大娘们只有一个进程执行,而从这一点却有两个进程在执行了。复制出来的进程全面的继承了父进程的所有资源和特性,但是还有一些区别。

第一,子进程有不同于父进程的进程号pid,而且子进程的task_struct中有几个字段说明谁是它的父亲。

第二,二者从fork()返回时所具有的返回值不一样。当子进程从fork()返回时,其返回值为0;而父进程从fork()返回时的返回值确是子进程的pid,这是不可能为0的。

在这程序程中,if语句吧父子进程区分开来。然后第10-12(没有13行,原因接下来说)行属于子进程,第16-19行属于父进程,各自执行各自的路线。在此进程中,我们选择让父进程停下来等待,所以父进程调用wait4();而子进程通过execve()执行“/bin/echo”。子进程在执行echo以后不会再执行第十三行,而是“壮士一去兮不复返”。这是因为在echo中必定有一个exit()调用,使子进程结束它的生命。对exit()的调用是每个可执行程序映像必有的,虽然我们在这个程序中并没有调用它,而是以return语句从main()返回,但是gcc在编译和连接时会自动加上,所以每个程序都逃不过这一关。

参考:

毛德操,胡希明《linux内核源代码情景分析》(上册)

时间: 2024-10-11 16:57:55

linux进程的创建、执行和消亡的相关文章

Linux进程的创建函数fork()及其fork内核实现解析

进程的创建之fork() Linux系统下,进程可以调用fork函数来创建新的进程.调用进程为父进程,被创建的进程为子进程. fork函数的接口定义如下: #include <unistd.h> pid_t fork(void); 与普通函数不同,fork函数会返回两次.一般说来,创建两个完全相同的进程并没有太多的价值.大部分情况下,父子进程会执行不同的代码分支.fork函数的返回值就成了区分父子进程的关键.fork函数向子进程返回0,并将子进程的进程ID返给父进程.当然了,如果fork失败,

Linux内核分析:实验六--Linux进程的创建过程分析

刘畅 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 概述 本次实验在MenuOS中加入fork系统调用,并通过GDB的调试跟踪,近距离的观察Linux中进程创建的过程.阅读Linux进程部分的源码,结合起来理解Linux内核创建新进程的过程. Linux中对进程的描述 Linux中task_struct结构体用于描述系统中的进程,对应x86机器的此结构体定义放在了/include/li

linux 进程的创建

1. 进程号: 每个进程在被初始化的时候,系统都会为其分配一个唯一标识的进程id,称为进程号: 进程号的类型为pid_t,通过getpid()和getppid()可以获取当前进程号和当前进程的父进程的进程号: 2. 进程复制: fork函数,是以父进程为蓝本复制一个新的子进程,包括复制代码段,数据段,堆栈段等,除了代码段,子进程会有自己的物理内存空间,其中的内容是和父进程的数据段,堆栈段是一样的,当然进程号是不一样: 在linux下,fork函数是用写复制实现的.所谓写复制,只复制父进程的虚拟地

Linux下的进程类别(内核线程、轻量级进程和用户进程)以及其创建方式--Linux进程的管理与调度(四)

本文声明 日期 内核版本 架构 作者 GitHub CSDN 2016-05-12 Linux-4.5 X86 & arm gatieme LinuxDeviceDrivers Linux进程管理与调度-之-进程的创建 本文中出现的,内核线程,轻量级进程,用户进程,用户线程等概念,如果不太熟悉, 可以参见 内核线程.轻量级进程.用户线程三种线程概念解惑(线程≠轻量级进程) Linux进程类别 虽然我们在区分Linux进程类别, 但是我还是想说Linux下只有一种类型的进程,那就是task_str

Linux进程管理(3):总结

7. exit与_exit的差异    为了理解这两个系统调用的差异,先来讨论文件内存缓存区的问题. 在linux中,标准输入输出(I/O)函数都是作为文件来处理.对应于打开的每个文件,在内存中都有对应的缓存,每次读取文件时,会多读一些记录到缓存中,这样在下次读文件时,就在缓存中读取:同样,在写文件时也是写在文件对应的缓存中,并不是直接写入硬盘的文件中,等满足了一定条件(如达到一定数量,遇到换行符\n或文件结束标志EOF)才将数据真正的写入文件.这样做的好处就是加快了文件读写的速度.但这样也带来

linux进程编程:子进程创建及执行函数简介

子进程创建及执行函数有三个: (1)fork();(2)exec();(3)system();    下面分别做详细介绍.(1)fork()    函数定义:    pid_t fork();    函数说明:    linux下进程在内存中由三部分数据组成:代码段.数据段.堆栈段.在一个进程中,调用fork函数,可以创建.启动一个新进程.新进程与父进程共享代码段,复制父进程的数据段和堆栈段.创建成功后,fork()会向两个进程都有返回值.向父进程的返回值为子进程的进行号,向子进程的返回值为0.

《linux下进程的创建,执行,监控和终止》

http://blog.csdn.net/miss_acha/article/details/43671047 http://blog.csdn.net/rosekin/article/details/15341835 linux下用脚本实现:监控一个进程,不存在则将其启动. http://www.jb51.net/article/38099.htm linux下监视进程 崩溃挂掉后自动重启的shell脚本 http://www.cnblogs.com/zdz8207/p/3282352.htm

Linux 下进程操作,----进程的创建与控制

---恢复内容开始--- 进程是一个程序一次执行的过程,是操作系统动态执行的基本单元. 进程的概念主要有两点:第一,进程是一个实体.每个进程都有自己的虚拟地址空间,包括文本区.数据区.和堆栈区.文本区域存储处理器执行的代码:数据区存储变量和动态分配的内存:堆栈区存储着活动进程调用的指令和本地变量.第二,进程是一个"执行中的程序",它和程序有本质区别.程序是静态的,它是一些保存在磁盘上的指令的有序集合:而进程是一个动态的概念,它是一个运行着的程序,包含了进程的动态创建.调度和消亡的过程,

Linux-进程描述(4)之进程优先级与进程创建执行

进程优先级 进程cpu资源分配就是指进程的优先权(priority).优先权高的进程有优先执行权利. 权限与优先级.权限(privilege)是指在多用户计算机系统的管理中,某个特定的用户具有特定的系统资源使用权力,像是文件夹,特定系统指令的使用或存储量的限制.权限是有或没有的问题,而优先级则是在已经具有了权限而讨论权限大小的问题.配置进程优先权对多任务环境的linux很有用,可以改善系统性能.还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能.