系统编程-信号与时间

本文探讨了信号编程的基本概念、信号掩码和集合,介绍了SIGINT、SIGALRM等常见信号的处理,以及kill、pause、sleep和定时器接口的使用。通过实例演示如何在shell中实现Ctrl+C的中断处理和间隔计时器,以及如何利用select实现跨平台的sleep替代。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

信号

在这里插入图片描述

基本概念

信号掩码

在这里插入图片描述
在这里插入图片描述

信号集

在这里插入图片描述

信号处理函数

在这里插入图片描述

常见信号

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

SIGCHLD

在这里插入图片描述

SIGKILL

在这里插入图片描述

SIGSTOP

在这里插入图片描述

SIGTSTP

在这里插入图片描述

SIGINT

在这里插入图片描述

SIGALRM

在这里插入图片描述

SIGHUP

在这里插入图片描述

SIGPIPE

在这里插入图片描述
在这里插入图片描述

常用接口

kill : 发信号给进程、检查信号是否存在

在这里插入图片描述

killpg : 向进程组的所有成员发信号

在这里插入图片描述

sigaction :处置信号,建立信号处置程序

在这里插入图片描述
在这里插入图片描述

  1. 从C语言的角度来考虑函数指针(signal),以及回调函数handler;

raise :给自身发信号

多线程与单线程

pause : 等待信号

在这里插入图片描述

实例1:实现简易的shell的ctrl + c

描述

程序运行时,SIGINT信号执行处置函数,使clt + c变成换行
在这里插入图片描述

实现步骤

  1. sigaction实现处置函数

代码

/*************************************************************************
	> File Name: head.h
	> Mail: 1136984246@qq.com
 ************************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/file.h>
#include <sys/time.h>
#include <pthread.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/epoll.h>
#include <dirent.h>
#include <error.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/sem.h>

// #include "common.h"
// #include "tcp_client.h"
// #include "tcp_server.h"
/*************************************************************************
	> File Name: kill.c
	> Mail: 1136984246@qq.com
 ************************************************************************/
// 屏蔽cltr + c信号
// clt + z
// 终端 vs a.out
#include "head.h"

// static 没有用
static void print(int signum) {
    printf("signum = %d\n", signum);
    printf("\n");
}

int main() {
    // kill -l : 9 ,2, 3
    int age;
    struct sigaction sa;
    sa.sa_flags = 0;
    sa.sa_handler = print;
    // signal(SIGINT, SIGING); // 
    // signal调用后,它做了什么?
    // signal 在C 语言中应该有很多讲究 ; 返回一个函数指针, SIG_ERR是一个函数指针
    // signal(SIGINT, print); // 不用循环,在内核中
    // 现在的信号与之前的信号?
    if (sigaction(SIGINT, &sa, NULL) < 0) {
        perror("sigaction");
        exit(1);
    } else {
        printf("sigaction works!\n");
    }
    while (1) {
        char buff[1024] = {0};
        ssize_t n = read(STDIN_FILENO, buff, sizeof(buff));
        if (n == -1) {
            if (errno == EINTR) {
                printf("<Shell> return : 130\n");
            }
        } else {
            printf("<Shell> received %s", buff);
        }
    }
    return 0;
}


思考与拓展

  1. zsh中按下回车control + cclear,shell都会输出回车,返回值分别是0,130,0;这些是怎么实现的?
    在这里插入图片描述
  2. 为什么用signal实现,可以不采用循环输入,而用sigaction必须采用循环输入?
  3. 一定要注意“输出端”的空格,很容易被忽略,任何一个细节都不要放过
    在这里插入图片描述

思考

  1. 信号的可重入问题?
  2. windows启动的时候,进入一个界面,然后显示“按任意键返回”这是怎么实现的?
  3. 为什么说全局变量最好不要接触信号?
  4. 信号量在父子进程之中会继承吗?(fork, exec)

时间

常用接口

sleep:暂停调用程序

在这里插入图片描述
备注:usleep, nanosleep()

ctime : 输出标准格式的时间

在这里插入图片描述

getitimer、setitimer : 定时器

在这里插入图片描述
在这里插入图片描述

alarm : 定时器

在这里插入图片描述

思考

  1. 简述一下挂起的过程
  2. 三个时间:itimer_real, itimer_virtual, itimer_prof
  3. struct itimerval
  4. TCP的定时器是否用到alarm?
  5. atime, ctime, mtime之间有什么区别?
  6. setitimer中的三种时间有什么区别?

实例2:间隔计时器

代码

/*************************************************************************
	> File Name: itimer.c
	> Mail: 1136984246@qq.com
 ************************************************************************/
#include "head.h"

void print(int sig) {
    printf("3 sec counts down...\n");
}

int main() {
    struct itimerval itv;
    itv.it_interval.tv_sec = 3; // 周期闹钟的间隔时间为3s
    itv.it_interval.tv_usec = 0;  

    itv.it_value.tv_sec = 10; // 第一次发送所需要的时间为10s
    itv.it_value.tv_usec = 0;
    // 当进程接收到SIGALRM信号,执行函数print,默认终止进程
    signal(SIGALRM, print);
    
    // 创建一个以真实时间倒计时的定时器,且不关心定时器的前一设置, 到时间点发送SIGALRM信号
    setitimer(ITIMER_REAL, &itv, NULL);

    // sleep, 被SIGALRM中断后,执行完print后,恢复后继续执行sleep
    while (1) {
        pause();
    }


    return 0;
}

实例3 :利用select实现一个可移植的sleep

在这里插入图片描述
在这里插入图片描述

思路

select在windows和linux都有具有很好的移植性,精度是微妙级别,因为select有struct timeval类型,但是sleep不可以移植是为什么?信号signal在不同系统不同,莫非sleep基于信号,而select的timeout不是基于信号?
基于信号的sleep实现

/*************************************************************************
	> File Name: 1.sleep.c
	> Mail: 1136984246@qq.com
 ************************************************************************/
#include "head.h"

void wakeup(int signum) {
    printf("wakeup...\n");
}

int main(int argc, char **argv) {
    time_t time; 
    time = atoi(argv[1]);
    signal(SIGALRM, wakeup);
    alarm(time); 
    pause();
    return 0;
}

代码

/*************************************************************************
	> File Name: com_sleep.c
	> Mail: 1136984246@qq.com
 ************************************************************************/
#include "head.h"

int main(int argc, char **argv) {
    struct timeval timeout;
    if (argc != 2) {
        fprintf(stderr, "Usage : %s time\n", argv[0]);
        exit(1);
    } 
    timeout.tv_sec = atoi(argv[1]);
    timeout.tv_usec = 0;
    printf("%ld\n", timeout.tv_sec);
    if (select(0, NULL, NULL, NULL, &timeout) == -1) {
        perror("select");
        exit(1);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值