screen nohup session

nohup和&的缺点是,如果你要在一个shell会话里面执行多个命令和脚本,那么要每个命令和脚本都要加nohup和&非常麻烦,所以才有了screen和TMUX
Screen 命令与 Tmux 命令
另一种思路是使用 terminal multiplexer (终端复用器:在同一个终端里面,管理多个session),典型的就是 Screen 命令和 Tmux 命令。
它们可以在当前 session 里面,新建另一个 session。这样的话,当前 session 一旦结束,不影响其他 session。而且,以后重新登录,还可以再连上早先新建的 session。

kill 调用的是 SIGTERM, 此信号可以被捕获和忽略。
kill -9 调用的是 SIGKILL, 杀掉进程,不能被捕获和忽略。
SIGHUP是在终端被断开时候调用,如果信号没有被处理,进程会终止。



只要在命令的尾部加上符号&,启动的进程就会成为”后台任务”。如果要让正在运行的”前台任务”变为”后台任务”,可以先按ctrl + z,然后执行bg命令(让最近一个暂停的”后台任务”继续执行)。
“后台任务”有两个特点。
继承当前 session (对话)的标准输出(stdout)和标准错误(stderr)。因此,后台任务的所有输出依然会同步地在命令行下显示。
不再继承当前 session 的标准输入(stdin)。你无法向这个任务输入指令了。如果它试图读取标准输入,就会暂停执行(halt)。
可以看到,”后台任务”与”前台任务”的本质区别只有一个:是否继承标准输入。所以,执行后台任务的同时,用户还可以输入其他命令。
三、SIGHUP信号
变为”后台任务”后,一个进程是否就成为了守护进程呢?或者说,用户退出 session 以后,”后台任务”是否还会继续执行?
Linux系统是这样设计的。
用户准备退出 session
系统向该 session 发出SIGHUP信号
session 将SIGHUP信号发给所有子进程
子进程收到SIGHUP信号后,自动退出
上面的流程解释了,为什么”前台任务”会随着 session 的退出而退出:因为它收到了SIGHUP信号。
那么,”后台任务”是否也会收到SIGHUP信号?
这由 Shell 的huponexit参数决定的。



$ shopt | grep huponexit
执行上面的命令,就会看到huponexit参数的值。
大多数Linux系统,这个参数默认关闭(off)。因此,session 退出的时候,不会把SIGHUP信号发给”后台任务”。所以,一般来说,”后台任务”不会随着 session 一起退出。
四、disown 命令
通过”后台任务”启动”守护进程”并不保险,因为有的系统的huponexit参数可能是打开的(on)。
更保险的方法是使用disown命令。它可以将指定任务从”后台任务”列表(jobs命令的返回结果)之中移除。一个”后台任务”只要不在这个列表之中,session 就肯定不会向它发出SIGHUP信号。



新建一个 session


$ screen
$ screen -r
如果新建多个后台 session,就需要为它们指定名字。



$ screen -S name



切回指定 session


$ screen -r name
$ screen -r pid_number



列出所有 session


$ screen -ls
如果要停掉某个 session,可以先切回它,然后按下ctrl + c和ctrl + d。
Tmux 比 Screen 功能更多、更强大



Signal是在一种软件体系下对中断的模拟,所以也被称为软中断。



Signal是进程间通信机制中唯一的异步通信机制。



与其他进程间通信方式(管道pipeline、共享内存等)相比,信号所能传递的信息比较粗糙,只是一个整数。
但正是由于传递的信息量少,信号也便于使用和管理,可以用于系统管理相关的任务,例如通知进程终结、中止或者恢复等。



信号可分为可靠、不可靠,实时、非实时。其主要区别是Unix与后来改进版Linux信号的差别。



进程对信号的响应可分为忽略、捕捉、缺省三种。



发送信号的主要函数有:kill()、raise()、 sigqueue()、alarm()、setitimer()以及abort()



每种信号用一个整型常量宏表示,以SIG开头,比如SIGCHLD、SIGINT等,它们在系统头文件中定义。



内核中针对每一个进程都有一张表来保存信号。



在进程task_struct结构体中有一个未决信号的成员变量struct sigpending pending。



每个信号在进程中注册都会把信号值加入到进程的未决信号集。



当内核需要将信号传递给某个进程时,就在该进程对应的表中写入信号,这样就生成了信号。



非实时信号发送给进程时,如果该信息已经在进程中注册过,不会再次注册,故信号会丢失;
实时信号发送给进程时,不管该信号是否在进程中注册过,都会再次注册。故信号不会丢失;
信号在从内核态切换至用户态时,进行处理。



当该进程由用户态陷入内核态,再次切换到用户态之前,会查看表中的信号。如果有信号,进程就会首先执行信号对应的操作,此时叫做执行信号。



从生成信号到将信号传递给对应进程这段时间,信号处于等待状态。



我们可以编写代码,让进程阻塞block某些信号,也就是让这些信号始终处于等待的状态,直到进程取消阻塞unblock或者忽略信号。



processes
在远程主机上,有两个相关的进程:



