言之者无罪,闻之者足以戒。 ——《诗序》
命令:kill -l 可以查看内核可以发送多少种信号
命令:ps -axj 可以查看进程的状态
信号:
信号通信,其实就是内核向用户空间进程发送信号,只有内核才能发信号,用户空间进程不能发送信号
信号通信的框架:
(1)信号的发送(发送信号的进程):kill() 、raise() 、alarm()
(2)信号的接收(接收信号进程):pause()、sleep()、while(1)
(3)信号的处理(接收信号进程):signal()
一、信号的发送(发送信号的处理)
1、kill: 杀死一个进程
所需头文件 | #include <signal.h> #include <sys/types.h> | |
函数原型 | int kill(pid_t pid, int sig); | |
函数传入值 | pid: | 正数:要接收信号的进程的进程号 |
0:信号被发送到所有和pid进程在同一个进程组的进程 | ||
-1:信号发给所有的进程表中的进程(除了进程号最大的进程外) | ||
sig:信号 | ||
函数返回值 | 成功:0 | |
出错:-1 |
下面来看一下代码:
第一个程序:
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
int sig;
int pid;
if(argc <3)
{
printf("please input:\n");
return -1;
}
sig=atoi(argv[1]);
pid=atoi(argv[2]);
printf("sig=%d,pid=%d\n",sig,pid);
kill(pid,sig);
return 0;
}
第二个程序:
#include <stdio.h>
int main()
{
while(1);
return 0;
}
上面的程序实现的就是调用kill()函数来实现杀死正在运行的进程。
2、raise:发送信号给自己
所需头文件 | #include <signal.h> #include <sys/types.h> |
函数原型 | int raise(int sig); |
函数传入值 | sig:信号 |
函数返回值 | 成功:0 |
| 出错:-1 |
下面我们用程序来学习一下这个函数:
include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
int main()
{
printf("raise befor\n");
raise(9);
printf("raise after");
return 0;
}
下面用kill()函数和raise()函数来改变进程的状态:
#include "stdio.h"
#include "sys/types.h"
#include "signal.h"
#include "stdio.h"
#include "stdlib.h"
int main()
{
pid_t pid;
pid=fork();
if(pid >0)
{
sleep(8);
if(waitpid(pid,NULL,WNOHANG));
{
kill(pid,9);
}
wait(NULL);
while(1);
}
if(pid == 0)
{
printf("raise function befoer\n");
raise(SIGTSTP);
printf("raise function after\n");
exit(0);
}
return 0;
}
(1)、wait函数:
wait(int *status)
进程一旦调用了wait函数,就会立即阻塞自己,由wait函数自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个这样的子进程出现为止。
参数status用来保护被 收集进程退出时的一些状态,它是一个指向int类型的指针。但如果我们对这个子进程是如何死掉的毫不在意,只是想把这个僵尸进程消灭掉(事实上绝大多数情况我们都是这样的),我们就可以设定这个参数为NULL,
例如:pid=wait(NULL);如果成功,wait会返回被收集的子进程的进程ID,如果调用进程没有子进程,调用就会失败,此时wait返回-1,同时errno被置为ECHILD。
(2)、waitpid函数:
pid_t wait(pid_t pid,int *status,int options)
pid :进程的ID,但当pid取值不同时,意义也不同
pid >0:只等待进程ID等于pid的子进程,不管其他已经有多少子进程运行结束退出了,只要指定的子进程还没结束,waitpid就一直等下去。
pid =-1:等待任何一个子进程的退出,没有任何限制,此时waitpid和wait的作用一样
pid=0:等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬
pid< -1:等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值
参数status用来保护被 收集进程退出时的一些状态,它是一个指向int类型的指针。但如果我们对这个子进程是如何死掉的毫不在意,只是想把这个僵尸进程消灭掉(事实上绝大多数情况我们都是这样的),我们就可以设定这个参数为NULL
opion:目前在Linux中只支持WNOHANG和WUNTRACED两个选项,使用WNOHANG参数调用waitpid,即使没有子进程退出,它也会立即返回,不会像wait那样永远等下去。如果两个都不用可以直接写0。
返回值和错误 :
waitpid的返回值比wait稍微复杂一些,一共有3种情况:
1、当正常返回的时候,waitpid返回收集到的子进程的进程ID;
2、如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
3、如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
当pid所指示的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid就会出错返回,这时errno被设置为ECHILD;
(3)、exit函数
exit()函数关闭所有打开的文件并终止程序。相当于kill(getppid(),17),getppid()返回父进程的标识
exit()函数的参数会被传递给一些操作系统,通常的约定是正常终止的程序传递值0,非正常终止的程序传递非0值
3、alarm:发送闹钟信号的函数
alarm与raise函数的比较:
相同点:让内核发送信号给当前进程
不同点:
(1)alarm只会发送SIGALARM信号
(2)alarm会让内核定时一段时间之后发送信号,raise会让内核立刻发送信号
所需头文件 | #include <unistd.h> |
函数原型 | unsigned int alarm(unsigned int seconds) |
函数传入值 | seconds:指定秒数 |
函数返回值 | 成功:如果调用此alarm()前,进程中已经设置了闹钟时间,则 返回上一个闹钟时间的剩余时间,否则返回0。 |
| 出错:-1 |
常用信号的处理方式:
信号名 | 含义 | 默认操作 |
SIGHUP | 该信号在用户终端连接(正常或非正常)结束时发出,通常是在终端的控制进程结束时,通知同一会话内的各个作业与控制终端不再关联。 | 终止 |
SIGINT | 该信号在用户键入INTR字符(通常是Ctrl-C)时发出,终端驱动程序发送此信号并送到前台进程中的每一个进程。 | 终止 |
SIGQUIT | 该信号和SIGINT类似,但由QUIT字符(通常是Ctrl-\)来控制。 | 终止 |
SIGILL | 该信号在一个进程企图执行一条非法指令时(可执行文件本身出现错误,或者试图执行数据段、堆栈溢出时)发出。 | 终止 |
SIGFPE | 该信号在发生致命的算术运算错误时发出。这里不仅包括浮点运算错误,还包括溢出及除数为0等其它所有的算术的错误。 | 终止 |
SIGKILL | 该信号用来立即结束程序的运行,并且不能被阻塞、处理和忽略。 | 终止 |
SIGALRM | 该信号当一个定时器到时的时候发出。 | 终止 |
SIGSTOP | 该信号用于暂停一个进程,且不能被阻塞、处理或忽略。 | 暂停进程 |
SIGTSTP | 该信号用于暂停交互进程,用户可键入SUSP字符(通常是Ctrl-Z)发出这个信号。 | 暂停进程 |
SIGCHLD | 子进程改变状态时,父进程会收到这个信号 | 忽略 |
SIGABORT | 该信号用于结束进程 | 终止 |
下面给出一段程序:
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
int main()
{
int i=0;
printf("alarm befor\n");
alarm(8);
printf("alarm after\n");
while(i < 20)
{
printf("process working time is %d\n",i);
i++;
sleep(1);
}
return 0;
}
到此为止信号的发文送相关的函数就已经说完了。