Prime 基础
Unix system -> Userland(用户) / Kernel(内核)
Userland: your program, do mathmatics
man命令:
man1 一般命令, ls
man2 系统调用, fork, getpid, getppid等
man3 C库函数, malloc
man4 特殊文件
Process 进程
Unix进程有:
- id
- ppid(父进程)
- fd(file descriptors 文件描述符) Unix中一切都是文件
- 资源限制
- 环境变量(environment)
- 参数(arguments)
- 名称(name)
- 退出码(exit codes) 0-255, 0代表成功, 其他代表错误
FD - 文件描述符
文件描述符在进程打开资源的时候分配,然后与进程一起死亡。每次打开资源的时候,优先使用没有被占用的最低值。关闭的资源(没有使用的资源)没有文件描述符。
passwd = File.open('/etc/passwd')
puts passwd.fileno
passwd.close
puts passwd.fileno
"""
3
-e:4:in `fileno': closed stream (IOError)
"""
0 STDIN.fileno 标准输入
1 STDOUT.fileno 标准输出
2 TDERR.fileno 标准错误
资源限制
即打开资源的数量。
- 软限制(可以超过)
- 硬限制
获取限制getrlimit(2)
, 设置限制 setrlimit(2)
环境变量
没有系统调用操作环境变量,一般通过setenv(3)
和getenv(3)
退出
Kernel.exit # code为1
Kernel.abort "some error messages." # code为1
Kernel.raise # 抛出异常, 如果异常没有被处理则最终会退出
Fork
进程Fork时,调用fork(2)的进程为父进程,fork出来的进程为子进程。 子进程继承了父进程的所有内存的拷贝、打开的文件描述符,由于是一份拷贝,子进程可以自由修改内存而不影响父进程。
在Ruby中常使用fork:
fork do
puts "hello" # 子进程执行完块后就退出
end
孤立进程
父进程退出时,子进程会继续运行。
写时复制 - CoW
为了解决fork复制大量内存的问题,父进程和子进程实际上将共享内存中的相同物理数据,直到其中一个需要修改它,此时将复制内存,以便保留两个进程之间的适当分离。
arr = [1]
fork do
arr << 2
puts arr # [1, 2]
end
puts arr # [1]
等待子进程
fork do
5.times do
sleep 1
puts "I am an orphan!"
end
end
Process.wait # 等待一个子进程结束
abort "Parent process died..."
如果需要等待多个子进程结束:
FORK_NUM.times do
puts Process.wait # 返回PID
# puts Process.wait2 # 返回PID和状态码
# Process.waitpid() # 等待指定PID子进程退出
end
僵尸进程
在其父进程仍处于活动状态时死亡的每个子进程都将成为僵尸。一旦父进程从僵尸那里收集到状态,它就会消失,不再消耗内核资源。
用 Process.detach(pid)
来给子进程收尸!
信号
child_processes = 3
dead_processes = 0
# We fork 3 child processes.
child_processes.times do
fork do
# They sleep for 3 seconds.
sleep 3
end
end
# Sync $stdout so the call to #puts in the CHLD handler isn't
# buffered. Can cause a ThreadError if a signal handler is
# interrupted after calling #puts. Always a good idea to do
# this if your handlers will be doing IO.
$stdout.sync = true
# Our parent process will be busy doing some intense mathematics.
# But still wants to know when one of its children exits.
# By trapping the :CHLD signal our process will be notified by the kernel
# when one of its children exits.
trap(:CHLD) do
# Since Process.wait queues up any data that it has for us we can ask for it
# here, since we know that one of our child processes has exited.
# We loop over a non-blocking Process.wait to ensure that any dead child
# processes are accounted for.
begin
while pid = Process.wait(-1, Process::WNOHANG)
puts pid
dead_processes += 1
end
rescue Errno::ECHILD
end
end
loop do
# We exit ourself once all the child processes are accounted for.
exit if dead_processes == child_processes
sleep 1
end
通过SIGCHLD信号来获得子进程死亡的消息,并用Process.wait(-1, Process::WNOHANG)
来关闭阻塞。没有子进程退出时,就结束。
Signal Value Action Comment
-------------------------------------------------------------------------
SIGHUP 1 Term Hangup detected on controlling terminal
or death of controlling process
SIGINT 2 Term Interrupt from keyboard
SIGQUIT 3 Core Quit from keyboard
SIGILL 4 Core Illegal Instruction
SIGABRT 6 Core Abort signal from abort(3)
SIGFPE 8 Core Floating point exception
SIGKILL 9 Term Kill signal
SIGSEGV 11 Core Invalid memory reference
SIGPIPE 13 Term Broken pipe: write to pipe with no readers
SIGALRM 14 Term Timer signal from alarm(2)
SIGTERM 15 Term Termination signal
SIGUSR1 30,10,16 Term User-defined signal 1
SIGUSR2 31,12,17 Term User-defined signal 2
SIGCHLD 20,17,18 Ign Child stopped or terminated
SIGCONT 19,18,25 Cont Continue if stopped
SIGSTOP 17,19,23 Stop Stop process
SIGTSTP 18,20,24 Stop Stop typed at tty
SIGTTIN 21,21,26 Stop tty input for background process
SIGTTOU 22,22,27 Stop tty output for background process
The signals SIGKILL and SIGSTOP cannot be trapped, blocked, or ignored.
如SIGINT就是Ctrl+C
Ruby中重新定义接受到信号的行为:
puts Process.pid
trap(:INT) { print "Na na na, you can't get me" }
sleep # so that we have time to send it a signal
当另外一个进程尝试发送Process.kill(:INT, <pid of first session>)
中断信号时,这个进程不会被中断。
进程间通信(IPC)
管道
reader, writer = IO.pipe
, IO.pipe可以看作匿名文件。
reader, writer = IO.pipe
writer.write("Into the pipe I go...")
writer.close
puts reader.read
close的时候会在管道最后放上EOF(文件结束标志),读取的时候遇到就会停止。
[IO Object].write, read 和 puts, gets 区别是puts和gets是使用换行符作为分割符,write和read是用EOF。
IO.pipe 只能用于单向通信,reader只能读,writer只能写。
Socket - 套接字
require 'socket'
Socket.pair(:UNIX, :DGRAM, 0) #=> [#<Socket:fd 15>, #<Socket:fd 16>]
子套接字和父套接字都可以读取(recv)和写入(send)