UC成长之路11

回顾:
UC成长之路1
UC成长之路2
UC成长之路3
UC成长之路4
UC成长之路5
UC成长之路6
UC成长之路7
UC成长之路8
UC成长之路9
UC成长之路10

一、信号的产生

信号的产生有三种方式

  • 硬件:如Ctrl+c, Ctrl+\
  • 使用Linux命令产生:kill -signum pid
  • 使用函数产生信号:kill(2), raise(3), alarm(2)
    这里详细讲解alarm(2)
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
//功能:产生一个信号SINGNAL,递送给当前进程
//参数
//seconds:指定秒数,如果seconds==0没有新的闹钟使用,取消所有闹钟的闹钟设置
//返回值:原来闹钟的剩余时间

eg:举例说明alarm(2)的使用

#include <stdio.h>
#include <unistd.h>

int main(void)
{
    int i;
    alarm(1);
    //先測試自己的機器一秒能大概打印多少個數字
    for(i=1;i>0;i++){//注意這裏不是死循環,當超出計算機表達的數字範圍後i會變爲負數
        printf("%d\n", i); 
    }   
    return 0;
}

在这里插入图片描述

#include <stdio.h>
#include <unistd.h>

int main(void)
{
    int i;
    //設置鬧鍾
    alarm(5);
    for(i=1;i<690000;i++){
        printf("%d\n", i); 
    }   
    //取消鬧鍾,將原來的鬧鍾沒有執行的時間返回
    int remain_time=alarm(0);
    printf("remain_time=%d\n", remain_time); 
    return 0;
}

在这里插入图片描述

  • 补充:函数pause(2)
#include <unistd.h>
int pause(void);
//功能:等待一个信号。让进程睡眠,直到一个信号到达
//返回值:只有信号抵达,信号处理函数返回以后才返回-1。errno被设置

eg:使用alarm(2)和pause(2)实现sleep(3)函数

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void doit(int n)
{
    //不做任何操作
}
unsigned int t_sleep(unsigned int seconds)
{
    //設置鬧鍾爲seconds秒
    alarm(seconds);
    //進程停下來等待信號的到達
    pause();
    //將剩餘沒有執行的時間返回
    return (alarm(0));
}
int main(void)
{
    signal(SIGALRM, doit);
    while(1){
        t_sleep(2);
        printf("today...\n");
    }   
    return 0;
}

在这里插入图片描述

二、信号阻塞和未决信号

在这里插入图片描述

  • 工程师可以设置进程对某些信息的阻塞。信号抵达以后不对信号进行处理,结束对信号的阻塞以后,再对信号进程处理。
  • 系统为我们封装了sigset_t类型,信号集类型。了解这个类型的相关操作man 3 sigisemptyset
#include <signal.h>
int sigemptyset(sigset_t *set);
//功能:将信号集设置为空,所有的信号都被排除
//参数:
//set:指定要初始化的信号集
//返回值:成功返回0;错误返回-1
int sigfillset(sigset_t *set);
//功能:初始化信号集为满,包括所有的信号
//参数:
//set:指定要初始化的信号集
//返回值:成功返回0;错误返回-1
int sigaddset(sigset_t *set, int signum);
//功能:将信号集添加到信号集
//参数:
//set:指定信号集
//signum:指定了要添加到信号集的信号
//返回值:成功返回0;错误返回-1
int sigdelset(sigset_t *set, int signum);
//功能:从信号集中删除指定的信号
//参数:
//set:指定信号集
//signum:指定要删除的信号
//返回值:成功返回0;错误返回-1
int sigismember(const sigset_t *set, int signum);
//功能:测试信号是否是信号集的成员
//参数:
//set:指定信号集
//signum:指定要测试的信号
//返回值:如果信号是信号集的成员返回1;如果信号不是信号集的成员返回0;错误返回-1

eg:将2、3号信号添加到信号集中,并测试2号信号是否在信号集中

#include <stdio.h>
#include <signal.h>

int main(void)
{
    //聲明信號集類型
    sigset_t set;
    //初始化信號集爲空
    sigemptyset(&set);
    //添加2、3號信號
    sigaddset(&set, 2); 
    sigaddset(&set, 3); 
    //測試2號信號是否是信號集的成員
    int n = sigismember(&set, 2); 
    if(n==-1){
        printf("sigismember error...");
        return -1;
    }   
    n ? printf("set member...\n") : printf("is't set member...\n");

    return 0;
}

在这里插入图片描述

  • 进程的信号屏蔽字(信号掩码)
  • 如何将一个信号集设置为信号的掩码呢?使用sigprocmask(2)函数
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
//功能:设置进程的信号掩码
//参数
//how:有以下取值
//1).SIG_BLOCK:当前进程的信号掩码和set集合的并集,设置成进程的信号掩码
//2).SIG_UNBLOCK:将set集合里的信号从当前进程的信号掩码中移除
//3).SIG_SETMASK:信号掩码设置成set集合
//set:如果为空进程的信号集不改变,how参数将被忽略;如果不为空,信号掩码的当前值仍然在oldset中返回
//oldset:保存原来的信号掩码集,如果是NULL,不保存原来的
//返回值:成功返回0,;错误返回-1

