信号
基本概念
信号掩码
信号集
信号处理函数
常见信号
SIGCHLD
SIGKILL
SIGSTOP
SIGTSTP
SIGINT
SIGALRM
SIGHUP
SIGPIPE
常用接口
kill : 发信号给进程、检查信号是否存在
killpg : 向进程组的所有成员发信号
sigaction :处置信号,建立信号处置程序
- 从C语言的角度来考虑函数指针(signal),以及回调函数handler;
raise :给自身发信号
pause : 等待信号
实例1:实现简易的shell的ctrl + c
描述
程序运行时,SIGINT
信号执行处置函数,使clt + c
变成换行
实现步骤
- 用
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;
}
思考与拓展
- 在
zsh
中按下回车,control + c,clear,shell都会输出回车,返回值分别是0,130,0;这些是怎么实现的?
- 为什么用
signal
实现,可以不采用循环输入,而用sigaction
必须采用循环输入? - 一定要注意“输出端”的空格,很容易被忽略,任何一个细节都不要放过
思考
- 信号的可重入问题?
- windows启动的时候,进入一个界面,然后显示“按任意键返回”这是怎么实现的?
- 为什么说全局变量最好不要接触信号?
- 信号量在父子进程之中会继承吗?(fork, exec)
时间
常用接口
sleep:暂停调用程序
备注:usleep, nanosleep()
ctime : 输出标准格式的时间
getitimer、setitimer : 定时器
alarm : 定时器
思考
- 简述一下挂起的过程
- 三个时间:itimer_real, itimer_virtual, itimer_prof
- struct itimerval
- TCP的定时器是否用到alarm?
- atime, ctime, mtime之间有什么区别?
- 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;
}