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环境高级编程读书笔记相关推荐

  1. 文件和目录(二)--unix环境高级编程读书笔记

    在linux中,文件的相关信息都记录在stat这个结构体中,文件长度是记录在stat的st_size成员中.对于普通文件,其长度可以为0,目录的长度一般为1024的倍数,这与linux文件系统中blo ...

  2. unix进程的环境--unix环境高级编程读书笔记

    1.进程的启动 进程总是从   main   函数开始执行的,main函数的函数原型如下: int main(int argc,char* argv[]); 当内核启动  c   程序时,使用一个   ...

  3. linux信号(二)--unix环境高级编程读书笔记

    1.信号集 在linux中,可以用一个称为信号集的数据类型  sigset_t,来表示所有的被阻塞信号的一个集合.对这个集合的操作函数有: #include <signal.h>int s ...

  4. linux信号(一)--unix环境高级编程读书笔记

    1.信号的概念 在这里要给出一个信号的准确概念感觉很困难,可以这么说,信号就是进程之间或者内核与进程间异步通信的一种机制,有点类似于中断的性质.在  linux  系统中有  31  种信号,每一种信 ...

  5. linux系统数据文件和信息--unix环境高级编程读书笔记

    linux系统中的数据文件有很多,在这一章里介绍的主要内容是和系统有关的一系列文件,包括passwd,shadow,group,utmp,wtmp以及一些系统的相关信息和时间的相关操作. 1.pass ...

  6. 高级IO(一)--UNIX环境高级编程读书笔记

    在前面学习了文件IO,标准IO和终端IO,现在学习高级IO,UNIX中怎么有这么多的IO. 1.非阻塞IO 可以将系统调用分为两类:低速系统调用和其他.低速系统调用是可能会使进程永远阻塞的一类系统调用 ...

  7. 文件io(一)--unix环境高级编程读书笔记

    unix-like(后面以linux为例)系统中的文件操作只需要五个函数就足够了,open.close.read.write以及lseek.这些操作被称为不带缓存的io,这里有必要说一下带缓存和不带缓 ...

  8. 标准IO库--unix环境高级编程读书笔记

    标准IO库是C语言提供的一个库,不光存在于linux中,在windows中也是有的.标准IO库和文件IO的不同是,标准IO库是对文件IO(即系统调用)的封装,并且在用户层添加了一些缓冲区. 文件IO的 ...

  9. APUE Unix环境高级编程读书笔记

    .. 转载于:https://www.cnblogs.com/solitrarychen/p/5407536.html

最新文章

  1. 今日 Paper | 模态平衡模型;组合语义分析;高表达性SQL查询;多人姿态估计模型等
  2. 股市大涨是不是楼市就要跌了?
  3. 你必须知道的容器监控 (1) Docker自带子命令与Weave Scope
  4. varnish几个工具命令行工作情况
  5. java除了框架还需要什么_除了框架,前端面试还问什么
  6. 阶段3 3.SpringMVC·_02.参数绑定及自定义类型转换_3 配置解决中文乱码的过滤器
  7. 【紫书第九章】动态规划(DP)常见模型汇总与DP问题分析方法
  8. android xml 工具下载,安卓xml文件编辑器
  9. Java 中设计日期工具类 DateTools 和日期工厂类 DateFactory 完善饱受诟病的原生 Date 类
  10. 【英语学习工具】程序员学习英语硬背硬记太难了, 在这里推荐 LeHoCat 提供免费的,看视频学英语的工具,制作英语教学课件的工具,帮助自学英语
  11. iptables 删除规则
  12. 背诵考研英语单词计划总览
  13. 安卓直播间websocket协议破解还原
  14. 用计算机做科学实验心得体会,做实验的心得体会3篇
  15. 图的存储-邻接矩阵和邻接表之间的相互转化
  16. C++中,system的神级用法总和,实现用C++操作系统
  17. 廖雪峰历时3个月打磨出价值1980的数据分析教程,终终终于免费啦!
  18. IT直男搞装修,他们会把房子折腾成啥样呢?
  19. Sql Server中and和or的优先级问题
  20. 新能源行业一站式智慧供应链解决方案

热门文章

  1. [转载] pandas入门:Series、DataFrame、Index基本操作都有了!
  2. [转载] 20个常用Python库及200个第三方库
  3. 编写DAO,通过JdbcTemplate操作数据库的实践
  4. django 数据库交互2
  5. c#获取带有汉字的字符串长度
  6. tolua++ 使用有感
  7. 数据库性能Quest Performance Analysis Overview
  8. 数据结构上机实践第九周项目2 - 二叉树遍历的递归算法
  9. top 并grep 特定信息打印至txt
  10. python email模块详解_Python使用email模块对邮件进行编码和解码的实例教程