linux进程控制(一)--unix环境高级编程读书笔记
1.进程PID和特殊的3个进程
每一个进程在系统中都有一个唯一的标识,这个标识叫做进程标识符,或者叫 PID(process identity)。我们可以通过调用 getpid 函数来获取一个进程的PID,也可以调用getppid函数来获取当前进程的父进程PID。在 linux 系统中,有三个特殊的进程,它们的进程 PID 分别为0,1,2。0号进程是系统进程,它存在与内核当中,而不是磁盘文件上。该进程常被成为交换进程,或者叫 swapper。1号进程就是 init 进程,这个进程用来读取一些初始化文件,并且引导系统到一个状态,如多用户状态,init 进程是一个用户进程。2号进程是页精灵进程,用来做一些和虚拟内存相关的工作,可是这个进程我没有在系统中找到。如果有知道的朋友,请告诉我,这个页精灵进程在系统中是哪一个进程,另外页精灵进程也是系统进程。需要说明的是,这3个进程都是由特殊的方式产生的,除了这3个进程以外,linux 系统中的其它进程都是由调用 fork 函数产生的。
2.fork函数
fork 函数用来创建一个子进程,这是 linux 系统中创建子进程的唯一方式。当调用 fork 函数之后,我们称调用 fork 函数产生的进程为子进程,调用fork函数的进程为父进程。fork 函数调用一次,但是返回两次。这两次返回分别是在父进程和子进程中,在父进程中返回子进程的 PID,在子进程中返回0。当调用 fork 函数成功后,父子进程就分别从调用 fork 的地方开始执行。子进程是父进程的一个拷贝,但是现在很多的实现,并不是调用 fork 成功之后就立即拷贝,而是采用了一种称为 COW(copy on write)的技术,只有当子进程需要修改一些值时,才进行拷贝。拷贝的是父进程的数据空间以及堆和栈。我们可以用一个小例子来解释一下 fork 函数的作用及特性:
#include <stdio.h>
#include <unistd.h>int glob = 6;
char buf[] = "a write to stdout\n";int main(void)
{pid_t pid;int var = 88;if( write(STDOUT_FILENO,buf,sizeof(buf)-1)!=sizeof(buf)-1 ){printf("write error\n");return -1;}printf("before fork\n");if( (pid=fork())<0 ){printf("fork error\n");return -1;}else if(pid==0){glob++;var++;}else{sleep(2);}printf("pid=%d\tglob=%d\tvar=%d\n",getpid(),glob,var);return 0;
}
将程序编译生成 a.out 直接运行,输入命令:
./a.out
得到输出结果如下:
a write to stdout
before fork
pid=3131 glob=7 var=89
pid=3130 glob=6 var=88
如果将程序的输出结果重定向到一个文件:
./a.out > /tmp/a
查看 /tmp/a 的内容:
vim /tmp/a
得到输出结果如下:
a write to stdout
before fork
pid=3077 glob=7 var=89
before fork
pid=3076 glob=6 var=88
看到这个结果,我们可能会感到奇怪。为什么第一次输出了一次 before fork,而后面重定向到文件之后,就输出了两次呢?原因其实很简单,我们之前说过,fork之后的子进程会复制父进程的数据空间和堆栈。因为 write 函数是系统调用,没有缓存,所以立即就输出了。而printf在交互的情况下是行缓存的,它是由换行符刷新的,所以当没有重定向时,printf 碰到换行符就立即输出了。而当把这个程序重定向到一个文件时,printf 函数就变为全缓存的了。所以它并不立即输出,而是保留在缓存中。在调用 fork 之后,当子进程复制父进程时,把父进程缓存中的内容也复制到子进程的工作空间中。所以 before fork 分别在父进程和子进程中各输出一次。
fork 函数创建的子进程,会把在父进程中打开的文件描述符,复制到子进程中,也就是说它们共享同一个文件表项。
3.vfork函数
vfork 函数和 fork 函数的基本功能是一样的,它也是调用一次返回两次。但是 vfork 函数和 fork 函数的不同在于,vfork 的目的是创建一个新进程,该进程用来通过调用 exec 函数来执行一个新进程,所以子进程并不复制父进程的数据空间和堆栈,而是先在父进程的工作空间中运行。vfork 函数要求父进程等待子进程先执行,如果在调用了vfork函数之后,子进程的执行还需要依赖于父进程的进一步动作,这将导致死锁。只有当子进程执行了 exec 函数或者 exit 函数之后,父进程才可以被调度执行。我们将上面的程序中的fork函数替换为vfork函数,得到的程序如下:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>int glob = 6;int main(void)
{int var = 88;pid_t pid;printf("before vfork\n");if( (pid=vfork())<0 ){printf("vfork error\n");return -1;}else if(pid==0){var++;glob++;_exit(0);}printf("pid=%d var=%d glob=%d\n",getpid(),var,glob);return 0;
}
不论你将上面的程序执行多少次,
before vfork
pid=3487 var=89 glob=7
输出结果肯定是上面的样子,因为 vfork 函数保证子进程先执行。在子进程中修改了 var 和 glob 的值之后,就调用了 _exi t函数退出了。因为vfork函数产生的子进程并不复制父进程的工作空间,它是在父进程的工作空间中运行的,所以子进程改变了父进程工作空间中的数据,这是很正常的。
4.init 进程领养其他进程
经过前面的介绍,我们已经了解了 init 进程。它不是一个系统进程,它就是存在于 /sbin/init 文件。它的作用是在系统启动时用来读取一些初始化信息,将系统引导到一个状态。另外,它还有一个重要的角色是收养系统中的孤儿进程。那么什么进程叫做孤儿进程呢?顾名思义,孤儿进程就是其父进程在其终止之前终止的进程。
init 进程收养孤儿进程的手续是怎么样的呢?当一个进程要终止时,内核会逐个检查系统中的各个进程,查看它们的父进程是否是正要终止的进程,如果是这样的话,就将它们的父进程修改为 init 进程。
另外,关于init进程清除系统中的僵死进程和僵死进程的一些概念,请参考博客点击打开链接。
5.wait和waitpid
wait 和 waitpid 函数,用来在父进程中等待子进程的结束。wait 函数用来等待父进程的任何一个子进程,只要有子进程终止,它就立即返回。调用 wait 函数,会发生3种情况:
1.当该进程没有任何子进程时,会发生错误,返回-1
2.当该进程的子进程都在运行,尚没有子进程结束时,该进程会发生阻塞
3.当该进程已经有子进程终止时,可期待 wait 函数立即返回,返回值为终止子进程 PID
waitpid 函数和 wait 函数的不同在于,通过参数的设置,它既可以等待任何一个子进程(这时候功能相当于 wait),又可以等待指定 pid 的子进程,并且它可以不用阻塞。
6.exec函数
exec 函数族包括很多的函数,这些函数的作用都是执行一个新的进程。内核执行一个进程的唯一方法是调用 exec 函数,但是 exec 函数并不修改进程的PID。一般的使用方法是先调用fork函数创建一个子进程,然后在子进程中执行 exec 函数。exec 函数族如下:
#include <unistd.h>extern char **environ;int execl(const char *path, const char *arg, ...);int execlp(const char *file, const char *arg, ...);int execle(const char *path, const char *arg,..., char * const envp[]);int execv(const char *path, char *const argv[]);int execvp(const char *file, char *const argv[]);int execve(const char *filename, char *const argv[],
char *const envp[]);
这六个函数的记忆方法是以 p 结尾的 execlp 和 execvp 中第一个参数是文件名,而不是路径。当执行这个文件名指定的文件时,就会在 PATH 指定的路径中,搜寻指定的文件。如果在文件名中出现“/”则按路径来处理。名字中包含“l” 的函数 execl ,execlp 和 execle,说明了在这些函数中传递给要执行的程序的命令行参数是一个列表,这个列表以空指针NULL结尾,它们参数排列一般如下:
pathname , arg1 , arg2 , (char*)0
而函数名字中带有v的函数 execv, execvp 和 execve,一般是将命令行参数的指针放在一个数组 argv 中,这个数组以空指针 NULL 结尾。函数名中包含“e”的函数 execve 和 execle,它规定了需要向该函数传递环境变量。
上面的6个函数中只有 execve 是系统调用,其他的函数都是库函数,最终都需要调用 execve 函数。
linux进程控制(一)--unix环境高级编程读书笔记相关推荐
- 文件和目录(二)--unix环境高级编程读书笔记
在linux中,文件的相关信息都记录在stat这个结构体中,文件长度是记录在stat的st_size成员中.对于普通文件,其长度可以为0,目录的长度一般为1024的倍数,这与linux文件系统中blo ...
- unix进程的环境--unix环境高级编程读书笔记
1.进程的启动 进程总是从 main 函数开始执行的,main函数的函数原型如下: int main(int argc,char* argv[]); 当内核启动 c 程序时,使用一个 ...
- linux信号(二)--unix环境高级编程读书笔记
1.信号集 在linux中,可以用一个称为信号集的数据类型 sigset_t,来表示所有的被阻塞信号的一个集合.对这个集合的操作函数有: #include <signal.h>int s ...
- linux信号(一)--unix环境高级编程读书笔记
1.信号的概念 在这里要给出一个信号的准确概念感觉很困难,可以这么说,信号就是进程之间或者内核与进程间异步通信的一种机制,有点类似于中断的性质.在 linux 系统中有 31 种信号,每一种信 ...
- linux系统数据文件和信息--unix环境高级编程读书笔记
linux系统中的数据文件有很多,在这一章里介绍的主要内容是和系统有关的一系列文件,包括passwd,shadow,group,utmp,wtmp以及一些系统的相关信息和时间的相关操作. 1.pass ...
- 高级IO(一)--UNIX环境高级编程读书笔记
在前面学习了文件IO,标准IO和终端IO,现在学习高级IO,UNIX中怎么有这么多的IO. 1.非阻塞IO 可以将系统调用分为两类:低速系统调用和其他.低速系统调用是可能会使进程永远阻塞的一类系统调用 ...
- 文件io(一)--unix环境高级编程读书笔记
unix-like(后面以linux为例)系统中的文件操作只需要五个函数就足够了,open.close.read.write以及lseek.这些操作被称为不带缓存的io,这里有必要说一下带缓存和不带缓 ...
- 标准IO库--unix环境高级编程读书笔记
标准IO库是C语言提供的一个库,不光存在于linux中,在windows中也是有的.标准IO库和文件IO的不同是,标准IO库是对文件IO(即系统调用)的封装,并且在用户层添加了一些缓冲区. 文件IO的 ...
- APUE Unix环境高级编程读书笔记
.. 转载于:https://www.cnblogs.com/solitrarychen/p/5407536.html
最新文章
- 今日 Paper | 模态平衡模型;组合语义分析;高表达性SQL查询;多人姿态估计模型等
- 股市大涨是不是楼市就要跌了?
- 你必须知道的容器监控 (1) Docker自带子命令与Weave Scope
- varnish几个工具命令行工作情况
- java除了框架还需要什么_除了框架,前端面试还问什么
- 阶段3 3.SpringMVC·_02.参数绑定及自定义类型转换_3 配置解决中文乱码的过滤器
- 【紫书第九章】动态规划(DP)常见模型汇总与DP问题分析方法
- android xml 工具下载,安卓xml文件编辑器
- Java 中设计日期工具类 DateTools 和日期工厂类 DateFactory 完善饱受诟病的原生 Date 类
- 【英语学习工具】程序员学习英语硬背硬记太难了, 在这里推荐 LeHoCat 提供免费的,看视频学英语的工具,制作英语教学课件的工具,帮助自学英语
- iptables 删除规则
- 背诵考研英语单词计划总览
- 安卓直播间websocket协议破解还原
- 用计算机做科学实验心得体会,做实验的心得体会3篇
- 图的存储-邻接矩阵和邻接表之间的相互转化
- C++中,system的神级用法总和,实现用C++操作系统
- 廖雪峰历时3个月打磨出价值1980的数据分析教程,终终终于免费啦!
- IT直男搞装修,他们会把房子折腾成啥样呢?
- Sql Server中and和or的优先级问题
- 新能源行业一站式智慧供应链解决方案
热门文章
- [转载] pandas入门:Series、DataFrame、Index基本操作都有了!
- [转载] 20个常用Python库及200个第三方库
- 编写DAO,通过JdbcTemplate操作数据库的实践
- django 数据库交互2
- c#获取带有汉字的字符串长度
- tolua++ 使用有感
- 数据库性能Quest Performance Analysis Overview
- 数据结构上机实践第九周项目2 - 二叉树遍历的递归算法
- top 并grep 特定信息打印至txt
- python email模块详解_Python使用email模块对邮件进行编码和解码的实例教程