用户空间里面的两个进程不能直接通信,必须通过内核进行通信
用户空间中一个进程不能直接给另一个进程发信号,必须通过内核给另一个进程发信号
信号是unix或linux响应某些条件而产生的一个事件,接收到该信号的进程会采取一些相应的行动
信号通信的对象在内核中已经创建
内核空间有很多信号,可以通过命令kill -l
来查看内核中一共有多少种信号,我们会发现一共有64种
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
数字代表每一种信号的ID号,宏代表具有的功能
发送信号时,需要指定接收信号的进程ID和信号ID
比如,当我们写一个死循环代码时,我们可以通过在另一个终端输入 kill 9 进程pid
来杀死死循环的进程
同样我们可以自己写一个代码,来调用linux内核提供的kill命令。
#include <iostream>
#include <stdlib.h>
#include <signal.h>
using namespace std;
int main(int argc, char *argv[]){
int pid, sid;
if (argc < 3){
cout << "arg error" << endl;
return -1;
}
pid = atoi(argv[1]);
sid = atoi(argv[2]);
kill(pid, sid);
return 0;
}
运行的时候,需要输入死循环进程的pid号和信号id,信号id我输入的是9,pid可以通过运行ps -aux命令先找到自己运行的进程的名字再找到进程相对应的pid号
kill函数包含在signal.h
头文件中,声明如下:
/* Send signal SIG to process number PID. If PID is zero,
send SIG to all processes in the current process's process group.
If PID is < -1, send SIG to all processes in process group - PID. */
#ifdef __USE_POSIX
extern int kill (__pid_t __pid, int __sig) __THROW;
#endif /* Use POSIX. */
前面用的kill函数是发送信号,接下来讲raise函数发送信号,与kill函数不同点是,raise函数可以给自己发送信号:
代码如下:
#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
using namespace std;
int main(){
pid_t pid = fork();
switch (pid) {
case -1:
cout << "fork failed" << endl;
exit(-1);
case 0:
cout << "child raise" << endl;
raise(SIGTSTP);
cout << "raise after" << endl;
break;
default:
cout << "parent sleep" << endl;
sleep(5);
if (waitpid(0, nullptr, WNOHANG) == 0)
kill (pid, 9);
cout << "kill child process" << endl;
wait(nullptr);
cout << "endl ..." << endl;
break;
}
return 0;
}
上述代码解析:
子进程给自己发送暂停信号,父进程睡眠5秒后,杀掉子进程并回收子进程的资源
接下来讲alarm函数
alarm函数给当前进程发送一个信号,类似于raise函数,只不过alarm函数是过一段时间再发送,alarm函数的只有一个作用,所以调用时不用指定信号类型,只有一个参数是时间,指定时间过后,进程将被退出
代码如下所示:
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
using namespace std;
int main(){
cout << "alarm before" << endl;
alarm(5);
cout << "alarm after" << endl;
int i = 0;
while (1) {
cout << i++ << endl;
sleep(1);
}
return 0;
}
运行上面的代码可以看出,程序在5秒后退出,可知alarm函数正确发挥作用
问题? alarm函数如何发挥作用的呢?
接下来是pause( )函数,pause函数不需要参数,调用pause函数会将进程进入睡眠状态,直到传递了终止该进程的信号为止
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
using namespace std;
int main(){
cout << "pause before" << endl;
pause();
cout << "pause after" << endl;
int i = 0;
while (1) {
cout << i++ << endl;
sleep(1);
}
return 0;
}
运行如上代码,程序在pause()函数处暂停,进程进入睡眠状态,按Ctrl + c退出程序
接下来讲signal函数,signal函数有两个参数,一个是信号类型,另一个是自定义的函数指针,表示当收到对应的信号类型时执行自定义的函数
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
using namespace std;
void myfunc(int signum){
for (int i = 0; i < 10; i++) {
cout << "signum = " << signum << endl;
}
}
int main(){
cout << "alarm before" << endl;
signal(14, myfunc);
alarm(5);
cout << "alarm after" << endl;
int i = 0;
while (i < 10) {
cout << i << endl;
sleep(1);
i++;
}
return 0;
}
运行上面的代码,当主函数的while循环运行到i = 4后进程接收到alarm信号,开始运行自定义的函数,自定义函数运行就结束后,继续运行主函数的while循环
注意:alarm信号的信号id是14,所以signal函数的第一个参数要输入14
备注:子进程退出用exit(0)函数会返回一个SIGCHLD信号给父进程,父进程可以使用signal函数结合自定义函数对子进程的资源进行回收,防止僵尸进程的出现。