如何在Linux上设立一个软IOC框架
以下说明是基于我们的Debian Linux机器。其它发行版(或其他Unixes)会有不同命令和对于东西有不同位置。这对我添加到本页的Debian /etc/init.d尤其如此。如果你为一个不同发行版创建一个不同的脚本,请添加它到本页。其它人将能够使用它。但基本步骤在所有发行版上相同。我已经认为了解了基本的系统管理任务(创建用户账户等)。
介绍
我们为什么做这件事?
当在生产中使用软IOC时,它们应该被当作重要的系统服务:
- 软IOCs应该由系统启动和停止。
- 应该有后备系统,遇到硬件故障,你可以简单地切换到这个后备系统。
其它目标是:用如用于VME IOCs相同地方法,应用程序开发这应该能够在无需对主机进行root访问下重置软IOC。
- IOC应用开发者应该能够手动启动和停止IOCs。
当多个软IOC共享相同主机(和相同IP地址)时,通道访问不能区分它们。访问安全将不能区分来自不同软IOCs的CA连接。在调试客户端时,CA不能告诉你一个连接去向哪个软IOCs。
- 即使不同软IOCs驻留在相同机器上,通道访问应该能够区分不同的软IOCs。
我曾考虑使用一个虚拟化层(基于VMware)在一个封装环境中运行软IOCs。我发现工作量太高,此层太厚,并且预计的性能损失太大--仅得到了每个软IOIC一个单独IP地址。
在调试和/或尝试查看在一个IOC上发生了什么时,开发者不必要知道数据库运行在一个基于VME上还是在一个基于主机的软IOC上。
- Console访问(以及记录console输出日志)应该是统一的:对于软IOCs和VME IOCs使用相同方式。
实现这个目标所需的设置在文档<<如何为VME和软IOCs设置Console访问和日志>>。
概念
要使访问安全区分软IOCs,在不同用户名下运行它们。
procServ工具将用做一个环境,它允许在后台启动软IOCs并且之后连接到它们的consoles,非常类型VME IOCs的串行consoles。(见EPICS Related Software — EPICS Documentation documentation (epics-controls.org)上procServ链接。之前,使用screen工具,但报告的问题,例如,在console访问后IOCs挂起,使得我们切花到复杂性更低的东西)。
/etc/init.d/softIOC是用于将EPICS软IOC作为系统服务启动的脚本:参考网页https://wiki-ext.aps.anl.gov/epics/index.php/How_to_Set_Up_a_Soft_IOC_Framework_on_Linux
这个脚本的描述:
DESC="EPICS soft IOCs"
这个脚本的完整路径:
SCRIPTNAME=/etc/init.d/softIOC
获取主机名称:
HOST=`uname -n`
设置procServ程序的路径
PROCSERV=/usr/bin/procServ
设置配置文件的完整路径:
CONFFILE=/usr/local/EPICS/program/softIOC/softiocs.orangepi5plus
设置IOC应用程序所在的家目录:
HOMEDIRS=/usr/local/EPICS/program
检查配置文件 , 不能读取配置文件,显示不能找到配置文件,并且以错误码1退出本脚本。
if [ ! -r $CONFFILE ]
then
echo "Error: Can't find configuration file $CONFFILE!"
exit 1
fi
此函数函数:清理环境变量"CA_AUTO" "CA_ADDR" "CA_PORT" "IOC_USER" "PORT"。
clear_options()
{
for option in "CA_AUTO" "CA_ADDR" "CA_PORT" "IOC_USER" "PORT"
do
unset $option;
done
}
clear_options()的测试脚本如下:
echo "=============test clear_options() ====================="
export CA_AUTO="NO"
export CA_ADDR="127.0.0.1"
export CA_PORT=5065
export IOC_USER="blctrl"
export PORT="20000"
echo "CA_AUTO=$CA_AUTO"
echo "CA_ADDR=$CA_ADDR"
echo "CA_PORT=$CA_PORT"
echo "IOC_USER=$IOC_USER"
echo "PORT=$PORT"
echo "call clear_options()"
clear_options
echo "called clear_options()"
echo "CA_AUTO=$CA_AUTO"
echo "CA_ADDR=$CA_ADDR"
echo "CA_PORT=$CA_PORT"
echo "IOC_USER=$IOC_USER"
echo "PORT=$PORT"
echo "===================end================================="
echo ""
测试结果:
=============test clear_options() =====================
CA_AUTO=NO
CA_ADDR=127.0.0.1
CA_PORT=5065
IOC_USER=blctrl
PORT=20000
call clear_options()
called clear_options()
CA_AUTO=
CA_ADDR=
CA_PORT=
IOC_USER=
PORT=
===================end=================================
根据读入参数设置环境变量:
evaluate_options()
{
while [ $# != 0 ]
do # 将第一个参数的小写字符全部替换成大写字母
TAG=`echo $1 | tr [:lower:] [:upper:]`
case "$TAG" in
"#") ;; # 匹配”#”,则进入下次循环
"CA_AUTO" | "CA_ADDR" | "CA_PORT" | "COREDUMPSIZE" | \
"HOMEDIR" | "BOOTDIR" | "IOC_USER" | "PORT" ) # 匹配这些字符
# 测试当前选项值的存在
OPTION=$TAG
shift
if [ -z $TAG -o $TAG = "#" ] # TAG变量为空或者内容为”#”
then
echo "$CONFFILE: Value(s) required for $TAG.";
exit 1
else
VALUE=$1
shift
fi
# 如果多个值跟随,也分配它们
while [ $1 != '#' -a $# != 0 ]
do
VALUE="$VALUE $1"
shift;
done
eval ${OPTION}=\$VALUE
;;
*) echo "$CONFFILE: Unknown option $1."
exit 1
esac
shift
done
}
测试evaluate_options()函数的脚本:
echo "============================Test evaluate_options============================="
ARGS="CA_AUTO NO # CA_ADDR 127.0.0.1 # CA_PORT 5065 5066 # IOC_USER blctrl # PORT 20000"
evaluate_options ${ARGS}
echo CA_AUTO=$CA_AUTO
echo CA_ADDR=$CA_ADDR
echo CA_PORT=$CA_PORT
echo IOC_USER=$IOC_USER
echo PORT=$PORT
echo "============================Test evaluate_options==============================="
测试结果如下:
============================Test evaluate_options=============================
CA_AUTO=NO
CA_ADDR=127.0.0.1
CA_PORT=5065 5066
IOC_USER=blctrl
PORT=20000
============================Test evaluate_options===============================
为选项设置IOC环境变量默认值可能在配置文件中被重写:
- BOOTDIR:IOC启动目录。
- HOMEDIR:IOC应用程序顶层目录
- PIDFILE:生产的pid文件路径
- ENVFILE:环境文件路径
- IOC_USER:IOC应用程序的用户
default_options()
{
IOC_LC=$1
BOOTDIR=$HOMEDIRS/$LOC_LC/iocBoot/ioc$LOC_LC/
HOMEDIR=$HOMEDIRS/$IOC_LC
PIDFILE=$HOMEDIR/$IOC_LC.pid
ENVFILE=$HOMEDIR/$IOC_LC.env
IOC_USER=blctrl
}
default_options()的测试脚本如下:
#echo "===================Test default_options()=============="
default_options "aiDriver"
echo "BOOTDIR=$BOOTDIR"
echo "HOMEDIRS=$HOMEDIRS"
echo "HOMEDIR=$HOMEDIR"
echo "PIDFILE=$PIDFILE"
echo "IOC_USER=blctrl"
#echo "===================end================================="
#echo ""
测试结果如下:
BOOTDIR=/usr/local/EPICS/program/aiDriver/iocBoot/iocaiDriver/
HOMEDIRS=/usr/local/EPICS/program
HOMEDIR=/usr/local/EPICS/program/aiDriver
PIDFILE=/usr/local/EPICS/program/aiDriver/aiDriver.pid
IOC_USER=blctrl
从配置文件中读取配置信息,并且生产一个如以下格式的
assign_options()
{
TAG=$1
SECTION=`sed -n "/^$TAG:/I,/^[\t ]*$/p" $CONFFILE | \ # 查找$TAG段落
sed -n '/^[^#]/p' | \ # 删除注释
sed -e 's/^[ \t]*//' -e 's/[ \t]*$//' \ # 删除开头和结尾空白
-e "s/$TAG://I" \ # 移除$TAG:标签
-e :a -e '/\\\\$/N; s/\\\\\\n//; ta' \ # 将以一个”\”结尾的行合并
-e 's/$/ \#/' \ # 用一个”#”标记选项结尾
-e 's/[\t ]/ /g'` #移除不必要的空白
evaluate_options $SECTION
}
assign_options的测试脚本如下:
echo "=================Test assign_options()================="
assign_options aiDriver
echo SECTION=$SECTION
echo PORT=$PORT
echo CA_AUTO=$CA_AUTO
echo CA_ADDR =$CA_ADDR
echo CA_PORT =$CA_PORT
echo IOC_USER =$IOC_USER
echo "===================end================================="
echo ""
配置文件所在完整路径/usr/local/EPICS/program/softIOC/softiocs.orangepi5plus:
AUTO: aiDriver
GLOBAL:
aiDriver:
# 配置端口号
PORT 20000
CA_AUTO NO
CA_ADDR 192.168.50.10
CA_PORT 5080
IOC_USER blctrl
PORT 20000
测试结果如下:
=================Test assign_options()=================
SECTION= # PORT 20000 # CA_AUTO NO # CA_ADDR 192.168.50.10 # CA_PORT 5080 # IOC_USER blctrl # PORT 20000 #
PORT=20000
CA_AUTO=NO
CA_ADDR =192.168.50.10
CA_PORT =5080
IOC_USER =blctrl
从命令行或配置文件中AUTO:项获取IOCs。测试配置文件中的匹配段落。
get_iocs()
{
if [ $# = 0 ]
then
TEST_LIST=`grep -i '^AUTO:' "$CONFFILE" | cut -d: -f2-`
else
TEST_LIST="$@"
fi
CHECKED_LIST=""
for IOC in $TEST_LIST
do
grep -qi "^$IOC:" $CONFFILE
if [ $? = 0 ]
then
CHECKED_LIST="$CHECKED_LIST $IOC"
fi
done
echo $CHECKED_LIST
}
get_iocs()的测试脚本如下:
echo "===============Test get_iocs()========================="
echo "call get_iocs"
get_iocs
echo "call get_iocs aiDriver"
get_iocs aiDriver
echo "===================end================================="
echo ""
测试结果如下:
===============Test get_iocs()=========================
call get_iocs
aiDriver
call get_iocs aiDriver
aiDriver
===================end=================================
此脚本第一方面设置环境变量设置字符串,例如CA_AUTO环变量不为空,则将其设置到环境变量,其它CA_XXX环境变量的设置类似,第二方面为procserv程序设置选项。
set_cmdenvopts()
{
EPICS_CA_AUTO_ADDR_LIST中,并且进行导出
SETENV="LINES=60 "`test ! -z "$CA_AUTO" && echo "export EPICS_CA_AUTO_ADDR_LIST=\"$CA_AUTO\";"`
SETENV="$SETENV "`test ! -z "$CA_ADDR" && echo "export EPICS_CA_ADDR_LIST=\"$CA_ADDR\";"`
SETENV="$SETENV "`test ! -z "$CA_PORT" && echo "export EPICS_CA_SERVER_PORT=\"$CA_PORT\";"`
SETENV="$SETENV "`test ! -z "$BOOTDIR" && echo "export BOOTDIR=\"$BOOTDIR\";"`
PROCSERVOPTS=`test ! -z "$IOC_USER" && echo "-n \"$IOC_USER\""`
PROCSERVOPTS="$PROCSERVOPTS "`test ! -z "$COREDUMPSIZE" && echo "--coresize \"$COREDUMPSIZE\""`
PROCSERVOPTS="$PROCSERVOPTS -q -c $BOOTDIR -p $PIDFILE -i ^D^C^] $PORT"
}
set_cmdenvpots()的测试脚本如下:
echo "================Test set_cmdenvopts()=================="
default_options aiDriver
assign_options aiDriver
set_cmdenvopts
echo "SETENV=$SETENV"
echo "PROCSERVOPTS=$PROCSERVOPTS"
echo "===================end================================="
echo ""
测试结果如下:
SETENV变量内容可以设置以下变量:
- LINES=60
- export EPICS_CA_AUTO_ADDR_LIST="NO"
- export EPICS_CA_ADDR_LIST="127.0.0.1"
- export EPICS_CA_SERVER_PORT="5080";
- export BOOTDIR="/usr/local/EPICS/program/aiDriver/iocBoot/iocaiDriver/"
PROCSERVPORTS内容可以设置procserv的选项:
- -n "blctrl" :在所有服务程序消息中,使用blctrl替代完整命令行来增加可读性。
- -q:不要写信息输出(服务程序)。当作为系统脚本的一部分运行时,避免弄乱屏幕。
- -c /usr/local/EPICS/program/aiDriver/iocBoot/iocaiDriver/:在启动子进程前,切换目录为 /usr/local/EPICS/program/aiDriver/iocBoot/iocaiDriver/。
- -p /usr/local/EPICS/program/aiDriver/aiDriver.pid:写服务进程的PID到file以便于集成到常规的系统服务管理机制中。
- -i ^D^C^]:忽略访问连接上再chars中所有字符。
- 20000:本地ftp连接端口。
================Test set_cmdenvopts()==================
SETENV=LINES=60 export EPICS_CA_AUTO_ADDR_LIST="NO"; export EPICS_CA_ADDR_LIST="127.0.0.1"; export EPICS_CA_SERVER_PORT="5080"; export BOOTDIR="/usr/local/EPICS/program/aiDriver/iocBoot/iocaiDriver/";
PROCSERVOPTS=-n "blctrl" -q -c /usr/local/EPICS/program/aiDriver/iocBoot/iocaiDriver/ -p /usr/local/EPICS/program/aiDriver/aiDriver.pid -i ^D^C^] 20000
===================end=================================
启动守护/服务进程的函数:
do_start()
{
# 返回
# 0:启动了守护进程
# 1:守护进程已经在运行
# 2:不能启动守护进程
# 如果需要,在此处添加代码,其等待这个进程准备好处理来自之后启动的服务的请求,这个服务依赖于这个进程。作为最后的方法,休眠一段时间。
echo -n "Starting soft IOCs ... "
MYIOCS=`get_iocs $@`
[ "$MYIOCS" = "" ] && echo -n "<none> "
for IOC in $MYIOCS
do
echo -n "$IOC "
clear_options #清除环境变量
default_options "$IOC" #设置默认环境变量
assign_options "GLOBAL" #设置全局设置的环境变量
assign_options "$IOC" #设置本IOC的环境变量
set_cmdenvopts #用于启动IOC的环境变量字符串,用于procserv的选项字符串
if [ -d $BOOTDIR ] # 测试启动目录是否存在
then
if [ -d $HOMEDIR ] # 测试家目录是否存在
then
# 测试是否已经有pidfile文件$PIDFILE的进程在运行了,如果是返回1
sudo -H -u $IOC_USER bash -c "$SETENV (env > $ENVFILE; /usr/sbin/start-stop-daemon --start --quiet --chdir $BOOTDIR \
--pidfile $PIDFILE --startas $PROCSERV --name procServ --test > /dev/null)"
if [ "$?" = 1 ]
then
echo -n "<was running> "
else
# 启动服务进程以及IOC
sudo -H -u $IOC_USER bash -c "$SETENV (env > $ENVFILE; /usr/sbin/start-stop-daemon --start --quiet --chdir $BOOTDIR \
--pidfile $PIDFILE --startas $PROCSERV --name procServ -- $PROCSERVOPTS ./st.cmd)"
if [ "$?" = 1 ]
then
echo -n "<failed> "
fi
fi
else
echo -e "\nWarning: Home directory $HOMEDIR does not exist! Ignoring $IOC"
fi
else
echo -e "\nWarning: Boot directory $BOOTDIR does not exist! Ignoring $IOC"
fi
done
echo "... done."
}
do_start的测试脚本如下:
echo "========================Test do_start()============================="
default_options aiDriver
assign_options aiDriver
set_cmdenvopts
do_start aiDriver
echo "===================end================================="
echo ""
测试结果如下:
========================Test do_start()=============================
Starting soft IOCs ... aiDriver ... done.
===================end=================================
IOC顶层目录下,产生两个新的文件aiDriver.env和aiDriver.pid:
:/usr/local/EPICS/program/aiDriver# ls
aiDriverApp aiDriver.env aiDriver.pid bin configure db dbd iocBoot lib Makefile
ftp本地连接20000端口,进行测试:
root@orangepi5plus:~# telnet localhost 20000
Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
@@@ Welcome to procServ (procServ Process Server 2.7.0)
@@@ Use ^X to kill the child, auto restart is ON, use ^T to toggle auto restart
@@@ procServ server PID: 171785
@@@ Server startup directory: /usr/local/EPICS/program/aiDriver/iocBoot/iocaiDriver
@@@ Child startup directory: /usr/local/EPICS/program/aiDriver/iocBoot/iocaiDriver/
@@@ Child "orangepi" started as: ./st.cmd
@@@ Child "orangepi" PID: 171786
@@@ procServ server started at: Fri Jul 5 15:11:02 2024
@@@ Child "orangepi" started at: Fri Jul 5 15:11:02 2024
@@@ 0 user(s) and 0 logger(s) connected (plus you)
epics> dbl
Random:AiRandom
通道访问这个IOC:
caget Random:AiRandom
Random:AiRandom 527
停止守护/服务进程的函数:
sudo -H -u $IOC_USER bash -c "/usr/sbin/start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name procServ"
--retry:使用--stop,指定start-stop-daemon检查这些进程是否结束了。它将重复检查任何匹配的进行是否正在运行,直到没有匹配的进程运行。如果这些进程不存在,它将采取由调度表决定的进一步操作。如果指定了timeout替代schedule,则调度表signal/timeout/KILL/timeout被使用,此处signal是由--signal指定的信号。
schedule是一个列表,其至少有由斜杆(/)分隔的两项;每项可能是-signal-number或[-]signal-name,它表示发送那个信号,或timeout,它表示为进程退出等待那些秒数,或者forever,它表示如果需要一直重复调度表余下的。
do_stop()
{
# 返回:
# 0:守护进程被停止
# 1:守护进程已经停止
# 2:守护进程不能被停止
# 3:发生故障
echo -n "Stopping soft IOCs ... "
MYIOCS=`get_iocs $@`
[ "$MYIOCS" = "" ] && echo -n "<none> "
for IOC in $MYIOCS
do
echo -n "$IOC "
clear_options
default_options "$IOC"
assign_options "GLOBAL"
assign_options "$IOC"
set_cmdenvopts
# 测试停止守护进程, 返回1,表示已经停止
sudo -H -u $IOC_USER bash -c "/usr/sbin/start-stop-daemon --stop --quiet --pidfile $PIDFILE --name procServ --test > /dev/null"
if [ $? = 1 ]
then
echo -n "<not running> "
else
sudo -H -u $IOC_USER bash -c "/usr/sbin/start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name procServ"
if [ $? = 1 ]
then
echo -n "<failed> "
else
sudo -H -u $IOC_USER bash -c "rm -f $PIDFILE"
fi
fi
done
echo "... done."
}
do_stop的测试脚本:
echo "========================Test do_start()============================="
default_options aiDriver
assign_options aiDriver
set_cmdenvopts
do_stop aiDriver
echo "===================end================================="
echo ""
测试结果如下,ftp服务程序和epics ioc程序都已经终止。
========================Test do_start()=============================
Stopping soft IOCs ... aiDriver ... done.
===================end=================================
root@orangepi5plus:~# ss -tlnp
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=1383,fd=3))
LISTEN 0 4 127.0.0.1:5037 0.0.0.0:* users:(("adbd",pid=849,fd=5))
LISTEN 0 4096 0.0.0.0:111 0.0.0.0:* users:(("rpcbind",pid=900,fd=4),("systemd",pid=1,fd=32))
LISTEN 0 4 0.0.0.0:5555 0.0.0.0:* users:(("adbd",pid=849,fd=10))
LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:* users:(("systemd-resolve",pid=901,fd=14))
LISTEN 0 128 [::]:22 [::]:* users:(("sshd",pid=1383,fd=4))
LISTEN 0 4096 [::]:111 [::]:* users:(("rpcbind",pid=900,fd=6),("systemd",pid=1,fd=34))
并且IOC顶层目录中pidfile文件也被删除了:
root@orangepi5plus:/usr/local/EPICS/program/aiDriver# ls
aiDriverApp aiDriver.env bin configure db dbd iocBoot lib Makefile
发送SIGHUP信号给守护/服务进程的函数如下(此处没有实现):
do_reload() {
# 如果守护进程可以在不重启下重载其配置(例如,当向其发送要给SIGHUP),
# 则在此实现。
# start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
# return 0
echo "Restarting soft IOCs ... "
STARTDIR=$PWD
IOCS=`get_iocs $@`
[ "$IOCS" = "" ] && echo -n "<none> "
for IOC in $IOCS
do
echo -n "$IOC "
clear_options
default_options "$IOC"
assign_options "GLOBAL"
assign_options "$IOC"
if [ -d $BOOTDIR ]
then
cd "$BOOTDIR"
# restart it!
echo -e "\ndebug: Reloading ioc $IOC"
cd "$STARTDIR"
else
echo -e "\nWarning: Boot directory $BOOTDIR does not exist! Entry for $NET ignored!"
fi
done
echo "... done."
}
以下脚本用于从命令 行获取参数,并且执行守护进程启动/停止/重启:
COMMAND=$1
shift
IOCS=`echo $@`
case "$COMMAND" in
start)
do_start $IOCS
;;
stop)
do_stop $IOCS
;;
restart|force-reload)
do_stop $IOCS
sleep 1
do_start $IOCS
;;
*)
echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload} [iocs ...]" >&2
exit 3
;;
esac
以下命令行启动IOC:
root@orangepi5plus:~# ./test.sh start aiDriver
Starting soft IOCs ... aiDriver ... done.
root@orangepi5plus:~# ss -lntp
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=1383,fd=3))
LISTEN 0 20 0.0.0.0:5080 0.0.0.0:* users:(("st.cmd",pid=172122,fd=13))
LISTEN 0 5 127.0.0.1:20000 0.0.0.0:* users:(("st.cmd",pid=172122,fd=3),("procServ",pid=172121,fd=3))
LISTEN 0 4 127.0.0.1:5037 0.0.0.0:* users:(("adbd",pid=849,fd=5))
LISTEN 0 4096 0.0.0.0:111 0.0.0.0:* users:(("rpcbind",pid=900,fd=4),("systemd",pid=1,fd=32))
LISTEN 0 4 0.0.0.0:5075 0.0.0.0:* users:(("st.cmd",pid=172122,fd=15))
LISTEN 0 4 0.0.0.0:5555 0.0.0.0:* users:(("adbd",pid=849,fd=10))
LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:* users:(("systemd-resolve",pid=901,fd=14))
LISTEN 0 128 [::]:22 [::]:* users:(("sshd",pid=1383,fd=4))
LISTEN 0 4096 [::]:111 [::]:* users:(("rpcbind",pid=900,fd=6),("systemd",pid=1,fd=34))
以下命令行停止IOC:jj
root@orangepi5plus:~# ./test.sh stop aiDriver
Stopping soft IOCs ... aiDriver ... done.
root@orangepi5plus:~# ss -lntp
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=1383,fd=3))
LISTEN 0 4 127.0.0.1:5037 0.0.0.0:* users:(("adbd",pid=849,fd=5))
LISTEN 0 4096 0.0.0.0:111 0.0.0.0:* users:(("rpcbind",pid=900,fd=4),("systemd",pid=1,fd=32))
LISTEN 0 4 0.0.0.0:5555 0.0.0.0:* users:(("adbd",pid=849,fd=10))
LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:* users:(("systemd-resolve",pid=901,fd=14))
LISTEN 0 128 [::]:22 [::]:* users:(("sshd",pid=1383,fd=4))
LISTEN 0 4096 [::]:111 [::]:* users:(("rpcbind",pid=900,fd=6),("systemd",pid=1,fd=34))
将以下test.sh更名为softIOC并复制到/etc/init.d/目录下,IOC应用程序就可以当作系统服务程序一样被启动。
root@orangepi5plus:~# /etc/init.d/softIOC start aiDriver
Starting soft IOCs ... aiDriver ... done.
root@orangepi5plus:~# /etc/init.d/softIOC stop aiDriver
Stopping soft IOCs ... aiDriver ... done.