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)


书本链接