0725------Linux基础----------信号

1.基础知识巩固

  1.1 中断分为两类:

    a)硬中断,就是通常所说的中断,中断处理程序运行在内核态,需要一定的硬件支持;

    b)软中断,是在软件层次上对中断的一种模拟,就是常说的信号,它的处理程序运行在用户态。它是软件级别的,不需要特定的硬件支持。

  1.2 常见的信号:(用kill -l 和 man 7 signal 命令可查看)

    a)SIGINT : CTRL+C , 2

    b)SIGQUIT: CTRL+\ , 3

    c) SIGKILL: kill -9 pid 命令 ,9

    d)SIGPIPE: 与关闭的TCP连接有关,13

    e)SIGCHLD: 子进程消亡,出现僵尸进程,17

    f)SIGALARM: alarm 函数,14

  1.3 关于信号的术语:

    a)发送信号;

    b)接收信号。

  1.4 对信号的处理方式有三种(这里注意,杀死一个进程的方法是,kill -9 pid 或者 pkill filaname)

    a)自行编写handler

    b)忽略

    c)采用系统默认的行为

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#define ERR_EXIT(m)     do {         perror(m);        exit(EXIT_FAILURE);    }while(0)

/*
 * 处理 ctrl + C 信号 
 * 忽略 ctrl + \  信号
 */
void handler(int signum){
    printf("    catch Ctrl + C\n");
}

int main(int argc, const char *argv[])
{
    if(signal(SIGINT, handler) == SIG_ERR){
        ERR_EXIT("signal");
    }

    if(signal(SIGQUIT, SIG_IGN) == SIG_ERR){ //ctrl +         ERR_EXIT("signal");
    }

    for(;;)
        pause(); //暂停程序直到收到信号
    return 0;
}

2. 发送和接收信号

  2.1 发送信号的方法:

    a)kill -signum pid 命令:向pid进程发送signum信号,例如,kill -9 pid 就是向某进程发送SIGKILL信号杀死进程,kill -2 pid就是向某个进程发送CTRL + C 信号,使进程终止;

    b)使用系统调用kill 函数;

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#define ERR_EXIT(m)     do {         perror(m);        exit(EXIT_FAILURE);    }while(0)

/*
 * kill 函数  向一个进程发送某个信号
 */

void handler(int signum){
    printf("    catch Ctrl + C\n");
}

int main(int argc, const char *argv[])
{
    if(signal(SIGINT, handler) == SIG_ERR){
        ERR_EXIT("signal");
    }

    sleep(3); //睡三秒后向本进程发送 SIGINT 信号

    if(kill(getpid(), SIGINT) == -1){
        ERR_EXIT("kill");
    }

    for(;;)
        pause(); //暂停程序直到收到信号
    return 0;
}

    c)alarm 函数,给自己发送一个SIGALRM 信号,注意每个进程只能有一个alarm计时器,新的会取代旧的。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#define ERR_EXIT(m)     do {         perror(m);        exit(EXIT_FAILURE);    }while(0)

/*
 * alarm 函数 给自己发送 SIGALAM 函数
 */
void handler(int signum){
    printf("    catch sigalrm\n");
}

int main(int argc, const char *argv[])
{
    if(signal(SIGALRM, handler) == SIG_ERR){
        ERR_EXIT("signal");
    }
    alarm(3);
    for(;;)
        pause(); //暂停程序直到收到信号
    return 0;
}

  

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#define ERR_EXIT(m)     do {         perror(m);        exit(EXIT_FAILURE);    }while(0)

/*
 * alarm 计时器
 */
void handler(int signum){
    static int beeps = 0;
    printf("beep\n");
    if(++beeps < 5){
        alarm(1);
    }
    else{
        printf("boom\n");
        exit(EXIT_SUCCESS);
    }
}

int main(int argc, const char *argv[])
{
    if(signal(SIGALRM, handler) == SIG_ERR){
        ERR_EXIT("signal");
    }
    alarm(3);

    for(;;)
        pause();
    return 0;
}  

  

  2.2 sleep函数可以被信号中断。

    2.2.1 如本例,程序先睡10s,若在这10s内按了ctrl + C 即向程序发送了该信号,信号处理完毕后,此时程序不会再重新执行sleep,而是从sleep 的下一行开始执行。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#define ERR_EXIT(m)     do {         perror(m);        exit(EXIT_FAILURE);    }while(0)

/*
 * sleep 函数能被 信号打断
 */

void handler(int signum){
    printf("    catch Ctrl + C\n");
}

