On Github prioux / ipc-presentation
These things aren't that important for this discussion.
These are the essential components of IPC.
A process can only be created by another process...
... by making a perfect copy of itself.
This is also how the tree structure emerges. The copy becomes a child of the original process.
Two processes on the same computer
Two processes on the different computers
By creating files.
They are a sort of ultra-simple message.
The only content is the type of signal, a simple number.
E.g: Signal #11 is called SIGSEGV.
macosx% kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGEMT 8) SIGFPE 9) SIGKILL 10) SIGBUS 11) SIGSEGV 12) SIGSYS 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGURG 17) SIGSTOP 18) SIGTSTP 19) SIGCONT 20) SIGCHLD 21) SIGTTIN 22) SIGTTOU 23) SIGIO 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGINFO 30) SIGUSR1 31) SIGUSR2
NAME Default Action Description SIGHUP terminate process terminal line hangup SIGINT terminate process interrupt program SIGQUIT create core image quit program SIGILL create core image illegal instruction SIGTRAP create core image trace trap SIGABRT create core image abort(3) call (formerly SIGIOT) SIGEMT create core image emulate instruction executed SIGFPE create core image floating-point exception SIGKILL terminate process kill program SIGBUS create core image bus error SIGSEGV create core image segmentation violation ... etc...Ref: sigvec(2)
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 ... etc...Ref: signal(7)
To send a signal you need two values:
kill -TERM 1234 # same as kill -15 1234
kill(SIGTERM,1234); # SIGTERM is a constant
posix_kill(1234, SIGTERM);
Process.kill("TERM", 1234)
function oh_no { echo Hello } trap oh_no SIGUSR1
sub oh_no { print "Hello\n" } $SIG{'USR1'} = \&oh_no;
function oh_no($s) { echo "Hello" } pcntl_signal(SIGUSR1, "oh_no");
Signal.trap("USR1") do puts "Hello" end
#!/usr/bin/ruby float = 0.0 counter = 0 while true do float = float.next_float counter += 1 puts "Float=" + float.to_s if counter % 1000000 == 0 end
We get about two reports per second on my Mac mini:
unix% ruby test_inc.rb Float=4.940656e-318 Float=9.881313e-318 Float=1.482197e-317 Float=1.9762626e-317 Float=2.470328e-317 Float=2.964394e-317 ...
#!/usr/bin/ruby float = 0.0 Signal.trap("USR1") { puts "Float=" + float.to_s } while true do float = float.next_float endIn two terminals:
unix% ruby test_inc.rb Float=1.158144256e-315 Float=1.826497665e-315 Float=1.014102923e-314
unix% kill -USR1 3272 unix% kill -USR1 3272 unix% kill -USR1 3272
# Ruby from CBRAIN (simplified) task_list.each do |task| # iterates over each task, likely hundreds task.archive_work_dir() # this can take several minutes done
Sometimes, maintenance (or shutdowns) need to be performed while this process has already started.
If we kill this process at an arbitrary time, we will likely end up with:
# Ruby from CBRAIN (simplified) keep_going = true Signal.trap("TERM") { keep_going = false } task_list.each do |task| # iterates over each task, likely hundreds task.archive_work_dir() # this can take several minutes break if ! keep_going # break from the loop if signal received! done
This way we know that no task is left at an inconsistent state.
Puzzle: so how can this be even used for IPC ?
The end result is that a process has a capability to transfer a set of X=y values to their child processes.
Obviously, this communication mechanism is unidirectional: the information flows only from parents to their children.
Also, it can only be done once, just before the child process is created.
unix% export VISUAL=/bin/vim # how bash sets its own environment variables unix% git commit # will launch vim to edit commit message unix% export VISUAL=/bin/nano unix% git commit # now will launch nano to edit commit message
When run with no arguments:
unix% env ABC=def
This is a great way to inspect what are the environment variables currently set in your shell.
env A=b C=d E=f command arg1 arg2 arg3 ...
unix% env ABC=def unix% env TMPDIR=/home/prioux/tmp minc_scramble abcde_t1.mnc.gz minc scramble 1.0 : file abcde_t1.mnc.gz scrambled in /home/prioux/tmp/abcde_t1.mnc.gz unix% env ABC=def unix% export TMPDIR=/home/prioux/tmp # change this in SHELL level now unix% env ABC=def TMPDIR=/home/prioux/tmp
Name Role Example PATH dirs to search for executables (when running exec())
/bin:/usr/bin:/sbinLD_LIBRARY_PATH dirs to search for libraries
/lib:/usr/lib
Name Role Example VISUAL if set, what program to use to launch a visual text editor
/bin/vimEDITOR if set, what program to use to launch a text editor
/bin/edPAGER if set, what program to use to launch a pager
/usr/bin/lessDISPLAY X-window client programs get the socket to the X-window server
/tmp/socks-5/xwin:0LESS options for the pager less
MeqisXRF
unix% man less LESS(1) LESS(1) NAME less - opposite of more SYNOPSIS less -? less --help less -V (skipped through using 'more') ENVIRONMENT VARIABLES EDITOR The name of the editor (used for the v command). LANG Language for determining the character set. LESS Options which are passed to less automatically. (skipped through using 'more') unix% export PAGER=less # from now on I page using 'less' unix% export LESS=MeqisXRF # also, 'less' will default to these options
unix% mt -f /dev/nst0 status unix% mt -f /dev/nst0 rewind unix% mt -f /dev/nst0 fsf 3 unix% man mt # can I make this simpler?
unix% export TAPE=/dev/nst0 # seems the mt commands knows how to use this unix% mt status unix% mt rewind unix% mt fsf 3
Some are application-specific:e.g. the init.sh in the MNI Quarantine.
Some are used for user preferences or customizations:e.g. $HOME/.bashrc
In all cases, the script must be sourced, not executed.
unix% cat init.sh export ABC=def unix% bash init.sh # will not do anything: it's a distinct subprocess! unix% echo $ABC unix% source init.sh # affects current shell: it reads the lines itself unix% echo $ABC def
It used to be that a process could have a maximum of something like 64 FDs, nowadays it's more like 1024! Below: file descriptors for process mysqld on Ludmer server (excerpt)
COMMAND FD TYPE DEVICE SIZE/OFF NAME mysqld 0r CHR 1,3 0t0 /dev/null mysqld 1w REG 8,5 18042 /var/log/mysqld.log mysqld 2w REG 8,5 18042 /var/log/mysqld.log mysqld 3uW REG 8,5 1226833920 /var/lib/mysql/ibdata1 mysqld 4u REG 8,4 0 /tmp/ibHSKNG9 (deleted) mysqld 5u REG 8,4 473 /tmp/ibP57ycj (deleted) mysqld 6u REG 8,4 103 /tmp/ibLQzkIs (deleted) mysqld 7u REG 8,4 0 /tmp/ibfRYmeC (deleted) mysqld 8uW REG 8,5 5242880 /var/lib/mysql/ib_logfile0 mysqld 9uW REG 8,5 5242880 /var/lib/mysql/ib_logfile1 mysqld 10u IPv4 55802736 0t0 *:mysql (LISTEN) mysqld 11u REG 8,4 1467 /tmp/ibROAYLL (deleted) mysqld 12u unix 0xfff882ed80 0t0 /var/lib/mysql/mysql.sock mysqld 13u IPv4 61704300 0t0 192.168.122.1:mysql->ccna:48212 (ESTABLISHED) mysqld 15u REG 8,5 30720 /var/lib/mysql/wp_ludmer/wp_options.MYI mysqld 16u REG 8,5 580904 /var/lib/mysql/wp_ludmer/wp_options.MYD mysqld 37u REG 8,5 4096 /var/lib/mysql/canadachina/MMSE.MYI mysqld 38u REG 8,5 1704 /var/lib/mysql/canadachina/MMSE.MYD mysqld 52u REG 8,5 25300 /var/lib/mysql/wp_nist/wp_usermeta.MYD
they are opened and connected to a resource data is sent and/or received they are closed they can be reopened to another resource etc
What we typically call a file handle (FH) is often the product of a library or language providing an API to the file descriptors.
The most common one is the C stdio library, which works on structures that hide the file descriptors.
int fd; fd = open("/tmp/my_file",O_RDONLY);
File *fh; fh = fopen("/tmp/my_file","r");
They can be connected to files, the streams of other processes, terminals, etc, just like any other FDs.
# 'cat' command: copies all input to output unix% cat hello how are you? ^D hello how are you?
# 'echo' command: sends as string each argument, each separated by one space unix% echo hi how "are you" my,friend hi how are you my,friend
# 'tee' command: copies all input to output, and a copy to a separate file unix% tee myout hi there ^D hi there unix% cat myout hi there
# 'head' command: copies only the first N lines of of input to output unix% head -2 line 1 line 2 line 3 ^D line 1 line 2
# 'tail' command: copies only the last N lines of of input to output unix% tail -2 line 1 line 2 line 3 ^D line 2 line 3
# 'grep' command: select only the input lines matching a regex and sends them to output unix% grep pierre natacha drinks coffee but pierre eats chocolate and tristan writes code ^D but pierre eats chocolate
# 'sort' command: reads all lines, sorts them, then sends them to output unix% sort natacha drinks coffee but pierre eats chocolate and tristan writes code ^D and tristan writes code but pierre eats chocolate natacha drinks coffee
# 'cut' command: select part of lines and sends them to output unix% cut -d: -f2,4 pierre:rioux:le:fou alanis:morissette:the:singer justin:trudeau:le:politicien ^D rioux:fou morissette:singer trudeau:politicien
# 'uniq' command: removes successive duplicate lines and sends them to output unix% uniq hello hello goodbye hello ^D hello goodbye hello
# 'gzip' command: compress input stream and sends the compressed result to output unix% gzip pierre eats chocolate ^D $@~Fz-#@0
unix% cat hello how are you? ^D hello how are you?
unix% cat file line 1 of 'file' line 2 of 'file' line 3 of 'file'
# A sample (dummy) UNIX password file prioux:x:1037:1037:Pierre Rioux:/home/prioux:/bin/bash mero:x:1047:1047:Marc Rousseau:/home/mero:/bin/bash sdas:x:1055:1055:Samir Das:/home/sdas:/bin/tcsh nbeck:x:1055:1055:Natacha Beck:/home/sdas:/bin/sh tglat:x:1055:1055:Tristan Glatard:/home/sdas:/bin/tcshFind all users of the tcsh shell:
unix% cat /etc/passwd | grep /bin/tcsh | cut -d: -f5 Samir Das Tristan GlatardSo, what do grep and cut correspond to?
grep : the where clauses of a SQL select statement cut : the select `columns` part.
unix% program 0< file # fd 0 = stdin unix% program < file
unix% program 1> file # fd 1 = stdout unix% program > file
unix% program 2> file # fd 2 = stderr
unix% program 0< abc 1> def 2> xyz
unix% wc -l memoirs.txt 7233
unix% cat memoirs.txt | wc -l 7233
unix% ls -l abc.txt def.txt def.txt: no such file or directory -rwxr-xr-x prioux wheel 12311 abc.txt unix% ls -l abc.txt def.txt > out 2> err unix% ls -l abc.txt def.txt | wc -l def.txt: no such file or directory 1 unix% ls -l abc.txt def.txt 2>&1 | wc -l 2
Unlike a barrel of water, the data's I/O order is kept.
There is however a system call that seems promising:
The solution is for the process to fork() after pipe()
Then, each process decides to close() one of their file descriptors.
To send data from parent to child:
To send data from child to parent:
#!/bin/perl # Standard perl functions; no Perl libs, no strict, no nothin' pipe(READFD,WRITEFD); if (fork) { # this block is the parent process close(READFD); print WRITEFD "Good morning my son"; print "I am parent process $$ and I exit now.\n"; exit 0; } else { # this block is the child process close(WRITEFD); $a=<READFD>; print "I am child process $$ and I read '$a' from my parent. Exiting now.\n"; exit 0; }
unix% perl pipe.pl I am parent process 24891 and I exit now. I am child process 24892 and I read 'Good morning my son' from my parent. Exiting now.
fh=popen("ls -l","r");
open(FH,"ls -l|"); # or my $fh = IO::File->new("ls -l|");
$fh = popen("ls -l","r");
fh = IO.popen("ls -l","r")
An example is tar -czf out.tar.gz somedir
The tar command does not implement compression. It delegates this to a gzip child process that it pipes to.
unix% tar -czf /tmp/dummy.tar.gz MainStore & [1] 28976
unix% pstree -p -a -c 28976 tar,28976 -czf /tmp/dummy.tar.gz MainStore | +--gzip,28977
unix% lsof -w -b -p 28976 COMMAND PID FD TYPE NAME tar 28976 0u CHR /dev/pts/0 tar 28976 1u CHR /dev/pts/0 tar 28976 2u CHR /dev/pts/0 tar 28976 3r REG /data/MainStore/32/69/00/01221_2_t1.mnc.gz tar 28976 4w FIFO pipe
unix% lsof -w -b -p 28977 COMMAND PID FD TYPE NAME gzip 28977 0r FIFO pipe gzip 28977 1w REG /tmp/dummy.tar.gz gzip 28977 2u CHR /dev/pts/0
Instead of establishing a registry, or adding new system calls, the UNIX designers chose the filesystem as a meeting place.
unix% mkfifo abcde # on some system it's mknod unix% ls -l abcde prw-r--r-- 1 prioux staff 0 Jul 27 21:46 abcde
Except, the data is not going to disk at all!
unix% mkfifo robot.pdf # this file is neither a robot nor a PDF! unix% ls -l robot.pdf prw-r--r-- 1 prioux staff 0 Jul 28 10:52 robot.pdf|
In two separate shells, side by side:
unix% echo "Ceci n'est pas une pipe" > robot.pdf
unix% cat < robot.pdf Ceci n'est pas une pipe
They differ in how the data is transported and how the processes find themselves.
That presentation is available in the Documents tab of the ACELab Redmine tracker, in project ACELab IT.
Setting: We had a UNIX machine with small disk space. Largest partition: 80G dedicated to the LORIS development MySQL DBs.
unix% mysqldump [options] BIGDB > file.sql
unix% mysqldump [options] BIGDB | gzip -c | ssh me@remote.host 'cat > dump.sql.gz'Using what you all collectively know about UNIX:
how many processes are involved? which processes are they? which IPC mechanisms are used?
unix% mysqldump [options] BIGDB | gzip -c | ssh me@remote.host 'cat > dump.sql.gz'Let's identify the processes.
The easy ones: the processes we see on the command line:
They run on two different computers, right?
unix% mysqldump [options] BIGDB | gzip -c | ssh me@remote.host 'cat > dump.sql.gz'
They were are all started by some other processes, right? And what about the DB server process itself?
Note: two sshd processes are in fact involved...
unix% mysqldump [options] BIGDB | gzip -c | ssh me@remote.host 'cat > dump.sql.gz'
Now let's draw the connections to files. These are standard file operations, not IPC.
unix% mysqldump [options] BIGDB | gzip -c | ssh me@remote.host 'cat > dump.sql.gz'OK now, what are all the IPC mechanisms operating between these processes?
Recap: Signals, Environment Variables, Anonymous Pipes, Named Pipes, Network Sockets, UNIX-Domain Sockets
unix% mysqldump [options] BIGDB | gzip -c | ssh me@remote.host 'cat > dump.sql.gz'
Presented in four parts: June 2016 - October 2016
I'm willing to present again any previous parts of this presentation, for those who have missed them. (This offer includes the GIT, the File system, and the Networking presentations too)