uc_11_信号集_信号屏蔽_睡眠_暂停_闹钟

1  睡眠、暂停、闹钟

1.1  sleep()  睡眠(阻塞)

        #include

        unsigned int sleep(unsigned int seconds);

                功能:有限睡眠

                seconds:以秒为单位的睡眠时限

                返回值:0或剩余秒数 

        该函数是调用函数睡眠seconds秒,除非有信号终止了调用进程或被该进程捕获。

        如果有信号被调用进程捕获,在信号处理函数返回以后,阻塞的sleep()函数才会返回,且返回值为剩余的秒数。否则函数将返回0,表示睡眠充足。

        sleep()函数会被信号打断。信号捕获后,信号处理函数执行完,sleep()才关闭,进程继续。

1.2  usleep()  睡眠(阻塞)

        #include

        int usleep(useconds_t usec);

                功能:更精确的有限睡眠

                usec:以微秒(10的负6次方秒)为单位的睡眠时限

                返回值:成0-1  

        如果有信号被调用进程捕获,在信号处理函数返回后,阻塞的usleep()函数才会返回,且返回-1,同时置errno为EINTR,表示阻塞的系统调用被信号中断。

1.3  pause()  无限睡眠(阻塞)

        #include

        int pause(void);

                功能:暂停(无限睡眠

                返回值:成功阻塞,失败返回-1 

        pause()函数要么阻塞不返回,要么返回-1,永远不会返回0。

        pause()函数是调用进(线)程进入无时限的睡眠状态,知道有信号终止了该进程或信号被该进程捕获。(看电影时按下pause,进入无限睡眠,收到信号后,关闭pause,继续看电影)

        如果有信号被调用进程捕获,在信号处理函数返回以后,阻塞的pause()函数才会返回,返回值-1,同时置errno为EINTR,表示阻塞的系统调用被信号打断。

        pause()函数会被信号打断。信号捕获后,信号处理函数执行完,pause()才关闭,进程继续。

        需要程序暂停时,调用pause(),需要继续时,给个信号即可。

//pause()函数演示
#include
#include
#include
//信号处理函数
void sigfun(int signum){
    printf("%d号信号处理开始\n",signum);
    sleep(3);
    printf("%d号信号处理结束\n",signum);
}
int main(void){
    //捕获2号信号
    if(signal(SIGINT,sigfun) == SIG_ERR){
        perror("signal");
        return -1;
    }
    printf("%d进程:我要睡觉了\n",getpid());
    int ret = pause();//进入暂停,若捕获到2号信号,则执行信号处理函数,完毕后,暂停才结束。
    printf("%d进程;pause函数返回%d\n",getpid(),ret);
    return 0;
}//编译执行,择机输入Ctrl + C

1.4  alarm()  闹钟(非阻塞)

        #include

        unsigned int alarm(unsigned int seconds);

                功能:设置闹钟

                seconds:以秒为单位的闹钟时间。取0表示取消先前设过且未到期的闹钟。

                返回值:0或先前所设闹钟的剩余秒数 

        alarm()函数使系统内核在该函数被调用以后seconds秒的时候,向调用进程发送SIGALRM (14)信号。

        若在调用该函数前已设置过另一个该函数且尚未到期,则函数会重设闹钟,并返回先前所设闹钟的剩余秒数,否则返回0。

2  信号集

        多个信号组成的信号集合。

        系统内核用sigset_t结构体类型表示信号集。

        -在中又被定义为typedef  __sigset_t  sigset_t;

        -在中有如下类型定义

                #define _SIGSET_NWORDS (1024 / (8* sizeof (unsigned long int)))

                typedef struct {

                        unsigned long int __val[_SIGSET_NWORDS];

                } __sigset_t;

        sigset_t类型是一个结构体,该结构体中只有1个成员,是1个包含32个元素的整数数组(针对32位系统而言)。                                        (那直接用数组不就行了?答:约定俗称吧。)

        故可以把sigset_t类型看成一个由1024个二进制位组成的大整数。

        对于64位系统,long是8字节,数组是16个元素,依然共1024位:

                其中的每一位对应一个信号,当然,目前远没有那么多信号。

                某位为1就表示信号集中有此信号,反之为0就是无此信号。

                当需要同时操作多个信号时,常以sigset_t作为函数的参数或返回值的类型。

        uc_11_信号集_信号屏蔽_睡眠_暂停_闹钟_第1张图片

2.1  sigfillset()

        #include

        int sigfillset (sigset_t* sigset);

                功能:常用于初始化,填满信号集,即,将信号集的全部位,置1

                sigset:信号集

                返回值:成0-1  

2.2  sigemptyset()

        #include

        int sigemptyset (sigset_t* sigset);

                功能:常用于初始化,清空信号集,即,将信号集的全部位,置0

                sigset:信号集

                返回值:成0-1  

2.3  sigaddset()

        #include

        int sigaddset (sigset_t* sigset,  int signum);

                功能:加入信号,即,将信号集中与指定信号编号对应的信号位,置1 

                sigset:信号集

                signum:信号编号

                返回值:成0-1  

2.4  sigdelset()

        #include

        int sigdelset (sigset_t* sigset,  int signum);

                功能:删除信号,即,将信号集中与指定信号编号对应的信号位,置0 

                sigset:信号集

                signum:信号编号

                返回值:成0-1  

2.5  sigismember()

        #include

        int sigismember (const sigset_t* sigset,  int signum);

                功能:判断信号集中是否有某信号,即检查信号集中与指定信号编号对应的信号位为1否 

                sigset:信号集

                signum:信号编号

                返回值:有则返回1没则返回0,失败返-1  

//sigset.c  信号集
#include
#include
#include
//打印一个字节八位内容
void printb(char byte){              // b
    for(int i = 0;i < 8;i++){
        printf("%d",byte & 1 << 7 - i ? 1 : 0);
    }
    printf(" ");//空格
}
// 一块存储区的所有比特位内容的输出
void printm(void* buf,size_t size){  // m
    for(int i = 0;i < size;i++){
        printb(((char*)buf)[size-1-i]);  //字节倒着输出,与bit顺序一致
        if((i + 1) % 8 == 0){
            printf("\n");
        }
    }
}

int main(void){
    //信号集
    sigset_t s;//欠初始化
    printf("填满信号集:\n");
    sigfillset(&s);//初始化
    printm(&s,sizeof(s));
    printf("清空信号集:\n");
    sigemptyset(&s);
    printm(&s,sizeof(s));
    printf("添加2号信号\n");
    sigaddset(&s,SIGINT);
    printm(&s,sizeof(s));
    printf("添加3号信号\n");
    sigaddset(&s,SIGQUIT);
    printm(&s,sizeof(s));
    printf("删除2号信号\n");
    sigdelset(&s,SIGINT);
    printm(&s,sizeof(s));
    printf("信号集中%s2号信号\n",sigismember(&s,SIGINT) ? "有" : "无");
    printf("信号集中%s3号信号\n",sigismember(&s,SIGQUIT) ? "有" : "无");
    return 0;
}//编译执行,注意下低32、低33位

3  信号屏蔽

3.1  递送、未决、掩码    

        uc_11_信号集_信号屏蔽_睡眠_暂停_闹钟_第2张图片

        递送(delivery):当信号产生时,系统内核会在其所维护的进程表中,为特定的进程设置一个与该信号相对应的标志位,这个过程叫做递送。

        未决(pending):信号从产生到完成递送之间存在一定的时间间隔,处于这段时间间隔中的信号称为未决。

        掩码(signal mask):每个进程都有一个信号掩码,它实际是一个信号集,位于该信号集中的信号一旦产生,并不会被递送给响应进程,而是会被阻塞(block)在未决状态。

       

        相同不可靠信号瞬时发送多个,只接收2个的原因:

        捕获到某信号,在信号处理函数执行期间,这个正在被处理的信号总是会处于信号掩码中。如果又有该信号产生,则会被阻塞,直到上一个针对该信号的处理过程结束以后才会被递送。(相同不可靠信号瞬时来多个,只接受2个,其余丢弃,的出处在这。)

       

        信号掩码的意义:

        当信号正在执行类似更新数据库这样的敏感任务时,可能并不希望被某些信号中断。这时可以通过信号掩码暂时屏蔽而非忽略这些信号,使其一旦产生即被阻塞于未决状态(即屏蔽),待任务完成后,再回过头来处理这些信号。

3.2  sigprocmask()

        #include

        int sigprocmask (int how,  const sigset_t* sigset,  sigset_t* oldset);

                功能:设置调用进程的信号掩码

                how:对信号掩码进行何种修改,可取以下值

                                SIG_BLOCK  将sigset中的信号加入到当前信号掩码

                                SIG_UNBLOCK  将sigset中的信号从当前信号掩码中删除

                                SIG_SETMASK  用sigset取代当前信号掩码

                sigset:信号集,取NULL则忽略此参数

                oldset:输出原信号代码(方便后续复原),取NULL则忽略此参数

                返回值:成0-1        

//sigprocmask.c  信号掩码
#include
#include
#include
#include
//信号处理函数
void sigfun(int signum){
    printf("%d进程:捕获到%d号信号\n",getpid(),signum);
}

//假装更新数据库
void updatedb(void){
    for(int i = 0;i < 5;i++){
        printf("正在更新第%d条数据\n",i+1);
        sleep(1); 
    }
}

int main(void){
    int signum = /*SIGINT*/ 50;   //注意不可靠信号、可靠信号
    //对2号信号进行捕获处理
    if(signal(signum,sigfun) == SIG_ERR){
        perror("signal");
        return -1;
    }
    //对2号信号进行屏蔽处理,        屏蔽期间信号阻塞,无法捕获
    printf("%d进程:对%d号信号进行屏蔽\n",getpid(),signum);
    sigset_t sigset;
    sigemptyset(&sigset);//清空
    sigaddset(&sigset,signum);//添加2号信号
    sigset_t oldset;//用来输出修改前的信号掩码
    if(sigprocmask(SIG_SETMASK,&sigset,&oldset) == -1){
        perror("sigprocmask");
        return -1;
    }
    //创建子进程
    pid_t pid = fork();
    if(pid == -1){
        perror("fork");
        return -1;
    }
    //子进程代码,向父进程发送信号
    if(pid == 0){
        for(int i = 0;i < 5;i++){
            printf("%d进程:向父进程发送%d号信号\n",getpid(),signum);
            if(kill(getppid(),signum) == -1){
                perror("kill");
                return -1;
            }
        }
        return 0;
    }
    //父进程代码,更新数据库
    updatedb();
    //父进程解除2号信号屏蔽
    printf("%d进程:解除对%d号信号的屏蔽\n",getpid(),signum);
    if(sigprocmask(SIG_SETMASK,&oldset,NULL) == -1){
        perror("sigprocmask");
        return -1;
    }
    //父进程收尸
    if(wait(NULL) == -1){
        perror("wait");
        return -1;
    }
    return 0;
}//编译执行

编译执行,结果如下:

        ​​​​​​​ uc_11_信号集_信号屏蔽_睡眠_暂停_闹钟_第3张图片

         解释如下:

        对于不可靠信号(1~31),通过sigprocmask()函数设置信号掩码以后,每种被屏蔽信号中只有第一个会被阻塞,并在解除屏蔽后被递送,其余的则全部丢失。

        uc_11_信号集_信号屏蔽_睡眠_暂停_闹钟_第4张图片

        对于可靠信号(34~64),通过sigprocmask()函数设置信号掩码以后,每种被屏蔽信号中的每个信号都会被阻塞,并按先后顺序排队,一旦解除屏蔽,这些信号会被一次递送。

        uc_11_信号集_信号屏蔽_睡眠_暂停_闹钟_第5张图片

 

3.3  sigpending()

        #include

        int sigpending (sigset_t* sigset);

                功能:获取调用进程的未决信号集

                sigset:输出未决信号集

                返回值:成0-1 

你可能感兴趣的:(uc,unix)