eg:使用signalprocmask设置进程对2号信号阻塞

#include <stdio.h>
#include <signal.h>

int main(void)
{
    sigset_t set;
    //初始化信號集set爲空
    sigemptyset(&set);
    //將2號信號添加到set集合中
    sigaddset(&set, 2); 
    //設置對2號信號阻塞
    sigprocmask(SIG_SETMASK, &set, NULL);
    while(1);//讓進程不要結束
    return 0;
}

在这里插入图片描述

  • 有些信号在阻塞的时候发送多次,进程在解除对信号的阻塞的时候,调用一个,这样的信号称为不可靠信号(1~31号信号)。信号丢失
  • 在进程对信号阻塞的时候,信号发送多次,在进程解除对信号的阻塞以后,发送几次处理几次,这样的信号称为可靠信号(34~64号信号)。

eg:

#include <stdio.h>
#include <signal.h>

void doit(int n)
{
    printf("recv...%d\n", n);
    return;
}

int main(void)
{
    int i=0;
    sigset_t set, old_set;
    signal(2, doit);
    signal(45, doit);
    //初始化信號集set爲空
    sigemptyset(&set);
    //將2,45號信號添加到set集合中
    sigaddset(&set, 2);
    sigaddset(&set, 45);
    //設置對2號信號阻塞
    sigprocmask(SIG_SETMASK, &set, &old_set);
    for(i=0; i<8000000; i++){//確保長時間運行
        printf("%d\n", i);
    }
    //恢復進程的屏蔽字
    sigprocmask(SIG_SETMASK, &old_set, NULL);

    return 0;
}

在这里插入图片描述

  • 如何检测进程的未决信号?使用sigpending(2),信号要阻塞后才能捕获未决信号
#include <signal.h>
int sigpending(sigset_t *set);
//功能:检测未决信号
//参数
//set:指定存储未决信号集的地址
//返回值:成功返回0;错误返回-1

eg:

#include <stdio.h>
#include <signal.h>

int main(void)
{
    sigset_t set, pset;
    //初始化set集合
    sigemptyset(&set);
    //將2號信號加入set集合中
    sigaddset(&set, 2); 
    //設置進程的信號掩碼爲set
    sigprocmask(SIG_SETMASK, &set, NULL);
    while(1){
        sleep(2);
        //獲取進程的未決信號
        sigpending(&pset);
        //檢測2號信號是不是未決信號的成員
        int is = sigismember(&pset, 2); 
        if(is==-1){printf("sigismember error...\n"); return -1;}
        is?printf("is member...\n"):printf("is't member...\n");
    }   
    return 0;
}

在这里插入图片描述

  • 阻塞(blocking)和未决(pending)信号都是进程控制块PCB中的一部分
    在这里插入图片描述

三、信号从产生到处理的过程

  • 1.在bash下启动进程a.out
  • 2.按下ctrl+C键,产生硬件中断,不管进程运行在用户态还是内核态,进程都会切换到内核态
  • 3.驱动程序会将中断解释为2号信号,将这个到达的信号记录到进程的PCB中
  • 4.当进程从内核态切换回用户态的时候,检测PCB中是否有信号到达
  • 5.如果没有信号到达,直接切回用户态,继续执行
  • 6.如果有信号到达,调用信号的处理函数,信号处理函数执行完毕,调用sigreture,结束信号处理函数,清空函数的栈帧,返回到内核态。执行第4步。

四、可重入函数

  • 只使用函数私有空间,不使用进程的全局空间,这样的函数称为可重入函数。(面试回答:如果函数中使用了全局变量、静态局部变量或malloc分配的内存,这样的函数就是不可重入函数。)
    eg: 不可重入函数出现错误(当2号信号到达时也执行doit函数,导致打印了相同的val值)
#include <stdio.h>
#include <signal.h>

int val;
void doit(int n)
{
    int c;
    c=val;
    c++;
    printf("c=%d\n", c); 
    val=c;
    return;
}
int main(void)
{
    signal(2, doit);
    while(1){
        doit(100);
    }   
    return 0;
}

在这里插入图片描述

五、前台作业和后台作业

ps -o pid,ppid,pgrp,session,comm

  • session是会话,会话跟终端相关,有的会话有终端,有的会话没有终端(精灵进程/守护进程)

  • 进程组:父进程创建的子进程,子进程再创建子进程都是属于同一个进程组

  • 作业:父进程和子进程是作业,而父进程和子进程的子进程不是同一个作业

  • 有终端的会话有且仅有一个前台作业,可以有多个后台作业

  • 使用ctrl+z将正在运行的作业放到后台停止

  • 使用命令jobs查看后台作业

  • 使用fg %作业号将后台作业切回到前台

  • 使用bf %作业号放在后台运行

  • command&执行作业的时候,直接放在后台
    在这里插入图片描述

  • bash脚本:将要执行的Linux命令编写到一个文件中一起执行,这样的文件就是bash脚本文件。
    eg:test.sh

#!/bin/bash
cat /etc/passwd
ls

在这里插入图片描述

  • 使用快捷键只能给前台作业发送信号,不能给后台发送信号

================================================
信号到此结束!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值