ssh守护进程(sshd)的一个实例,它将远程程序的输入和输出中继到本地终端;
进程组,用于维护各种bash命令(bash也是一个进程);
每个进程属于一个进程组
同组中的进程作为一个命令管道的一部分,例如cat file | sort | grep ‘word’将放置在运行cat(1),sort(1), grep(1)的进程组里
bash也是一个进程,也属于一个进程组
进程组是会话的一部分, 会话由一个或多个进程组组成
在会话中,最多有一个进程组,称为前台进程组,可能还有许多后台进程组
bash将进程从后台移动到前台,从前台移动到后台通过tcsetpgrp(3)
发送到进程组的信号将传递到该组中的每个进程。
some concept
tty: 终端设备,Teletypes, 电传打字机
pty: 伪终端,虚拟终端
pts/ptmx: pts=pseudo-terminal slave, ptmx=pseudo-terminal master, 对master的操作会反映到slave上
/dev/ttySn: 串行端口终端。计算机把每个串行端口都看作是一个字符设备
例如,echo test > /dev/ttyS1会把单词”test”发送到连接在ttyS1(COM2)端口的设备上
/dev/pty: 伪终端是成对的逻辑终端设备,例如/dev/ptyp3和/dev/ttyp3
例如,telnet连接PC,则telnet程序会连接到设备ptyp2(m2)上。此时一个getty程序就应该运行在对应的ttyp2(s2)端口上
当telnet从远端获取了一个字符时,该字符就会通过m2、s2传递给getty程序,而getty程序就会通过s2、m2和telnet程序往网络上返回”login:”字符串信息
/dev/tty: 控制终端,当前进程的控制终端的设备特殊文件,可以使用命令”ps –ax”来查看进程与哪个控制终端相连
shell中,/dev/tty就是使用的终端,设备号(5,0).使用命令”tty”可以查看它具体对应哪个实际终端设备
/dev/ttyn, /dev/console: 控制台终端
sshd
首先得明白sshd过程



当sshd接收到shell命令,会创建一对pty(master pty, slave pty).(不明白的参考上一节)



sshd拥有master pty,那么shell会把slave pty作为shell程序的stdin stdout stdeer



对应的ssh命令就是 ssh -t root@ip ls(对主要就是 -t)



如果不使用pty, 则sshd会使用pipe作为与shell的关联,shell会把stdin stdout stderr变为pipe



那么为什么ssh这边的客户端关了之后,服务器运行的命令会关闭呢?



那是因为如果ssh客户端关闭了,sshd可以感知的到,则服务器端会把master pty关闭。



那么操作系统就会向slave pty发送SIGNUP信号,默认的信号处理方式就是终止



如果换成pipe,那么sshd也会关闭pipe的一端。如果此时客户端已经关闭,那么会收到一个SIGNPIPE信号,默认的处理函数是终止函数,所以只要程序不读写stdin stdout stderr那么不会退出



nohup
再说说nohup



nohup的原理就是假装看不见SIGNHUP信号,同时把stdin重定向到/dev/null 把stdout stderr 重定向到nohup.out(重定向地址可以更改)



但是如果ssh是使用pipe方式的, 使用nohup发现没有效果,stdin还是pipe



那是因为nohup里面会对原有的stdin 做判断,只有是istty 才会把stdin stdout stderr替换掉



nohup command &
1
setsid
换个角度思考,如果我们的进程不属于接受SIGNHUP信号的终端的子进程,那么自然也就不会受到 HUP 信号的影响了



setsid 就是利用这一点



setsid command
1
&
如果把”&”放入“()”内之后,会发现所提交的job并不在jobs中,那么这样就能躲过SIGNHUP信号



$ (ping wyydsb.xin &)
$ ps -ef |grep wyydsb.xin
501 18420 18140 0 6:50PM ttys003 0:00.02 watch curl https://wyydsb.xin
501 18456 18140 0 6:51PM ttys003 0:00.23 watch -n 5 curl https://wyydsb.xin
501 20519 1 0 8:46PM ttys005 0:00.01 ping wyydsb.xin
501 20598 20528 0 8:47PM ttys008 0:00.00 grep –color=auto –exclude-dir=.bzr –exclude-dir=CVS –exclude-dir=.git –exclude-dir=.hg –exclude-dir=.svn wyydsb.xin
1
2
3
4
5
6
当然 这个 最好别作死去试



因为PID=1的是内核进程



然后启起来 你就发现kill不了 只能 重启了



disown
如果 我们 起命令的时候忘记加nohup或者setsid,然后还想忽略SIGNHUP,只能通过作业调度和 disown 来解决问题了



disown -h jobspec来使某个作业忽略SIGNHUP信号
disown -ah 来使所有的作业都忽略SIGNHUP信号
disown -rh来使正在运行的作业忽略SIGNHUP信号
screen
如果有大量这种命令需要在稳定的后台里运行,如何避免对每条命令都做这样的操作呢?



screen -dmS session name 建立一个处于断开模式下的会话(并指定其会话名)
screen -list 列出所有会话
screen -r session name 重新连接指定会话
快捷键CTRL-a d 暂时断开当前会话
systemd
当然还可以把bash封装成一个服务运行在Linux上


Category linux