本篇文章主要总结分享记录一下运维工作中经常打交道的Unix进程。程序是代码的集合,而进程是运行中的程序产生的。那么进程都有那些特性呢?且看下文,部分经典且难懂的地方,使用python代码实现,可以让读者更好的理解与记忆。

进程特性

  • 进程都有标识符

  在系统中运行的所有进程都有一个唯一的进程标识符,称之为pid。

  pid并不传达关于进程的任何信息,它仅仅是一个顺序字符标识。进程在内核眼中只是个数字而已。

pid是对进程的一种简单通用的描述,它与进程内容无关。

  • 进程都有父进程

  系统中运行的每一个进程都有对应的父进程。每个进程都知道其父进程的标识符(ppid)。

  在多数情况下,特定进程的父进程就是调用它的那个进程。

  举例:启动终端并进入bash提示,执行ls命令。那么bash的父进程是终端进程,ls的父进程是bash进程。

  • 进程都有文件描述符

  Unix哲学指出,在Unix世界中,一切皆文件。设备、套接字、管道等都是文件。   文件描述符代表资源。无论何时打开一个资源,都会得到一个文件描述符编号(file descriptor number)。文件描述符只存在其所属的进程之中,不会在无关进程之间共享。当进程结束,它会和其他有进程所打开的资源一同被关闭。

  文件描述符编号的分配是从尚未使用的最小的值开始。资源一旦关闭,对应的文件描述符编号就又能使用了。文件描述符只是用来跟踪打开的资源,已经关闭的资源是没有文件描述符的。

  • 进程资源限制

  接上文,文件描述符代表已打开的资源,那么一个进程可以拥有多少文件描述符,也就是可以拥有多少资源呢?这是由系统配置决定的。内核为进程施加了某些资源限制。比如并发数,最大的文件数,进程栈的段大小等。

  • 进程都有环境

  环境变量是包含进程数据的键值对(key-value pairs)。   所有进程都从其父进程处继承环境变量。它们由父进程设置并被子进程所继承。每个进程都有环境变量,环境变量对于特定进程而言是全局性的。(ENV)

  • 进程都有参数

  所有进程都可以访问名为ARGV的特殊数组。不同编程语言可能在实现方式上略微不同,但是都会有argv。

  argv的全称:argument vector。换句话说,argv是一个参数向量或数组。它保存了在命令行中传递给当前进程的参数。

  • 进程都有名字

  Unix进程几乎没有什么固有的方法来获悉彼此的状态。有两种运行在进程自身层面上的机制可以用来通信。一个是进程名称,另一个是退出码。

  系统中每一个进程都有名称。

  • 进程都有退出码

  当进程即将结束时,它还有最后一线机会留下自身的信息:退出码。所有进程在退出的时候都带有数字退出码(0-255),用于指明程序是否正常结束。按照惯例,退出码为0代表进程顺利结束,其他退出码则代表出现了错误,不同的退出码对应不同的错误。   在shell脚本中可以使用exit退出,并指定退出码。如:exit 1

  • 进程可以衍生/衍生进程

  衍生(forking)是Unix编程中最强大的概念之一。fork(2)系统调用允许运行中的进程以编程的形式创建新的进程。这个新进程和原始进程一模一样。

  进行衍生时,调用fork(2)的进程被称为父进程,新创建的进程被称为子进程。

  子进程从父进程处继承了其所占有内存中的所有内容,以及所有属于父进程的已打开的文件描述符。

  子进程是一个全新的进程,它拥有自己唯一的pid。子进程的ppid就是调用fork(2)的进程的pid。

  在fork(2)调用时,子进程从父进程处继承了所有的文件描述符,也获得了父进程所有的文件描述符的编号。这样,父子进程就可以共享打开的文件、套接字等资源。

  子进程继承了父进程内存中所有内容,即子进程实际上各自享有一份已载入内存代码库的副本。子进程可以随意更改其内存内容的副本,而不会对父进程造成任何影响。

fork示例:

handetiandeMacBook-Pro:~ handetian$ cat 1.py
#!/usr/bin/env python import os
print '1: %s' % os.getpid() if os.fork():
print 'This process is 2: %s' % os.getpid()
else:
print 'This process is 3: %s' % os.getpid()
handetiandeMacBook-Pro:~ handetian$ python 1.py
1: 21198
This process is 2: 21198
This process is 3: 21199

  从上面的示例发现,fork方法的一次调用实际上返回了两次。fork创造了一个新进程。所以它在调用进程(父进程)中返回一次,在新创建的进程(子进程)中又返回一次。if语句块中的代码是由父进程执行的,而else语句块中的代码是子进程执行的。子进程执行完else语句块之后退出,父进程则继续运行。

