Working_With_Process读书笔记

1、使用man手册查询

man 1 which 一般命令,即:shell命令

man 2 select 系统调用

man 3 malloc c函数

man 4 特殊文件

2、在unix中所有代码都运行在进程中

3、进程号pid可用于多个进程在日志文件的区分

Process.pid 或者 $$ 获取pid

4、父进程号可用于检测守护进程

Process.ppid 获取ppid

5、文件描述符是使用套接字、管道等进程网络编程的核心所在,也是文件系统操作的核心。其中0、1、2文件描述符标号分别被STDIN、STDOUT、STDERR占用

STDIN.fileno 获取文件描述符标号

6、在高并发网络连接中,往往会因为打开的文件描述符过多,超出限制,如果需要提高并发数就需要修改系统的资源限制

Process.getrlimit(:NOFILE) 获取资源限制

Process.setrlimit(:NOFILE) 设置软限制,对于硬限制需要具有相应的权限才能够修改

7、环境变量经常作为一种将输入传递到命令行程序中的通用方法,环境变量(即:ENV)可以在多种语言环境中共享。当然也可以使用解析命令行选项(即:命令行参数
ARGV,可通过标准库optparse解析)的方式来进行处理

8、通过进程名可以进行通信或识别进程,从而进行进程管理

$PROGRAME_NAME$0 可以获取和设置进程名

9、用0-255的数字表示的进程退出码,其中0表示正常结束。退出码也可以作为一种通信的途径

exit 0 即可退出进程

at_exit {} 可在exit、abort、raise之后自动调用,但不会在exit!之后调用

10、进程可以fork出新的子进程,子进程可以继承(复制一份相同的)父进程的内存中的所有内容,包括:套接字、文件描述符、管道等,对于fork的使用需要注意避免fork炸弹

fork
用于创建子进程,返回2个值,第一个为子进程的pid,来源于父进程,第二个为nil,来自子进程

11、父进程死亡之后不会影响子进程的运行,所以为了避免产生不受控制的孤儿进程,需要正确管理和控制子进程

12、由于fork进程会消耗大量的内存,也容易产生fork炸弹,所以写时拷贝(Cow)是一种更友好的方式,但是主流的MRI、Rubinis对其并不支持,不支持的原因是由于GC的实现方式限制,在ruby企业版中是支持的。【注:MRI2.x或许已经支持了,需要调研】

13、写时拷贝(Copy On Write)是在fork之后并不马上复制父进程的内存内容,而是在内存中共享一份,只有在涉及到数据修改时才会为子进程复制一份,而其余部分仍然共享。

14、父进程如果需要等待子进程执行完成之后再退出,可以有多种方式,即:Process.wait家族

Process.wait阻塞等待任意子进程(也可以传指定pid,同Process.waitpid),直到子进程退出,返回退出子进程的pid

Process.wait2类似wait,但是除了返回pid还会返回子进程的退出码信息,即可达到进程间通信的目的

Process.waitpid pid 阻塞等待指定进程退出,也可以传递-1参数,让其等待任意子进程

Process.waitpid2 pidProcess.wait2

15、内核会将退出的进程信息按照退出顺序加入到队列中,以便父进程顺序的接受退出信息

16、由于Process.wait在没有子进程的情况下会抛出异常,所以在使用时需要记录下创建了多少的子进程

17、关注子进程的理念是一本的Unix编程模式的核心。这种模式有时被称为看顾进程,master/worker或者perforking,fork出多个并发子进程的进程需要看管这些子进程,确保他们能够响应,并对子进程的退出码做出响应。这种模式兼顾了并发性和可靠性

18、僵尸进程是指那些已经结束的进程,如果它的状态信息一直未被读取,那么这个进程就称为了僵尸进程,一旦父进程读取了僵尸进程的状态信息,那么僵尸进程就会消失。僵尸进程会消耗内核资源,因为内核会一直保存进程的状态信息(难道内核没有机制来去除这些僵尸进程的状态信息?)

Process.detach pid可以防止僵尸进程的产生,原理是创建一个线程,专门来执行Process.wait pid,可使用spawngem包来实现这样的功能

19、Process.wait可以阻塞的等待子进程结束,但是这样父进程就没法同时进行其他的任务了,所以此时可以采用Unix信号的方式进程处理。

