进程间通讯
引入
概述
分类
1.信号量
2.管道
3.消息队列
4.socket(套接字)
5.内存共享
信号
概述
信号是Linux进程间通信的最古老方式
特点
简单
不能携带大量信息
满足某个特设条件才发出。
完整的信号周期
信号的注册
信号的产生
信号的处理函数
信号的注销
注意:这里的信号的注册,产生,注销是信号的内部机制,不是信号的函数实现
信号的编号
信号的产生
kill函数(他杀)
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{
int pid = fork();
if(pid == 0)
{
//进入子进程
while(1)
{
printf("子进程%u在玩游戏\n",getpid());
sleep(1);
}
_exit(-1);
}
else if(pid > 0)
{
//进入父进程
printf("给你三秒钟去写作业\n");
sleep(3);
kill(pid,SIGKILL);
wait(NULL);
printf("%d被%d干掉了\n",pid,getpid());
}
return 0;
}
raise函数(自杀)
作用:给当前进程自己发指定信号,等价于kill(getpid(),sig),自杀
语法:
所需头:
#include <signal.h>
函数:
int raise(int sig);
参数:
sig:信号编号
返回值:
成功:0
失败:非0
示例:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{
int i = 0;
while (1)
{
sleep(1);
printf("好无聊,想去死%d\n",i);
if(i == 3)
{
printf("死给你看\n");
raise(SIGKILL);
}
i++;
}
wait(NULL);
return 0;
}
abort函数(自杀)
作用:给自己发送异常终止信号(6)SIGABRT,并产生core文件,等价于kill(getpid(),sig),自杀
#include <stdio.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
char myfo[3] = {"我要发信号了","我真的要发信号了","拜拜了您呐"};
int i = 0;
while (1)
{
char *p = myfo[i];
printf("%s\n",p);
sleep(1);
i++;
if(i == 3)
abort();
}
return 0;
}
alarm函数(自杀)
作用:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <stdlib.h>
#include <sys/time.h>
int main(int argc, char const *argv[])
{
int seconds = 0;
seconds = alarm(5);
printf("计时器开始执行,seconds=%d\n",seconds);
sleep(3);
seconds = alarm(5);
printf("计时器开始执行seconds=%d\n",seconds);
while(1);
return 0;
}
setitimer函数(计时器)
作用:
设置定时器(闹钟),可代替 alarm 函数。精度微秒 us,可以实现周期定时。
#include <stdio.h>
#include <sys/time.h>
#include <signal.h>
void fun()
{
printf("文件已被处理\n");
}
int main(int argc, char const *argv[])
{
struct itimerval new_a;
new_a.it_value.tv_sec = 5;
new_a.it_value.tv_usec = 0;
new_a.it_interval.tv_sec = 1;
new_a.it_interval.tv_usec = 0;
signal(SIGALRM,fun);
setitimer(ITIMER_REAL,&new_a,NULL);
while(1);
return 0;
}
pause函数
作用:
将调用进程挂起直至捕捉到信号为止。这个函数通常用于判断信号是否已到
语法:
头文件:
#include <unistd.h>
函数:
int pause(void)
返回值:
知道捕获到信号,pause函数才返回-1,且errno被设置成EINTR
示例:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void fun()
{
printf("进程已被执行\n");
}
int main(int argc, char const *argv[])
{
printf("进程一开始,当前进程是%d\n",getpid());
signal(SIGALRM,fun);
alarm(3);
int x = pause();
printf("文件结束%d\n",x);
return 0;
}
信号处理
方式
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
void myfun()
{
printf("自定义函数\n");
_exit(0);
}
int main(int argc, char const *argv[])
{
signal(SIGINT,myfun);
while(1);
return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
char *p;
void fun(int sigcode)
{
if(p != NULL)
{
free(p);
}
printf("p空间被释放了\n");
_exit(0);
}
int main(int argc, char const *argv[])
{
p =malloc(50);
strcpy(p,"helloSIG");
signal(SIGINT,fun);
while(1)
{
printf("%s\n",p);
sleep(1);
}
return 0;
}
struct sigaction
{
void (*sa_handler)(int); // 旧的信号处理函数指针
void (*sa_sigaction)(int, siginfo_t *, void *); // 新的信号处理函数指针
sigset_t sa_mask; // 信号阻塞集
int sa_flags; // 信号处理的方式
void (*sa_restorer)(void); // 已弃用
};
/*
1,sa_handler,sa_sigaction:信号处理函数指针,和signal()里的函数指针用法
一样,应根据情况给 sa_sigaction、sa_handler 两者之一赋值,其取值如下:
a SIGIGN:忽略该信号
b SIGDFL:执行系统默认动作
c 处理函数名:自定义信号处理函数
2,sa_mask:信号阻塞集,在信号处理函数执行过程中,临时屏蔽指定的信号。
3,sa_flags:用于指定信号处理的行为,通常设置为 0,表使用默认属性。它可
以是以下值的“按位或”组合:
SA_RESTART:使被信号打断的系统调用自动重新发起(已经废弃)
SA_NOCLDSTOP:使父进程在它的子进程暂停或继续运行时不会收到 SIGCHLD
信号。
SA_NOCLDWAIT:使父进程在它的子进程退出时不会收到 SIGCHLD 信号,这
时子进程如果退出也不会成为僵尸进程。
SA_NODEFER:使对信号的屏蔽无效,即在信号处理函数执行期间仍能发出这
个信号。
SA_RESETHAND:信号处理之后重新设置为默认的处理方式。
SA_SIGINFO:使用 sa_sigaction 成员而不是 sa_handler 作为信号处理
函数
void(*sa_sigaction)(int signum, siginfo_t *info, void *context);
参数说明:
signum:信号的编号。
info:记录信号发送进程信息的结构体。
context:可以赋给指向ucontext_t类型的一个对象的指针,以引用在传递信
号时被中断的接收进程或线程的上下文。
*/
示例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
void fun(int sigcode)
{
printf("ctrl+c的信号编码是:%d\n",sigcode);
_exit(-1);
}
int main(int argc, char const *argv[])
{
struct sigaction cat;
cat.sa_handler = fun;
sigaction(SIGINT,&cat,NULL);
while(1);
return 0;
}
示例2::发出定义信号
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
void fun(int sigcode)
{
printf("SIGUSR1得信号编号是:%d\n",sigcode);
_exit(-1);
}
int main(int argc, char const *argv[])
{
struct sigaction cat;
cat.sa_handler = fun;
sigaction(SIGUSR1,&cat,NULL);
kill(getpid(),SIGUSR1);
while(1);
return 0;
}
可重入函数
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
void show(int pid)
{
for(int i = 0;i < 3;i++)
{
char buf[50] = {0};
sprintf(buf,"进程%d被%d次执行\n",pid,i,"\r\n");
write(1,buf,sizeof(buf));
sleep(1);
}
}
int main(int argc, char const *argv[])
{
for (int i = 0; i < 5; i++)
{
int pid = fork();
if(pid == 0)
{
printf(getpid());
_exit(-1);
}
else if(pid > 0)
{
printf("子进程%d被创建\n",pid);
_exit(-1);
}
}
while(1)
{
int id = waitpid(-1,NULL,WNOHANG);
if(id > 0)
{
printf("子进程在被回收\n",id);
sleep(1);
}
else if(id == 0)
{
break;
}
}
return 0;
}
信号集
概述
自定义信号集函数
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
int main(int argc, char const *argv[])
{
//定义一个信号集
sigset_t set;
//清空set集
sigemptyset(&set);
//将SIGINT添加到set集合中
sigaddset(&set, SIGINT);
//将SIGTSTP添加到set集和中
sigaddset(&set, SIGTSTP);
if (sigismember(&set, SIGINT))
{
printf("SIGINT是在set集合中\n");
}
else
{
printf("SIGINT不在set集合中\n");
}
//将SIGINT从集合中删除
sigdelset(&set, SIGINT);
if (sigismember(&set, SIGINT))
{
printf("SIGINT是在set集合中\n");
}
else
{
printf("SIGINT不在set集合中\n");
}
return 0;
}
信号阻塞集
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
int main(int argc, char const *argv[])
{
//定义一个信号集
//vscode中编写的sigset_t会报错,不管
sigset_t set;
//清空set集
sigemptyset(&set);
//将SIGINT添加到set集合中
sigaddset(&set, SIGINT);
//将set集合添加到阻塞集
//vscode中编写的SIG_BLOCK,SIG_BLOCK会报错不管
sigprocmask(SIG_BLOCK, &set, NULL);
printf("SIGINT信号将在5秒后从阻塞集中删除\n");
sleep(5);
sigprocmask(SIG_UNBLOCK, &set, NULL);
while (1);
return 0;
}