终端登录:
init进程知道那些终端设备可用来登录,并且为每个设备生成一个getty进程
网络登录:
为了使同一个软件技能处理终端登录,又能够处理网络登录,系统使用了一种称为伪终端(pseudo terminal)的软件驱动程序。
BSD登录中,有一个inetd进程-->等待大多数网络连接。
inetd等待TCP/IP连接请求到达主机,当一个连接请求到达时,执行一次fork,然后生成的子进程exec执行适当的程序。
进程组
pid_t getpgrp()获取进程组id
int setpgid(pid_t pid,pid_t pid);加入一个现有的进程组或者创建一个新的进程组
注意:一个进程只能为它自己或者它的子进程设置进程组ID。在它的子进程调用了exec后,它就不可以更改该孩子进程的进程组了!
会话:
返回前台进程组GroupID:
#include<unistd.h>
#include "apue.h"
int main(void){
/*returns the process group ID of the foreground process group on
the terminal associated to fd, which must be the
controlling terminal of the calling process.`*/
printf("前台进程组ID = %d\n",tcgetpgrp(STDIN_FILENO));
exit(0);
}
作业控制
作业编号为[1],进程ID为11073
以下转自:https://blog.csdn.net/q1007729991/article/details/56846375
1. 控制终端
一般来说,在 bash 中运行的进程,都会伴随着一个控制终端。默认情况下(没有重定向),每个进程的标准输入、标准输出和标准错误输出都指向控制终端。
需要特别注意的是,控制终端是进程的属性,它保存在进程 PCB 中。而 bash 进程在启动的时候,会自动建立一个控制终端。所以由 bash 进程启动(fork)的进程都会继承控制终端。所以在同一个会话中,控制终端都是一样的。
进程可以没有控制终端。如果要想让进程脱离控制终端,可以让该进程创建新的会话
2. 控制进程
建立与控制终端连接的会话首进程(session leader),被称为控制进程。如果终端设备与控制终端断开连接,系统会发送信号给控制进程。
如果会话首进程终止,则该信号发送到该会话前台进程组。一个进程退出导致一个孤儿进程组产生时,如果任意一个孤儿进程组中的进程处于 STOP 状态,发送 SIGHUP 和 SIGCONT 信号到该进程组中所有进程。
3. 前台进程组与后台进程组
如果一个会话有控制终端,则它有一个前台进程组,以及多个后台进程组。使用命令 ps ajx 可以查看会话的前台进程组,位于 TPGID 一栏,如果该值为 -1,表示该进程没有控制终端,所以也没有前台进程组。
当在终端输入或者终端产生信号时,都会发送到前台进程组。在很久以前学习信号时,我们使用快捷键 CTRL C、CTRL Z 等等发送信号,并没有指定进程和进程组编号,实际上,这样产生的信号默认都是发送到前台进程组中去的。
当你执行 CTRL C 时,前台进程组中所有的进程都会中断。
孤儿进程
#include "apue.h"
#include <errno.h>
/* 挂断信号 */
static void sig_hup(int signo){
printf("SIGHUP received,pid = %d\n",(long)getpid());
}
static void pr_ids(char* name){
printf("%s: pid = %ld,ppid = %ld,pgrp = %ld,tpgrp = %ld\n",name,(long)getpid(),(long)getppid(),(long)getpgrp(),(long)tcgetpgrp(STDIN_FILENO));
fflush(stdout);
}
int main(void){
char c;
pid_t pid;
pr_ids("parent");
if((pid = fork())<0){
printf("fork err!");
}
else if(pid>0){
sleep(1); /*sleep to let child stop itself! */
}
else{
pr_ids("chlid");
signal(SIGHUP,sig_hup); /* esteblish signal handler */
kill(getpid(),SIGTSTP); /* stop ourself */
pr_ids("child"); /* prints only if we are continue */
if(read(STDIN_FILENO,&c,1)!=1)
printf("read err %d on controlling TTY\n",errno);
}
exit(0);
}
父进程终止后,停止的子进程成为了孤儿进程
以下转自:https://www.cnblogs.com/ljygoodgoodstudydaydayup/p/3873323.html
1. 为什么会有孤儿进程组的概念,APUE没直接写,但是GNU有规定:
孤儿进程组不可以获得终端,这是为了保证控制进程死掉后他的终端可以安全分配给新session。posix要求向新孤儿进程组中停止状态的进程(GNU说是给孤儿进程组里所有进程发消息,而APUE中文版说是给停止状态的进程发消息)发送SIGHUP(挂起)信号和SIGCONT(继续)信号。首先处理SIGHUP信号,系统默认处理是终止进程,然而也可以另行处理这样进程会继续执行,但任不可以再获得终端。
上面代码,father等待5秒后狗带,child运行结果如下:
[karenyin@host relation]$ ./orphan3
parent: pid = 15877, ppid = 15811, pgrp = 15877, tpgrp = 15877 // father是组长,也是前台进程组ID,father的father是shell,shell pid=15811
child: pid = 15878, ppid = 15877, pgrp = 15877, tpgrp = 15877 // child是组员,当前这个进程组占据了终端。结果输出在这里停了几秒,然后才打印下一句“SIGHUP received”
[karenyin@host relation]$ SIGHUP received, pid = 15878 // 进程组先退出终端,然后又在终端打印SIGHUP
child: pid = 15878, ppid = 1, pgrp = 15877, tpgrp = 15811 // 前台进程组已经不是father了,说明终端又交给了shell
read error 5 on controlling TTY // child试图读标准输入read(STDIN_FILENO, &c, 1),触发了异常。
^C // child没有走到exit(0)
原因分析如下:
1. kill(15878, SIGTSTP)并不是要child杀死自己,而是暂停前台作业。
2. father自然死亡后,child成为孤儿进程,被init收养,因此ppid=1。
child也成为进程组15877也成为孤儿进程组,因此收到SIGHUP信号(之后还会收到SIGCONT信号,表示继续)。
3. 子进程处理SIGHUP信号的时候,已经是孤儿进程组成员了,没有权限刷到标准输出,为什么成功打印了“SIGHUP received, pid = 15878”呢?同样的疑惑,为什么child能在终端打印“read error %d on controlling TTY\n”呢?关于这个疑问其实apue也有写,POSIX.1只对读控制终端作出限制。
所以说,gnu对孤儿进程组的定义里access一词的意思是“读”,孤儿进程组成员试图读取控制终端时才抛EIO,对写终端未做规定。
来源:oschina
链接:https://my.oschina.net/u/4275752/blog/4308161