上述代码的输出是跟fork的返回值有关的。在父进程中,fork返回的值是新创建的子进程的pid,所以父进程执行if语句块中的代码。在子进程中,fork返回的值是nil,nil为假,所以子进程执行了else语句块中的代码。

  根据上边的示例以及返回结果可以表明:通过生成新的进程,程序代码可以(不能完全保证)被分配到多个CPU核心中。

  fork(2)创建了一个和旧进程一模一样的新进程。如果一个使用了1GB内存的进程进行了衍生,那么就有2GB的内存被占用了。如果重复几次该操作,内存会很快被耗尽。这也就是所谓的fork炸弹,因此,我们在执行并发操作前,一定要确保程序执行的后果!!!

  • 孤儿进程

  在上文中我们发现,如果涉及子进程,很多事情就变得不那么简单了。

  通过终端启动单个前台进程,只有该进程向STOUT输出,键盘输入CTRL-C可以退出该程序。

  一旦进程衍生了子进程,我们如何确定CTRL-C可以结束父进程还是子进程,还是全部呢?明白这其中的原理可以避免孤儿进程的产生。

  当父进程结束后,子进程会怎样? 子进程会安然无恙的运行,因为操作系统不会对子进程区别对待。

  父进程结束后,子(孤儿)进程由谁接管? 在Linux系统中,孤儿(子)进程会由init(1号)进程接管。其实守护进程也是孤儿进程。

  • 进程是友好的

  上文中我们聊到fork(2)创建了一个跟父进程一模一样的子进程。它包含了父进程在内存中的一切内容。如果确实复制一份所有数据,系统开销会很大,现代Unix系统采用了写时复制(copy-on-write, CoW)机制,解决该问题。也就是说父进程和子进程实际上是在共享内存中的内容,只有其中某个进程需要对数据进行修改时才会进程内存复制,是得不同进程之间具有一定的隔离。

  • 进程可以等待

  上文中我们使用fork(2)衍生的子进程都是跟父进程同时进行,如果我们希望子进程异步的处理事务那么没有问题。此种情况下,如果父进程先退出,那么子进程就会变成孤儿进程。那么父进程是否可以等待子进程退出后再退出呢?答案是可以的,可以通过Process.wait或Process.waitpid等待子进程执行完成。

  示例:

handetiandeMacBook-Pro:~ handetian$ cat 1.py
#!/usr/bin/env python import os,time
print 'This process is 1: %s' % os.getpid() pid = os.fork()
if pid:
print 'This process is 2: %s' % os.getpid()
#os.wait()
sleep(5)
#print "prcess 1 is died!!!"
else:
for i in xrange(10):
time.sleep(1)
print 'This process is 3: %s' % os.getpid()