int main(int argc, const char *argv[])
{
    if(signal(SIGINT, handler) == SIG_ERR){
        ERR_EXIT("signal");
    }

    sleep(10);   printf("after handler\n);
    return 0;
}

    2.2.2 因为sleep函数返回剩余还没有睡眠的秒数,因此,可以使用一个循环,使得程序被打断后继续睡眠,直到结束。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#define ERR_EXIT(m)     do {         perror(m);        exit(EXIT_FAILURE);    }while(0)

/*
 * sleep 返回剩余没有睡眠的秒数
 * 因此可以使用一个循环 来继续被打断的sleep
 */

void handler(int signum){
    printf("    catch Ctrl + C\n");
}

int main(int argc, const char *argv[])
{
    if(signal(SIGINT, handler) == SIG_ERR){
        ERR_EXIT("signal");
    }

    //sleep(10); //sleep 期间若有信号 直接结束

    int n = 10;
    do{
        n = sleep(n);
        printf("interupted left %d seconds\n", n);
    }while(n > 0);

    return 0;
}    

  2.3 已经发送但是还未被接收的信号称为待处理信号。系统采用一个向量表示待处理信号的集合,所以每种类型的信号最多只有一个待处理信号。一个待处理信号最多只能被接收一次。

3.信号处理

  3.1 信号处理带来的一些问题:

    a)待处理信号会被阻塞:如果程序正在处理 SIGINT,那么此时到达一个SIGINT,会被阻塞,成为待处理信号,一直到上一个SIGINT处理程序返回。

    b)待处理信号不会排队等待:每种类型的信号最后只有一个待处理信号,如果程序中已经有一个SIGINT待处理信号,此时到达一个SIGINT,会被丢弃。

    c)系统调用可以被handler打断,即EINTR,例如,read(fd...)是一个慢速的系统调用,此时如果有信号到来,会切换到该信号的handler中,当从该handler返回的时候,read也返回 -1, errno 置为 EINTR。

  3.2 例子。父进程产生 10 个子进程,每个子进程执行完一些操作后自动退出,此时父进程收到 10 个 SIGCHLD 信号(这10个信号几乎是同一时间发出的)。然后父进程开始处理第一个 SIGCHLD 的handler,里面sleep 2s,在这期间,第二个信号到来,被阻塞为待处理信号,后面的信号都被简单的丢弃。因此,本程序后面的子进程都变成了僵尸进程。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#define N 10
#define ERR_EXIT(m)     do {         perror(m);        exit(EXIT_FAILURE);    }while(0)

/*
 *  待处理信号只能有一个
 *  其他的 会被丢弃
 */

//处理 sigchld 信号,防止产生僵尸进程
void handler(int signum){
    pid_t pid;
    if((pid = waitpid(-1, NULL, 0)) < 0){
        ERR_EXIT("WAITPID");
    }
    printf("handler process child %d\n", pid);
    sleep(2); //故意阻塞别的SIGCHLD 信号,只有一个SIGCHLD信号处理完毕才能处理下一个
}

int main(int argc, const char *argv[])
{
    if(signal(SIGCHLD, handler) == SIG_ERR){
        ERR_EXIT("signal");
    }
    pid_t pid;
    int i;
    for(i = 0; i < N; i++){
        if((pid = fork()) < 0){
            ERR_EXIT("fork");
        }
        else if(pid == 0){
            printf(" child pid: %d\n", getpid());
            sleep(2);
            exit(EXIT_SUCCESS);
        }
    }
    for(;;)
        pause(); //暂停程序直到收到信号
    return 0;
}

  3.3 解决以上问题的关键思想:

    a)信号对应的向量置为 1,说明至少存在一个该信号;

    b)当处理 SIGCHLD 信号时,应该尽可能多处理子进程。

  3.4 例子。对上例的改进。程序分析:

    a)10个子进程几乎同时消亡,发出10个 SIGCHLD 信号;

    b)父进程接收到一个SIGCHLD,执行waitpid,回收第一个子进程,然后睡眠2 s;

    c)在这 2 s 内,第二个 SIGCHLG 变成待处理,其余丢失;

    d)睡眠结束,此时存在9个僵尸进程,然后执行 9+1 次while 循环,全部处理完毕;

    e)以上都属于第一个 SIGCHLD 的handler;

    f)处理第二个 SIGCHLD,此时没有子进程,waitpid 失败,退出。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#define N 3
#define ERR_EXIT(m)     do {         perror(m);        exit(EXIT_FAILURE);    }while(0)

/*
 * 多处理 sigchld 信号
 */

void handler(int signum){

    pid_t pid;
    while((pid = waitpid(-1, NULL, 0)) > 0){
        printf("handler process child %d\n", pid);
        sleep(2);
    }
    printf("end\n");
}

int main(int argc, const char *argv[])
{
    if(signal(SIGCHLD, handler) == SIG_ERR){
        ERR_EXIT("signal");
    }
    pid_t pid;
    int i;
    for(i = 0; i < N; i++){
        if((pid = fork()) < 0){
            ERR_EXIT("fork");
        }
        else if(pid == 0){
            printf(" child pid: %d\n", getpid());
            sleep(2);
            exit(EXIT_SUCCESS);
        }
    }
    int n;
    char buf[1024] = {0};
    //阻塞在这里
    //Linux 自动重启系统调用
    if((n = read(STDIN_FILENO, buf, 1024)) < 0){
        ERR_EXIT("read");
    }
    printf("parents process input\n");

    return 0;
}

  3.5 处理僵尸进程:

    a)在父进程中编写同步代码,可以按照顺序或者异步的方式逐个回收子进程。这种方法的缺陷是需要准确把握子进程的消亡时机。

    b)处理SIGCHLD信号,这个方法提供了一种异步处理能力。其中要注意:处理SIGCHLD信号时,应该使用while循环尽可能多去处理子进程,这里主要是防止信号的阻塞和丢失问题。

