- 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
}