20、Unix信号投递是不可靠的,即:并发的退出消息,当两个子进程同时退出时,在处理第一个CHLD信号时,未必能够收到第二个CHLD信号。为了避免这种情况,可以在信号处理代码段中循环的处理,使用Process.wait(-1, Process::WNOHANG),其中Process::WNOHANG表示如果没有子进程退出就就是阻塞等待

21、捕获信号使用trap,信号可以中断任何代码

22、如果没有子进程存在,Process.wait及其变量将会抛出Errno::ECHILD异常,如果不知道需要等待多少子进程就应该捕获异常并正确的处理他。

23、信号是一种异步通信,当进程从内核那里接收到一个信号时,它可以执行某一操作:(1)忽略该信号(2)执行特定的操作(3)执行默认的操作

24、Unix信号是由一个进程发送到另一个进程,其中由内核中转

trap 接收信号
Process.kill(信号,pid) 发送信号

25、信号有部分是可以重定义的,有部分是不可重定义的

27、由于使用信号相当于使用全局变量,重新定义信号的行为有可能影响到别的信号处理程序,故,在尽量避免在你的代码库中引入信号处理程序,如果想加一些操作,以在退出之前能够清理资源,可以使用at_exit钩子实现。信号一般使用在服务器和守护进程。memprof是个学习信号的不错案例

28、管道是进程间通信(IPC)的一种方式,管道是单向的,即:写管道不可用于读,读管道不可用于写。在向管道中写入数据时,如果结束了就需要向管道中传入EOF,用于告诉读端,数据已结束,否则读端会一直阻塞等待数据。管道中的数据是以数据流的方式存在

r, w = IO.pipe 创建一个管道,其他使用方式类似于文件

29、Unix套接字是IPC的一种方式,他比TCP套接字快很多,它可以接受以消息的格式进行通信。他只可以作用于同一物理机。套接字提供的是双向通信。

require 'socket'
child_socket, parent_scoket = Scoket.pair(:UNIX,:DGRAM,0)
其中数据传递方式可以修改

30、远程IPC可以有几种方案:(1)TCP套接字(2)RPC(远程过程调用)(3)消息系统,如:Kafka(4)分布式系统(包含http等其他协议?)

31、进程间通信一般采用管道、套接字比较抽象的方式,快速又简单,用来代替原始方法共享数据库或日志文件

32、守护进程就是衍生进程的时候,让其既不是会话领导,也不是同一进程组daemons
可以帮助更好的理解守护进程,一般只有一直需要在后台响应的进程才需要被设置为守护进程

Process.setsid 设置一个进程为新的进程组及新的会话组领导

33、由于像exec之类的方法会用另一个进程替换当前进程,且再也不会回来了。所以,常常使用fork+exec的方式进程,保证原进程可以继续工作又可以实现exec的功能。如果需要exec的执行结果,那么就可以使用Process.wait一直等到子进程结束之后获取相应的结果信息。注意:exec不会关闭任何打开的文件描述符(默认情况下)或是进行内存清理,只要是fork就会存在fork带来的问题,比如:内存复制等

exec '字符串参数' 会启动一个shell进程,然后再将字符串交由shell解释【比较浪费,除非必须由shell来解析】
exec 数组 会跳过shell,直接将此数组作为新进程的ARGV【推荐】

system 参数 结果退出码为0,返回值会true,否则为false,阻塞的
Process.spawn 参数 是非阻塞的,如果要实现阻塞的方式,就要截止Process.waitpid

IO.popen 参数 块最常见的用法是用纯Ruby来实现Unix管道,等价于fork+exec+通信管道参数,只能指定一个流

Open3.open3可以同时访问一个生成进程的STDIN/STDOUT/STDERR(依赖open3库)。

使用以上方法都会引入fork带来的缺点,有一些用于生成进程的原生Unix系统调用并不会带来fork所引发的那些开销。ruby核心库不支持,可以使用posix-spawn包来实现,其仅会复制父进程打开的文件描述符,而其他内存内容不会复制,即:不可以与父进程共享内存。


标签:

专题:

发表于2017-02-25 16:45:41,最后修改于2019-08-02 18:42:12。

本站文章欢迎链接分享,禁止全文转载。


« 上一篇 使用node-inspector调试你的node代码 下一篇 » 通过案例学习如何在mac中抓取http请求信息

推荐阅读

Big Image