上述示例,添加注释(#os.wait,#print...),效果是父进程和子进程同步执行,父进程退出后,子进程继续执行。解除注释(#os.wait,#print...),父进程会等待子进程执行完后,再退出,并且打印process is died!!!

  • 僵尸进程

  内核会一直保留已退出的子进程的状态信息,直到父进程使用使用Process.wait请求这些信息。如果父进程一直不发出请求,那么状态信息就会一直被内核保留,造成内核资源浪费。

  示例:

handetiandeMacBook-Pro:~ handetian$ cat 1.py
#!/usr/bin/env python import os,time
print 'This process is 1: %s' % os.getpid() pid = os.fork()
if pid:
print 'This process is 2: %s' % os.getpid()
while 1:
time.sleep(1)
else:
print 'This process is 3: %s' % os.getpid()

  执行结果:

handetiandeMacBook-Pro:~ handetian$ python .py &
[]
handetiandeMacBook-Pro:~ handetian$ This process is :
This process is :
This process is : handetiandeMacBook-Pro:~ handetian$ ps -ho pid,state -p
PID STAT
S
handetiandeMacBook-Pro:~ handetian$ ps -ho pid,state -p
PID STAT
Z

示例代码是fork(2)一个子进程,子进程执行后退出,父进程长眠。通过执行结果处的输入,可以看到父进程S(睡眠),子进程Z(僵尸)。这就是父进程一直没有读取子进程的状态信息导致的。相应的解决的办法就是父进程在子进程退出读取

  • 进程皆可获得信号

  上文中父进程可以通过Process.wait监管子进程。但Process.wait是一个block调用,直到子进程结束,调用才会返回。现实中,父进程不可能一直有时间等待子进程结束。所以,下面将要聊得是Unix信号。   信号是什么?信号是一种异步通信。进程可以对接收到的信息进行如下操作:1.忽略该信号 2.执行特定操作 3.执行默认操作

  信号来自哪里?信号由一个进程发送,经过内核,内核发送给另一个进程。内核是信号发送的中介。

  常用信号操作:

    Term -> 表示进程会立即结束

    Core -> 表示进程会立即结束并进行核心转储(栈跟踪)

    Ign -> 表示进程会忽略该信号

    Stop -> 表示进程会停止运行(暂停)

    Cont -> 表示进程会恢复运行(继续)

  常用的信号:

    SIGHUP 1 Term

    SIGINT 2 Term

    SIGQUIT 3 Core

    SIGKIAL 9 Term

    SIGTERM 15 Term

    SIGUSR1 30,10,16 Term #自定义信号

    SIGUSR2 31,12,17 Term # 自定义信号

    SIGSTOP 17,19,23 Stop #该信号不能被捕捉,阻塞或忽略。

  • 进程皆可互通

  进程间通信(IPC)的实现有很多方法,最常见的两种:管道(Pipe)和套接字(Socket pairs)

  一个进程打开一个wirte管道,一个进程打开read管道,rea管道一直接收数据,直至接收到一个EOF标志时停止接收。管道通信是单向的。

  IPC是运行在同一台机器上的进程间的通信,不同机器之间的进程通信需要使用TCP套接字实现。

Unix进程特性的更多相关文章

  1. Unix进程和线程管理及其异同

    Unix进程和线程管理及其异同 一,进程 1,什么是进程 在最初的单处理器系统中,系统中的多道程序按照一定规则切换而实现多任务处理,后来发现多个程序并发导致系统资源被共享,为了描述和管理程序对共享资源 ...

  2. UNIX进程

    UNIX进程控制的博客   http://blog.csdn.net/yang_yulei/article/details/17404021 Linux的概念与体系    http://www.cnb ...

  3. 了解Unix进程(1)

    今天瞎看 看到一本了解Unix进程 -- 理解UNIX进程 的书 不错,可以看看,使用的ruby语言,第一章讲的是一些基础的知识 1.输出进程号和父进程号: puts Process.pid # 得到 ...

  4. UNIX 进程间通讯(IPC)概念(Posix,System V IPC)

     IPC(Inter-Process Communication,进程间通讯)可以有三种信息共享方式(随文件系统,随内核,随共享内存).(当然这里虽然说是进程间通讯,其实也是可以和线程相通的). 相对 ...

  5. Linux/UNIX进程控制(1)

    进程控制(1) 进程标识符 每一个进程都有肺腑的整形表示唯一的进程ID.按一个进程终止后,其进程ID就能够再次使用了.例如以下是几个典型进程的ID及其类型和功能. ID     进程名         ...

  6. Supervisor安装与配置(Linux/Unix进程管理工具)

    原文链接:http://blog.csdn.net/xyang81/article/details/51555473 Supervisor(http://supervisord.org/)是用Pyth ...

  7. Supervisor(Linux/Unix进程管理工具)安装与配置

    参考链接:https://blog.csdn.net/xyang81/article/details/51555473 Supervisor(http://supervisord.org/)是用Pyt ...

  8. Shell实现Unix进程间信息交换的几种方法

    本文将介绍在SCO OpenServer5.0.5系统中使用shell语言来实现进程间信息交换的几种方法: 使用命名管道实现进程间信息交换 使用kill命令和trap语句实现进程间信息交换 使用点命令 ...

  9. unix进程通信方式总结(中)(转)

    在上一篇博客http://blog.csdn.net/caoyan_12727/article/details/52049417已经总结了<<uinx环境高级编程>>进程通信前 ...

随机推荐

  1. piezo film 压电相关信息记录 (2018-05-04 更新)

    piezo film 压电相关信息记录 起因需要使用 Piezo 做一些设计 http://www.te.com.cn/chn-zh/videos/transportation/piezo-film- ...

  2. Shell脚本一键安装LNMP环境

    https://sourceforge.net/projects/opensourcefile/files/ Nginx是一款高性能的HTTP和反向代理服务器.Nginx在反向代理,Rewrite规则 ...

  3. 封装与继承(PHP学习)

    什么是封装? 答:封装时不知道内部构造,对外部只展现功能的这种行为.例如:收音机,你不知道收音机内部的构造,但是你知道收音机是能用来听广播的. 在PHP中,封装是,不对外公布,属性和方法,这些属性和方 ...

  4. Hibernate学习3—映射对象标识符(OID)

    一.Hibernate 用对象标识符(OID)来区分对象 作如下代码的实验: public class StudentTest { public static void main(String[] a ...

  5. POJ 2785 4 Values whose Sum is 0(折半枚举+二分)

    4 Values whose Sum is 0 Time Limit: 15000MS   Memory Limit: 228000K Total Submissions: 25675   Accep ...

  6. MongoDB day01

    MongoDB芒果数据库 数据存储阶段 文件管理阶段(.txt .doc .xlc) 优点:数据可以长期保存:数据有一定格式化规范:可以大量存储:使用简单方便 缺点:数据一致性差:用户查找修改不方便: ...

  7. 翻译内核uvcvideo.txt

    翻译源:linux-3.4.20\Documentation\video4linux\uvcvideo.txt 译文: Linux USB视频类(UVC)驱动程序 ================== ...

  8. 存储过程与SQL语句怎么选择

    应用存储过程的优点:1.具有更好的性能存储过程是预编译的,只在创建时进行编译,以后每次执行存储过程都不需再重新编译,而一般 SQL 语句每执行一次就编译一次,因此使用存储过程可以提高数据库执行速度.2 ...

  9. open the flashback

    1.打开flashback: 关闭数据库 启动到mount方式 SQL>startup mount; 如果归档没有打开,打开归档[因为flashback依赖Media recovery,所以在打 ...

  10. spring声明式事务管理详情解析

    前沿:通过对spring事务管理有了比较深入学习,本文将不做实例,而是指定具体的类和配置文件进行讲解. 本文内容: 1.了解什么是声明式事务? 2.声明式事务管理分别有哪几种? 3.这几种事务管理之间 ...