0725------Linux基础----------信号

时间: 2024-08-04 22:40:01

0725------Linux基础----------信号的相关文章

Linux基础之常用命令(二)

一. Linux系统上命令通用格式为: COMMAND    [OPTIONS]    [ARGUMENTS] 命令     选项      参数 1.  命令 命令可分为两类:内置命令和外部命令 内置命令:由shell自带的命令 例如:cd,break,(un)alias,type,file,history,pwd--是系统在启动之初就已经调入到内存中,是常驻内存之中,执行效率高 外部命令:独立的可执行文件,文件即命令:系统根据环境变量中的设置查找来执行.例如:ls.ifconfig.user

[Z] linux基础编程:IO模型:阻塞/非阻塞/IO复用 同步/异步 Select/Epoll/AIO

原文链接:http://blog.csdn.net/colzer/article/details/8169075 IO概念 Linux的内核将所有外部设备都可以看做一个文件来操作.那么我们对与外部设备的操作都可以看做对文件进行操作.我们对一个文件的读写,都通过调用内核提供的系统调用:内核给我们返回一个file descriptor(fd,文件描述符).而对一个socket的读写也会有相应的描述符,称为socketfd(socket描述符).描述符就是一个数字,指向内核中一个结构体(文件路径,数据

linux基础学习笔记——操作大全

作者:liaoyi 更新时间:2014-6-2 ****************基本操作***************** 关机 shutdown -h now    root用户               init 0              root用户halt      root+一般用户poweroff 重启shutdown -r now    root用户init6     root用户reboot            root+一般用户 注意:1.shutdown 比较灵活,可

系统管理员-Linux基础学习-第一部分内容。

6月30日前完成Linux的基础部分学习. 包括内容: 马哥的Linux基础教程 鸟哥的Linux私房菜-基础学习篇 第一部分 认识linux及linux主机规划与安装. 课程内容包括: (1)计算机体系架构与linux背景常识 (2)Linux主机规划与安装及vmware.xmanager的使用. (3)启动帮助与开关linux系统及基本操作知识. 具体内容笔记: 第一节:计算机体系架构与linux背景常识 1)计算机组成主要分为五个部分:控制单元.运算单元.存储单元.输入\输出单元. 1.

linux基础编程:IO模型:阻塞/非阻塞/IO复用 同步/异步 Select/Epoll/AIO(转载)

IO概念 Linux的内核将所有外部设备都可以看做一个文件来操作.那么我们对与外部设备的操作都可以看做对文件进行操作.我们对一个文件的读写,都通过调用内核提供的系统调用:内核给我们返回一个file descriptor(fd,文件描述符).而对一个socket的读写也会有相应的描述符,称为socketfd(socket描述符).描述符就是一个数字,指向内核中一个结构体(文件路径,数据区,等一些属性).那么我们的应用程序对文件的读写就通过对描述符的读写完成. linux将内存分为内核区,用户区.l

Linux基础精华(转)

Linux基础精华 (继续跟新中...) 常用命令: Linux shell 环境 让你提升命令行效 率的 Bash 快捷键 [完整版] 设置你自己的liux alias Linux的Find使用 Linux命令Find实>例 Linux命令xargs+cut grep 小技巧 Linux任务前后台的切换 Linux运行与控制后台进程的方法:nohup, setsid, &, disown, screen Linux的rsh配置rhost Linux的nm查看动态和静态库中的符号 Linux

4、linux基础命令详解

linux基础命令 Linux图形界面和命令行界面的切换 进入Linux桌面环境后,可以使用键盘上的"Ctrl+Alt+F1~F6"组合键来切换不同的tty界面,Linux默认提供了6个命令行界面(F1-F6),比如"Ctrl+Alt+F1"就是切换到tty1: 在命令行模式下,想要切换回图形界面可以使用组合键"Ctrl+Alt+F7":另外,如果不是从图形界面切换到tty模式,而是系统启动时候直接进入了命令行模式,在登陆后可以使用"s

Linux基础初识(四)

Linux基础初识(四) 一.1. 系统监视和进程控制工具-top和free1) 掌握top命令的功能:top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器.2) 了解使用top命令列出系统状态时,系统默认每5秒刷新一下屏幕上的显示结果. 1. 第一行是任务队列信息 12:38:33 当前时间 up 50days 系统运行时间,格式为时:分 1 user 当前登录用户数 load average: 0.06, 0.60, 0.48

linux 基础与shell编程[笔记]

linux 基础与shell编程 linux 系统知识 linux是一个OS ,也是一个完全免费 开源 自由软件 linux 1991   荷兰大学生课程设计   unix GPL协议 GPL:General Public License 是一份GNU(完全自由软件操作系统)是一个广泛使用的自由软件许可证条款 常见的linux发行版本: Debian:Debian为一款提供您安装在计算机上使用的操作系统 (OS).操作系统就是能让您的计算机工作 的一系列基本程序和实用工具.由于 Debian 采