- try to use 'bg' instead of 'kill -cont' in ash
- if 'bg' doesn't work on non-child/non-job processes, 'trap' 'SIGSTOP' to run 'wait <wait_process>' and 'trap' 'SIGCONT' to 'kill -kill <wait_process>' in ash
- test if we can run multiple processes and make them echo on the screen concurrently but in different region
- Inter-process interactions: IPCs, shared resources
- On machines with multiple processors/cores, several processes/threads may run concurrently!
- Lock in the process being stopped/scheduled and unlock in the other processes? (using condition variables)
- Send SIGSTOP to all other processes accessing the message queue instead of locking them to avoid deadlock issues? (use the 3rd field of /proc/<pid>/stat to make sure a process is really stopped)
- Use message queue of size 1 byte to simulate inter-process condition variable is that possible?
- Understand the meaning of 'export' (such as 'export PATH') shell built-in: may it be used to exchange data between processes instead of files?
#!/bin/ash ## Inter-process interactions: ## . . ## . . ## . . ## Subdivide each statement/function call until each division becomes an atomic ## operation. ## Adding Sleep() between each instruction can outstand race condtion issues? ## !! There is interval between @a1@ and @a2@ such that other process would ## interfere the execution within the interval. NewTmpDir() { local Node while :; do Node=$RANDOM if [ ! -e /tmp/${Node} ]; then # @a1@ mkdir /tmp/${Node} # @a2@ eval "${1}=/tmp/${Node}" return fi done } ## s=a ## s=\$$s ## s=`eval "echo $s"` ## s=\$$s ## s=`eval "echo $s"` ## . ## . ## . DeRef() { DeRefRes=\$$1 DeRefRes=`eval "echo $DeRefRes"` } ## Atomically exchange contents of variables indicated by $1 and the spinlock. SpinAtomicXchg() { local Temp flock -x 8 DeRef $1 Temp="$DeRefRes" SpinLock_GetVar $1 SpinLock_SetVar $Temp flock -u 8 } ## Set the PID of the calling process to the variable named in the first argument. GetPid() { local Stat Path Path=`pwd` cd /proc/self Stat=`cat stat` cd "$Path" eval "${1}=${Stat%% *}" } ContinueNoLock() { kill -CONT $1 } StopNoLock() { kill -STOP $1 } Continue() { SpinLock_Lock ContinueNoLock $1 SpinLock_Unlock } Stop() { SpinLock_Lock StopNoLock $1 SpinLock_Unlock } IsStopped() { local Stat Stat=`cat /proc/${1}/stat` Stat=${Stat#* * } Stat=${Stat%% *} test "$Stat" = 'T' } TempUnlockProc() { while :; do if IsStopped $1; then break; fi done SpinLock_Unlock } GetMessage() { SpinLock_Lock if MsgQu_IsEmpty ...; then TempUnlockProc $Pid & StopNoLock $Pid # Self stopping. fi ## ...retrive one message and set to var indicated by $1... } ## $1:Pointer to MsgQu $2:Message $3:Target PID SendMessage() { local Wake SpinLock_Lock if MsgQu_IsFull "$1"; then TempUnlockProc ... & ## Add self to wait queue? StopNoLock ... # Self stopping. fi MsgQu_IsEmpty "$1" Wake=$? echo "$2" > "${1}/`cat \"${1}/End\"`" echo $((`cat "${1}/End"` + 1)) > "${1}/End" [ $Wake -eq 0 ] && ContinueNoLock $3 SpinLock_Unlock } ## {{ SpinLock SpinLock_Init() { if [ ! -e "$SpinLockVar" ]; then SpinLock_SetVar 0 fi exec 8> "$GlobalLock" } SpinLock_SetVar() { echo "$1" > "$SpinLockVar" } SpinLock_GetVar() { eval $1=`cat "$SpinLockVar"` } ## We should shorten the lock time as we can, becasue processes waiting on a ## spin lock are essentially doing busy waiting which consume CPU resource. SpinLock_Lock() { local Lock Lock=1 while :; do SpinAtomicXchg Lock if [ $Lock = 0 ]; then break; fi done } SpinLock_Unlock() { local Lock Lock=0 ## Atomic operation in case lock variable value alternation is not atomic. ## Unlike mutex, a spin lock may be locked in a process and unlocked in the ## other process. SpinAtomicXchg Lock } ## }} SpinLock ## {{ Proc ## $1:'' $2:Pointer to sys $3:Varable to receive 'this' pointer Proc_Init() { local Pid GetPid Pid set -- "${2}/${Pid}" "$2" "$3" # Set positional parameters. mkdir "$1" # Self allocation. eval "${3}=${1}" mkdir "${1}/MsgQu" MsgQu_Init "${1}/MsgQu" } Proc_Xxx() { local This MessageDir Message Proc_Init "$1" "$2" This set -- "$This" "$2" ## ... while GetMessage Message; do ProcessMessage $Message done ## ... } ## }} Proc ## {{ MsgQu MsgQu_Init() { echo 0 > "${1}/Begin" echo 0 > "${1}/End" } MsgQu_IsEmpty() { [ "`cat \"${1}/Begin\"`" = "`cat \"${1}/End\"`" ] } MsgQu_IsFull() { ... } ## }} MsgQu ## {{ Sys Sys_Init() { Proc_Init ... } ## }} Sys NewTmpDir Root Sys_Init "$Root" GlobalLock="${Root}/Global.lock" SpinLockVar="${Root}/SpinLock.var" # Locked:1 unlocked:0 This="${Root}/${Pid}" MessageQueue="${This}/MessageQueue" SpinLock_Init Proc_Init MsgQu_Init ## ...
#!/bin/ash ## Inter-process interactions: ## Subdivide each statement/function call until each division becomes an atomic operation ## Adding Sleep() between each instruction can outstand race condtion issues? ## s=a ## s=\$$s ## s=`eval "echo $s"` ## s=\$$s ## s=`eval "echo $s"` ## . ## . ## . DeRef() { DeRefRes=\$$1 DeRefRes=`eval "echo $DeRefRes"` } SpinSetLockVar() { echo "$1" > "$SpinLockVar" } SpinGetLockVar() { eval $1=`cat "$SpinLockVar"` } SpinInit() { if [ ! -e "$SpinLockVar" ]; then SpinSetLockVar 0 fi exec 8> "$GlobalLock" } ## Atomically exchange contents of variables indicated by $1 and the spinlock. SpinAtomicXchg() { local Temp flock -x 8 DeRef $1 Temp="$DeRefRes" SpinGetLockVar $1 SpinSetLockVar $Temp flock -u 8 } ## We should shorten the lock time as we can, becasue processes waiting on a spin lock are essentially doing busy waiting which consume CPU resource. SpinLock() { local Lock Lock=1 while :; do SpinAtomicXchg Lock if [ $Lock = 0 ]; then break; fi done } SpinUnlock() { local Lock Lock=0 SpinAtomicXchg Lock # Atomic operation in case lock variable value alternation is not atomic. Unlike mutex, a spin lock may be locked in a process and unlocked in the other process. } ## Set the PID of the calling process to the variable named in the first argument. GetPid() { local Stat Path Path=`pwd` cd /proc/self Stat=`cat stat` cd "$Path" eval "$1=${Stat%% *}" } ContinueNoLock() { kill -CONT $1 } StopNoLock() { kill -STOP $1 } Continue() { SpinLock ContinueNoLock $1 SpinUnlock } Stop() { SpinLock StopNoLock $1 SpinUnlock } IsStopped() { local Stat Stat=`cat /proc/${1}/stat` Stat=${Stat#* * } Stat=${Stat%% *} test "$Stat" = 'T' } TempUnlockProcess() { while :; do if IsStopped $1; then break; fi done SpinUnlock } GetMessage() { SpinLock if IsDirEmpty "$MessageDir"; then TempUnlockProcess $Pid & StopNoLock $Pid # Self stopping. fi ## ...retrive one message and set to var indicated by $1... } ##SendMessage() ##{ ##} TaskInit() { local Pid GetPid Pid mkdir "$This" > /dev/null 2>&1 } MessageQueueInit() { mkdir "$MessageQueue" > /dev/null 2>&1 echo 0 > "${MessageQueue}/Begin.var" echo 0 > "${MessageQueue}/End.var" } TaskXxx() { local Pid MessageDir Message GetPid Pid MessageDir="${Root}/${Pid}" ## ... while GetMessage Message; do ProcessMessage $Message done ## ... } local Root GlobalLock SpinLockVar Root=/tmp/mtbv GlobalLock="${Root}/Global.lock" SpinLockVar="${Root}/SpinLock.var" # Locked:1 unlocked:0 This="${Root}/${Pid}" MessageQueue="${This}/MessageQueue" SpinInit TaskInit MessageQueueInit ## ...
#!/bin/ash exec 8> /tmp/mtbv/aaa # In shell 1 flock -x 8 # In shell 1 (flock -x 8;) 8> /tmp/mtbv/aaa # In shell 2, and shell 2 is blocked flock -u 8 # In shell 1, and shell 2 is unblocked
#!/bin/ash exec 8> /tmp/mtbv/aaa # In shell 1 flock -x 8 # In shell 1 (flock -x 8;) 8> /tmp/mtbv/aaa # In shell 2, and shell 2 is blocked exec 8> /dev/null # In shell 1, and shell 2 is unblocked
#!/bin/ash ## Adding Sleep() between each instruction can outstand race condtion issues? ## s=a ## s=\$$s ## s=`eval "echo $s"` ## s=\$$s ## s=`eval "echo $s"` ## . ## . ## . DeRef() { DeRefRes=\$$1 DeRefRes=`eval "echo $DeRefRes"` } InitAtomicXchg() { exec 8> "$GlobalLock" } ## Atomically exchange contents of variables indicated by $1 and $2. AtomicXchg() { flock -x 8 DeRef $1 Temp="$DeRefRes" DeRef $2 eval "$1=$DeRefRes" eval "$2=$Temp" flock -u 8 } ## We should shorten the lock time as we can, becasue processes waiting on a spin lock are essentially doing busy waiting which consume CPU resource. SpinLock() { local Lock Lock=1 while :; do AtomicXchg Lock $1 if [ $Lock = 0 ]; then break; fi done } SpinUnlock() { local Lock Lock=0 AtomicXchg Lock $1 # Atomic operation in case lock variable value alternation is not atomic. Unlike mutex, a spin lock may be locked in a process and unlocked in the other process. } ## Set the PID of the calling process to the variable named in the first argument. GetPid() { local Stat Path Path=`pwd` cd /proc/self Stat=`cat stat` cd "$Path" eval "$1=${Stat%% *}" } ContinueNoLock() { kill -CONT $1 } StopNoLock() { kill -STOP $1 } Continue() { SpinLock SpinLockVar ContinueNoLock $1 SpinUnlock SpinLockVar } Stop() { SpinLock SpinLockVar StopNoLock $1 SpinUnlock SpinLockVar } IsStopped() { local Stat Stat=`cat /proc/${1}/stat` Stat=${Stat#* * } Stat=${Stat%% *} test "$Stat" = 'T' } TempUnlockProcess() { while :; do if IsStopped $1; then break; fi done SpinUnlock SpinLockVar } GetMessage() { SpinLock SpinLockVar if IsDirEmpty "$MessageDir"; then TempUnlockProcess $Pid & StopNoLock $Pid # Self stopping. fi ## ...retrive one message and set to var indicated by $1... } TaskXxx() { local Pid MessageDir Message GetPid Pid MessageDir="${Root}/${Pid}" ## ... while GetMessage Message; do ProcessMessage $Message done ## ... } local Root GlobalLock SpinLockVar Root=/tmp/mtbv GlobalLock="${Root}/global.lock" SpinLockVar=0 # Locked:1 unlocked:0 InitAtomicXchg ## ...
#!/bin/ash ## Adding Sleep() between each instruction can outstand race condtion issues? ## Set the PID of the calling process to the variable named in the first argument. GetPid() { local Stat Path Path=`pwd` cd /proc/self Stat=`cat stat` cd "$Path" eval "$1=${Stat%% *}" } ContinueNoLock() { kill -CONT $1 } StopNoLock() { kill -STOP $1 } Continue() { ( flock -x 8 ContinueNoLock $1 ) 8> $GlobalLock } Stop() { ( flock -x 8 StopNoLock $1 ) 8> $GlobalLock } IsStopped() { local Stat Stat=`cat /proc/${1}/stat` Stat=${Stat#* * } Stat=${Stat%% *} test "$Stat" = 'T' } PostUnlock() { while :; do if IsStopped $1; then break; fi done Unlock $GlobalLock } GetMessage() { Lock $GlobalLock if IsDirEmpty "$MessageDir"; then PostUnlock $Pid & StopNoLock $Pid # Self stopping. fi ## ...retrive one message and set to var indicated by $1... } TaskXxx() { local Pid MessageDir Message GetPid Pid MessageDir="${Root}/${Pid}" ## ... while GetMessage Message; do ProcessMessage $Message done ## ... } local Root Root=/tmp/mtbv GlobalLock="${Root}/global.lock"
#!/bin/ash ## Set the PID of the calling process to variable named in the first argument. m() { local s p p=`pwd` cd /proc/self s=`cat stat` cd "$p" eval "$1=${s%% *}" } ## Self stopping test function. p() { local i echo ...beg m i echo $i kill -stop $i echo ...end }
#!/bin/bash w() { echo '...beg w...' while :; do sleep 60; done echo '...end w...' } c() { echo '...beg c...' echo '...end c...' } s() { echo '...beg s...' wait $ttt # 'wait' will return (similar to WAIT(2) returns 'EINTR') when interrupted by signal like USR1 or c()? echo '...end s...' } p() { echo '...beg p...' w & ttt=$! trap c USR1 # Default handler terminates the program, so we make a dummy handler (do nothing) to avoid the termination trap s USR2 while :; do echo -n '^' sleep 0.05 done kill $ttt echo '...end p...' } p & echo $! ## Use 'kill -USR2 $!' to stop/suspend p() and use 'kill -USR1 $!' to continue/resume p()
#!/bin/ash ## Make text positioning and text printing done by a single 'echo' call, which ## should reduce/eliminate the race condition when this function is called ## within multiple processes. ## ## This function doesn't add newline at the end of the output. ## ## Notice that 'echo' don't accept '-e' option in 'sh' and 'ash'. echo_at() { local str str="\033[${1}H" if [ "$2" -gt 1 ]; then str="${str}\033[$(($2 - 1))C${3}" else str="${str}${3}" fi echo -n "$str" }
## In one of terminal window (flock -x 8; sleep 200;) 8> /tmp/kkk ## In the other terminal window (flock -x 7; echo ---why---;) 7> /tmp/kkk
#/bin/bash g() { while :; do echo -e "\033[${1}H" sleep `echo "0.$RANDOM"` done } echo_at() { echo -e "\033[${1}H" sleep .1 if [ $2 -gt 1 ]; then echo -e "\033[$(($2 - 1))C${3}" else echo -e "$3" fi } p() { while :; do echo_at "$1" "$2" " <$RANDOM> " sleep `echo "0.$RANDOM"` done } p 23 60 & g 1 & g 2 & g 3 & g 4 & g 5 & g 6 & g 7 & g 8 & g 9 & g 10 & g 11 & g 12 &
#/bin/bash echo_at() { echo -e "\033[${1}H" if [ $2 -gt 1 ]; then echo -e "\033[$(($2 - 1))C${3}" else echo -e "$3" fi } p() { while :; do echo_at "$1" "$2" " <$RANDOM> " sleep `echo "0.$RANDOM"` done } p() { iii=1 while :; do echo_at "$1" "$2" " <$iii> " iii=$(($iii + 1)) sleep `echo "0.$RANDOM"` done } ## (progn ## (setq i 1) ## (while (<= i 21) ## (insert (format "p %d 60 & " i)) ## (setq i (1+ i)) ## ) ## ) p 1 60 & p 2 60 & p 3 60 & p 4 60 & p 5 60 & p 6 60 & p 7 60 & p 8 60 & p 9 60 & p 10 60 & p 11 60 & p 12 60 & p 13 60 & p 14 60 & p 15 60 & p 16 60 & p 17 60 & p 18 60 & p 19 60 & p 20 60 & p 21 60 & ##ttt=$(echo '$ttt $!')
#!/bin/ash w() { echo '...beg w...' while :; do sleep 60; done echo '...end w...' } c() { echo '...beg c...' echo $ttt kill $ttt echo '...end c...' } s() { echo '...beg s...' w & ttt=$! echo $ttt wait $ttt echo '...end s...' } p() { echo '...beg p...' trap c USR1 trap s USR2 while :; do echo '^' sleep 1 done echo '...end p...' } ##### w() { echo '...beg w...' while :; do sleep 60; done echo '...end w...' } c() { echo '...beg c...' echo $ttt kill $ttt echo '...end c...' } s() { echo '...beg s...' w & ttt=$! echo $ttt wait $ttt echo '...end s...' } p() { echo '...beg p...' trap c USR1 trap s USR2 w & while :; do wait $! done echo '...end p...' } ##### c() { echo '..beg c...' echo $ttt echo '..end c...' } w() { echo '..beg w...' while :; do sleep 60; done echo '..end w...' } p() { echo '..beg p...' w & ttt=$! echo "ttt=$ttt" trap c CONT w & while :; do wait $! done echo '..end p...' } ##### e() { echo '..beg e...' echo '...trap...' echo '..end e...' } w() { echo '..beg w...' ping localhost > /dev/null echo '..end w...' } p() { echo '..beg p...' trap e CONT w & while :; do wait $! done echo '..end p...' } ##### w() { ping localhost > /dev/null } p() { trap e CONT w } p() { trap '-' CONT w } ##### w() { while :; do sleep 1 done } e() { echo '...trap...' } p() { trap e USR1 w }