信号
信号共性:简单、不能携带大量信息、满足条件才发送。
信号的特质:信号是软件层面上的“中断”。一旦信号产生,无论程序执行到什么位置,必须立即停止运行,处理信号,处理结束,再继续执行后续指令。所有信号的产生及处理全部都是由【内核】完成的。
产生信号:
1. 按键产生:ctrl+c ctrl+z
2. 调用函数:kill raise abort
3. 定时器:alarm setitimer
4. 命令产生:kill
5. 硬件异常产生:段错误,浮点型错误,总线错误,SIGPIPE
状态:
未决:产生与递达之间状态。
递达:产生并且送达到进程。直接被内核处理掉。
信号处理方式: 执行默认处理动作、忽略、捕捉(自定义)
阻塞信号集(信号屏蔽字): 用来记录信号的屏蔽状态。一旦被屏蔽的信号,在解除屏蔽前,一直处于未决态。
未决信号集:本质:位图。用来记录信号的处理状态。该信号集中的信号,表示,已经产生,但尚未被处理。
信号4要素:编号、名称、对应事件、默认处理动作(忽略,终止,终止core,暂停,继续)。
kill命令 和 kill函数
int kill(pid_t pid, int signum)
参数:
pid: > 0:发送信号给指定进程
= 0:发送信号给跟调用kill函数的那个进程处于同一进程组的进程。
< -1: 取绝对值,发送信号给该绝对值所对应的进程组的所有组员。
= -1:发送信号给,有权限发送的所有进程。
signum:待发送的信号,kill -l查看。
返回值:
成功: 0
失败: -1 errno
//01_kill_parent_process.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
int main(int argc, char const *argv[])
{
int i = 0;
for (; i < 5; ++i)
{
pid_t pid = fork();
if (pid == 0)
break;
}
if (i == 2)
{
printf("child process, kill parent process after 5s.\n");
sleep(5);
kill(getppid(), SIGKILL);
while (1)
{
sleep(1);
}
}
else if (i == 5)
{
while (1)
{
printf("parent process.\n");
sleep(1);
}
}
return 0;
}
//02_kill_child3_process.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
int main(int argc, char const *argv[])
{
int i = 0;
pid_t pid = 0, pid_child_3 = 0;
for (; i < 5; ++i)
{
pid = fork();
if (pid == 0)
break;
else if (pid > 0)
{
if (i == 2)
{
pid_child_3 = pid;
}
}
}
if (i == 2)
{
while (1)
{
printf("child 3, pid = %d.\n", getpid());
sleep(3);
}
}
if (i == 5)
{
printf("parent process, kill child 3 after 5s.\n");
sleep(5);
kill(pid_child_3, SIGKILL);
}
return 0;
}
//03_raise.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
int main(int argc, char const *argv[])
{
printf("kill myself after 2s.\n");
sleep(2);
raise(SIGKILL); //kill(getpid(), SIGKILL)'
//abort();
return 0;
}
alarm
alarm 函数:使用自然计时法,定时发送SIGALRM给当前进程。
unsigned int alarm(unsigned int seconds);
参数:
seconds:定时秒数
返回值:上次定时剩余时间。无错误现象。
alarm(0); 取消闹钟。
time 命令 : 查看程序执行时间。 实际时间 = 用户时间 + 内核时间 + 等待时间。 --》 优化瓶颈 IO
//04_alarm.c
#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
int ret = alarm(10);
int i = 0;
while (1)
{
printf("%d second\n", ++i);
sleep(1);
}
return 0;
}
setitimer
setitimer函数:
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
参数:
which: ITIMER_REAL: 采用自然计时。 ——> SIGALRM
ITIMER_VIRTUAL: 采用用户空间计时 —> SIGVTALRM
ITIMER_PROF: 采用内核+用户空间计时 —> SIGPROF
new_value:定时秒数
old_value:传出参数,上次定时剩余时间。
返回值:
成功: 0
失败: -1 errno
struct itimerval {
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
}it_interval;//---> 周期定时秒数
struct timeval {
time_t tv_sec;
suseconds_t tv_usec;
}it_value; //---> 第一次定时秒数
};
e.g.
struct itimerval new_t;
struct itimerval old_t;
new_t.it_interval.tv_sec = 0;
new_t.it_interval.tv_usec = 0;
new_t.it_value.tv_sec = 1;
new_t.it_value.tv_usec = 0;
int ret = setitimer(&new_t, &old_t); 定时1秒
//05_setitimer.c
#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
struct itimerval myit = { { 0, 0 }, { 3, 0 } }; //定义3s以后发送SIGALRM信号
setitimer(ITIMER_REAL, &myit, NULL);
while (1)
{
printf("Who whill kill me?\n");
sleep(1);
}
return 0;
}
//06_setitimer_cycle.c
#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
#include <signal.h>
void catch_sig(int num)
{
printf("cat %d sig\n", num);
}
int main(int argc, char const *argv[])
{
signal(SIGALRM, catch_sig);
struct itimerval myit = { { 3, 0 }, { 5, 0 } }; //5s后,每隔3s发送1次SIGALRM信号
setitimer(ITIMER_REAL, &myit, NULL);
while (1)
{
printf("Who whill kill me?\n");
sleep(1);
}
return 0;
}
//07_myalarm.c
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
unsigned int myalarm(unsigned int seconds)
{
struct itimerval oldit, myit = { { 0, 0 }, { 0, 0 } };
myit.it_value.tv_sec = seconds;
setitimer(ITIMER_REAL, &myit, &oldit); //seconds后发送SIGALARM信号
printf("tv_sec = %d, tv_mirsec = %d\n", oldit.it_value.tv_sec, oldit.it_value.tv_usec);
return oldit.it_value.tv_sec;
}
int main(int argc, char const *argv[])
{
int ret = -1;
ret = myalarm(5);
printf("ret = %d\n", ret);
//sleep(3);
ret = myalarm(3);
printf("ret = %d\n", ret);
while (1)
{
printf("came on baby!\n");
sleep(1);
}
return 0;
}
信号集操作函数:
sigset_t set; 自定义信号集。
sigemptyset(sigset_t *set); 清空信号集
sigfillset(sigset_t *set); 全部置1
sigaddset(sigset_t *set, int signum); 将一个信号添加到集合中
sigdelset(sigset_t *set, int signum); 将一个信号从集合中移除
sigismember(const sigset_t *set,int signum); 判断一个信号是否在集合中。 在--》1, 不在--》0
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); 设置信号屏蔽字和解除屏蔽
参数:
how:SIG_BLOCK: 设置阻塞
SIG_UNBLOCK: 取消阻塞
SIG_SETMASK: 用自定义set替换mask。
set: 自定义set
oldset:旧有的 mask。
int sigpending(sigset_t *set); 查看未决信号集:
set: 传出的 未决信号集。
//08_sigpending.c
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
int main(int argc, char const *argv[])
{
sigset_t pend, sigproc;
//设置阻塞信号,等待按键产生信号
sigemptyset(&sigproc); //先清空
sigaddset(&sigproc, SIGTSTP);
//设置阻塞信号集
sigprocmask(SIG_BLOCK, &sigproc, NULL);
//循环取未决信号
while(1)
{
sigpending(&pend);
int i = 1;
for (; i < 32; ++i)
{
if (sigismember(&pend, i) == 1)
{
printf("1");
}
else
{
printf("0");
}
}
printf("\n");
sleep(1);
}
return 0;
}
信号捕捉
sighandler_t signal(int signum, sighandler_t handler);
参数:
signum :待捕捉信号
handler:捕捉信号后的操纵函数
信号捕捉特性:
1. 捕捉函数执行期间,信号屏蔽字 由 mask --> sa_mask , 捕捉函数执行结束。 恢复回mask
2. 捕捉函数执行期间,本信号自动被屏蔽(sa_flgs = 0).
3. 捕捉函数执行期间,被屏蔽信号多次发送,解除屏蔽后只处理一次!
//09_sigaction_mask.c
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/time.h>
void catch_sig(int num)
{
printf("begin call, catch %d sig\n", num);
sleep(5); //模拟捕捉函数执行时间较长
printf("end call, catch %d sig\n", num);
}
int main(int argc, char const *argv[])
{
//注册以下捕捉函数
struct sigaction act;
act.sa_flags = 0;
act.sa_handler = catch_sig;
sigemptyset(&act.sa_mask);
//注册捕捉
sigaction(SIGINT, &act, NULL);
//setitimer
while (1)
{
printf("who can kill me?\n");
sleep(1);
}
return 0;
}
//10_sigaction_mask.c
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/time.h>
void catch_sig(int num)
{
printf("begin call, catch %d sig\n", num);
sleep(5); //模拟捕捉函数执行时间较长
printf("begin call, catch %d sig\n", num);
}
int main(int argc, char const *argv[])
{
//注册以下捕捉函数
struct sigaction act;
act.sa_flags = 0;
act.sa_handler = catch_sig;
sigemptyset(&act.sa_mask);
//注册捕捉
sigaction(SIGINT, &act, NULL);
//setitimer
while (1)
{
printf("who can kill me?\n");
sleep(1);
}
return 0;
}
//11_child_catch.c
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
void catch_sig()
{
pid_t wpid;
while ( (wpid = waitpid(-1, NULL, WNOHANG)) > 0)
{
printf("wait child %d ok\n", wpid);
}
}
int main(int argc, char const *argv[])
{
int i = 0;
pid_t pid;
//在创建子进行横之前屏蔽SIGCHLD信号
sigset_t myset, oldset;
sigemptyset(&myset);
sigaddset(&myset, SIGCHLD);
//oldset保留现场,设置了SIGCHLD到阻塞信号集
sigprocmask(SIG_BLOCK, &myset, &oldset);
for (; i < 10; ++i)
{
pid = fork();
if (pid == 0)
break;
}
if (i == 10)
{
//parent
sleep(2); //模拟注册晚于子进程死亡
struct sigaction act;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
act.sa_handler = catch_sig;
//捕捉
sigaction(SIGCHLD, &act, NULL);
//解除屏蔽现场
sigprocmask(SIG_SETMASK, &oldset, NULL);
while (1)
{
sleep(1);
}
}
else if (i < 10)
{
//child
printf("I am %d child, pid = %d\n", i, getpid());
//sleep(10 - i);
}
return 0;
}
//12_sig_count.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
pid_t pid;
int count = 0;
int flag = 0;
void cat_sig_father()
{
sleep(1);
flag = 1;
printf("%d\n", count);
count += 2;
}
void cat_sig_child()
{
sleep(1);
flag = 1;
printf("%d\n", count);
count += 2;
}
int main(int argc, char const *argv[])
{
pid = fork();
if (pid < 0)
{
printf("fork error!\n");
return;
}
else if(pid == 0)
{
//child
count = 1;
signal(SIGUSR2, cat_sig_child);
pid_t ppid = getppid();
while (1)
{
if (flag == 1)
{
kill(ppid, SIGUSR1);
flag = 0;
}
}
}
else
{
sleep(0.01);
count = 2;
signal(SIGUSR1, cat_sig_father);
kill(pid, SIGUSR2);
while (1)
{
if (flag == 1)
{
kill(pid, SIGUSR2);
flag = 0;
}
}
}
return 0;
}