SIGTTIN/SIGTTOU 信号

SIGTTIN: 当一个后台进程组试图读取其控制终端时,终端驱动程序产生此信号。在下列例外情况不产生次信号: a、读进程忽略或者堵塞此信号 b、读进程所属的进程组是孤儿进程组,此时读操作返回出错,errno设置未EIO。

SIGTTOU: 当一个后台进程组试图写其控制终端时,终端驱动程序产生此信号。与SIGTTIN信号不同,一个进程可以算着允许后台进程写控制终端。 如果不允许写控制终端,则与SIGTTIN相似,也有两种特殊情况: a、写进程忽略或者堵塞此信号 b:写进程所属的进程组是孤儿进程组,此时读操作返回出错,errno设置未EIO。

SIGTTIN 和 SIGTTOU 信号的默认动作是暂停进程。

下图代码,进程启动,fork 出子进程,子进程设置进程组,调用tcsetpgrp 将自己设置为前端进程组,父进程就变成了后端进程组。在read的时候,暂停。

#include 
#include 
#include 
#include 
#include 

//打印进程信息
void pr_process(char * name){
        printf("%s  pid:%d,ppid:%d,pgid:%d,sid:%d,grrp:%d\n",name,getpid(),getppid(),getpgid(getpid()),getsid(getpid()),tcgetpgrp(STDIN_FILENO));
}

void handler(int sig){
    printf("receive %d sig\n",sig);
}

int main(int argc,char *argv[]){
        pid_t pid ;

        pid_t fpgid = tcgetpgrp(STDIN_FILENO);
        if((pid = fork()) < 0){
                perror("fork error");
                exit(1);
        }

        if(pid == 0){
                signal(SIGTTOU,SIG_IGN);
                setpgid(getpid(),getpid());
                pr_process("child before set:");
                if(tcsetpgrp(STDIN_FILENO,getpid()) == -1){
                        printf("set error\n");
                        exit(2);
                }
                pr_process("child after set");
                exit(1);

        }

        pr_process("parent1");
    waitpid(pid,NULL,0);
        pr_process("parent2");
        char buf[100];
//    signal(SIGTTIN,handler);
        if(read(STDIN_FILENO,buf,100) < 0){
                perror("read error");
                printf("error\n");
                exit(127);
        }
        printf("sucess\n");
        return 0;
}

补充: 上面的代码中,在进程中设置了忽略SIGTTOU信号。
因为在tcsetpgrp 的手册中是这样说的:如果tcsetpgrp 被一个后端进程组中的一员调用,并且调用进程没有阻塞或忽略 SIGTTOU,那么SIGTTOU 信号会被发送给这个后端进组的所有成员。

我们在在子进程fork一个进程实验一下如果tcsetpgrp。
实验一:将上述程序的 signal(SIGTTOU,SIG_IGN); 改为 signal(SIGTTOU,handler); 执行打印结果: 一直在发送SIGTTOU信号,在收到终端信号后,标记执行tcsetpgrp 失败。这是因为(tcsetpgrp是一个可重入函数)
SIGTTIN/SIGTTOU 信号_第1张图片

你可能感兴趣的:(unix)