---------------------------------信号------------------------------------------
1. 信号的作用
通知其他进程相应。进程之间一种通信机制
信号处理函数:
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
小小案例(引出什么是信号)
// signaldeal.c
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
void deal(int s)
{
printf("信号发生!\n");
}
main()
{
signal(SIGINT,deal);
while(1)
{
printf("进程执行!\n");
sleep(1);
}
}
常见处理信号命令:
------------------------------------------------
$ kill -s 信号 进程ID ----信号: 数字1-31 34-64
$kill -l -- 查看所有信号
------------------------------------------------
案例: 使用程序发送信号
int kill(pid pid, int s)
进程ID:
>0 : 发送信号到指定的进程
=0 : 发送信号到该进程所在进程组的所有进程
=-1 : 发送给所有进程,除init外
<0 : 发送给指定的进程组。
2.信号的发送 与 安装
signal
kill
3.信号的应用
3.1 延时器 timeout
SIGALRM
信号发出函数: alarm
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
说明: alarm函数在sec秒之后发送一个SIGALRM信号。
3.2 定时器(有周期性,反复发送)
#include <sys/time.h>
int setitimer(int which, // 3种计时方式ITIMER_REAL(真实时间),ITIMER_VIRTUAL,ITIMER_PROF
const struct itimerval *new_value, // 定时器
struct itimerval *old_value); // 返回原来设置的定时器,如果为NULL,不返回
struct itimerval {
struct timeval it_interval; /* next value */ 间隔时间
struct timeval it_value; /* current value */ 延时时间
};
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
案例1: 指定时间执行
#include <stdio.h>
#include <signal.h>
void deal(int s)
{
printf("起床!\n");
}
main()
{
signal(SIGALRM,deal);
alarm(5);
while(1)
{
// ....
}
}
案例2: (5秒后,每隔1秒打印)
// alarm2.c
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
void deal(int s)
{
// printf("\7");
fprintf( stdout, "\7" );
printf("起床\n");
}
main()
{
struct itimerval v = {0};
signal(SIGALRM,deal);
v.it_value.tv_sec = 5; // 延时时间
v.it_interval.tv_sec = 1 ;///间隔时间
setitimer(ITIMER_REAL,&v,0);
while(1)
{
//...
}
}
信号的应用:
系统与应用程序之间
应用与应用程序之间
父子进程之间
案例: 使用单进程 + 信号中断
使用定时器信号(不是进程),实现多任务。
/// demo1.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <curses.h>
#include <math.h>
#include <sys/time.h>
#include <string.h>
#include <time.h>
WINDOW *wtime, *wnumb;
int num,i;
void showtime(int s)
{
time_t tt;
struct tm *t;
if(s == SIGALRM)
{
tt = time(0);
//time(&tt);
t = localtime(&tt);
mvwprintw(wtime,1,1,"%02d:%02d:%02d",t->tm_hour,t->tm_min,t->tm_sec);
refresh();
//wrefresh(wtime);
//wrefresh(wnumb);
}
}
main()
{
struct itimerval val;
initscr();
curs_set(0); /// 隐藏光标
signal(SIGALRM,showtime);/// 绑定一个信号,让时间显示
bzero(&val,sizeof(struct itimerval));
val.it_value.tv_sec=0; ///设置为立即显示延时时间,可以试试,这句不能少,不然滞后显示
val.it_value.tv_usec = 1;// 间隔时间
val.it_interval.tv_sec = 1;
wnumb = derwin(stdscr,3,11,(LINES-3)/2,(COLS-11)/2); //高度3,宽度11
wtime = derwin(stdscr,3,10,0,(COLS-10)/2);
box(wnumb,0,0); // 加上边框
box(wtime,0,0);
setitimer(ITIMER_REAL,&val,0); //不可少,启动定时器
while(1)
{
num = 0;
for(i=0; i<7; ++i)
{
num = num*10 +rand()%10;
}
mvwprintw(wnumb,1,2,"%07d",num);
refresh();
wrefresh(wnumb);
sleep(1);
wrefresh(wtime);
usleep(10000);
}
endwin();
}
案例2 : 多进程 + 信号 的应用
实现父子进程之间的通信
sleep 与 pause 函数被信号影响后,它们将不会继续执行。
随机显示7位随机属,当按下空格后,随机数停止显示。
// demo2.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <curses.h>
#include <math.h>
#include <sys/time.h>
#include <string.h>
WINDOW *w;
int num;
int istop =0;
void handle(int s)
{
if(s == SIGUSR1)
{
if(istop == 0)
istop = 1;
else
istop = 0;
}
}
main()
{
initscr();
curs_set(0); // 隐藏光标
noecho(); /// 禁止输入显示
/// 创建子窗体
w = derwin(stdscr,3,11,(LINES-3)/2,(COLS-11)/2);
keypad(stdscr,TRUE); // z支持功能键
keypad(w,TRUE);
// 给窗体加边框
box(w,0,0);
refresh();
wrefresh(w);
if( fork() ) /// 父进程
{
signal(SIGUSR1,handle); // handle函数绑定到信号上
while(1)
{
if(istop == 1)
{
pause(); /// 进程挂起
}
num = 0;
num = rand()%10000000;
mvwprintw(w,1,2,"%07d",num);
refresh();
wrefresh(w);
usleep(100000);
}
}
else //子进程中处理按键
{
while(1)
{
int ch;
ch = getch();
if(ch == 32) // 32指的是空格键
{
kill(getppid(),SIGUSR1);
}
}
}
endwin();
}
4.信号的可靠与不可靠
信号有丢失现象。
分为:可靠信号 与 不可靠信号
早期: 信号1-31 31个信号,不可靠(与系统有关)
后期: 信号34-64 可靠信号(用户有关)
5.信号的操作
信号导致的问题:
1.信号的屏蔽
#include <signal.h>
int sigprocmask(int how, // 操作方式
SIG_BLOCK ----把参数set中的信号添加到信号屏蔽字中
SIG_SETMASK --- 把信号屏蔽字设置为参数set中的信号
SIG_UNBLOCK --- 从信号屏蔽字中删除参数set中的信号
const sigset_t *set, // 操作信号的集合
sigset_t *oldset); // 返回原来操作的信号集合
编程步骤:
1.声明信号集合
sigset_t sigs;
2.加入屏蔽信号
一组信号集合维护函数
2.1 清空集合 int sigemptyset(sigset_t *set);
2.2 添加信号到集合 int sigaddset(sigset_t *set, int signum);
2.3 从集合中删除信号 int sigdelset(sigset_t *set, int signum);
2.4 添加所有信号到集合 int sigfillset(sigset_t *set);
2.5 判定信号是否在集合 int sigismember(const sigset_t *set, int signum);
3.屏蔽信号
4.解除屏蔽
案例: (花费10秒钟计算1-10之间的和,外界中断信号不会影响程序的执行)
// mask.c
#include <stdio.h>
#include <signal.h>
main()
{
int sum = 0;
int i;
// 1
sigset_t sigs;
//2
sigemptyset(&sigs); // 清空信号集合
sigaddset(&sigs,SIGINT); // 添加SIGINT 这个信号到信号集合
//3 屏蔽SIGINT(Ctrl+c) 这个信号
sigprocmask(SIG_BLOCK,&sigs,0);
for(i=0; i<=10; ++i)
{
sum += i;
sleep(1);
}
printf("sum = %d\n",sum);
//4.
sigprocmask(SIG_UNBLOCK,&sigs,0);// 从信号屏蔽子中删除参数set中的信号
printf("Over!\n"); // 这个有可能被外界信号干扰
}
2.信号屏蔽的切换
int sigsuspend(const sigset_t *mask);-------------------目前还没有理解这个函数,
sigsuspend 是阻塞函数,对参数信号屏蔽。
3.查询被屏蔽的信号
int sigpending(sigset_t *set);