文章目录

  • 进程间通信功能
  • 信号
    • 信号的概念
    • 产生信号的方式
    • 信号的默认(缺省)处理方式
    • 进程接收到信号后的处理方式
    • kill函数
    • alarm函数
    • raise函数
    • abort函数
    • pause函数
    • signal函数
    • 可重入函数

进程间通信功能

数据传输:一个进程需要将它的数据发送给另一个进程。
资源共享:多个进程之间共享同样的资源。
通知事件:一个进程需要向另一个或一组进程发送消息,通知它们发生了某种事件。
进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程 希望能够拦截另一个进程的所有操作,并能够及时知道它的状态改变。
Linux操作系统支持的主要进程间通信的通信机制

进程间通信的实质

系统只要创建一个进程,就会给当前进程分配4G的虚拟内存(32位操作系统),虚
拟内存 不是常说的内存条的空间,内存条的空间称之为物理内存,虚拟内存和物理
内存之间存在映 射关系 4G的虚拟内存分为3G的用户空间(0~3G)和1G(3~4G)
的内核空间, 用户空间是进程所私有的,每一个进程的用户空间只能自己访问和使用,我们之前说的栈区、堆区、数据区、代码区等都是用户空间的区域.内核空间是所有进程
所公有的,也就意味着绝大多数进程间通信方式,本质就是对内核空间的操作。

特殊的进程间通信方式

socket通信可以实现不同主机的进程间通信,其他六个只能在一台主机的多个进程
间通信,信号通信是唯一的一种异步通信机制 共享内存是所有进程间通信方式中效
率最高的,他是直接对物理内存进行操作。

信号

信号的概念

信号是软件中断,它是在软件层次上对中断机制的一种模拟。
信号可以导致一个正在运行的进程被另一个正在运行的异步进程中断,转而处理某一个 突发事件。 信号是一种异步通信方式。
每个信号的名字都以字符SIG开头。
每个信号和一个数字编码相对应,在头文件signum.h中,这些信号都被定义为正整数。
在Linux下,要想查看这些信号和编码的对应关系,可使用命令:kill -­l。
信号是由当前系统已经定义好的一些标识,每一个标识都会在特定的场合使用,并且都会对进程有一定的影响。
当信号产生时,会让当前信号做出相应的操作这些信号都是已经定义好的,我们不能自己再去创造,直接使用这些就可以。

产生信号的方式

1、当用户按某些终端键时,将产生信号
例如:终端上按“Ctrl+c”组合键通常产生中断信号SIGINT、
终端上按"Ctrl+\"键通常产生中断信号SIGQUIT、
终端上按"Ctrl+z"键通常产生中断信号SIGSTOP。
2、硬件异常将产生信号 除数为0,无效的内存访问等。这些情况通常由硬件检测到
,并通知内核,然后内核产生 适当的信号发送给相应的进程。
3、软件异常将产生信号。 当检测到某种软件条件已发生,并将其通知有关进程时,
产生信号。
4、调用kill函数将发送信号。 注意:接收信号进程和发送信号进程的所有者必须相
同,或发送信号进程的所有者必 须是超级用户。
5、运行kill命令将发送信号。 此程序实际上是使用kill函数来发送信号。也常用此命
令终止一个失控的后台进程。

信号的默认(缺省)处理方式

当进程中产生了一个信号,就会让当前进程做出一定的反应, 默认处理进程的方式
如下
1、终止进程:当信号产生后,当前进程就会立即结束
2、缺省处理:当信号产生后,当前进程不做任何处理
3、停止进程:当信号产生后,使得当前进程停止
4、让停止的进程回复运行:当信号产生后,停止的进程会回复执行(后台进程)

进程接收到信号后的处理方式

1、执行系统默认动作 对大多数信号来说,系统默认动作是用来终止该进程。
2、忽略此信号 接收到此信号后没有任何动作。
3、执行自定义信号处理函数 用用户定义的信号处理函数处理该信号。
注意:SIGKILL和SIGSTOP这两个信号只能以默认的处理方式执行,不能忽略也不
能自定义。

kill函数


例子
使用kill给父进程发送信号,然后父进程接收到信号后直接退出。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
int main(int argc, char *argv[])
{pid_t pid;pid = fork();if(pid < 0){perror("fail to fork");exit(1);}else if(pid > 0) //父进程的代码区{while(1){printf("This is parent peocess\n");sleep(1);}}else //子进程的代码区 {printf("This is son process\n");//子进程在3秒之后,让父进程退出sleep(3);//使用kill给父进程发送信号,然后父进程接收到信号后直接退出就可以了kill(getppid(), SIGINT);}return 0;
}

alarm函数


例子
如果一个程序中出现多个alarm闹钟,第一个如果没有到达指定的时间就遇到第二个。则第一个的闹钟时间清除,按照第二个alarm闹钟的时间继续向下运行。

#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{unsigned int sec;//当执行到alarm之后,代码会接着往下执行,当设定的时间到后,会产生SIGALRM信号//如果alarm之前没有设置其他闹钟,则返回0,如果之前设置了,则返回之前剩余的秒数//如果一个程序中出现多个alarm闹钟,第一个如果没有到达指定的时间就遇到第二个//则第一个的闹钟时间清除,按照第二个alarm闹钟的时间继续向下运行sec = alarm(5);printf("sec = %d\n", sec);sleep(3);sec = alarm(6);printf("sec = %d\n", sec);while(1){printf("hello world\n");sleep(1);}return 0;
}

raise函数


例子

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
int main(int argc, char const *argv[])
{int num = 0;while(1){printf("hello world\n");sleep(1);num++;//当循环执行5秒后,进程退出if(num == 5){//使用raise给当前进程本身发送信号raise(SIGALRM);//kill(getpid(), SIGALRM);}}return 0;
}

abort函数


注意:即使SIGABRT信号被加入阻塞集,一旦进程调用了abort函数,进程也还是会被终止, 且在终止前会刷新缓冲区,关闭文件描述符。
例子

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
int main(int argc, char const *argv[])
{int num = 0;while(1){printf("hello world\n");sleep(1);num++;//当循环执行5秒后,进程退出if(num == 5){abort();}}return 0;
}

pause函数


例子

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
int main(int argc, char *argv[])
{pid_t pid;pid = fork();if(pid < 0){perror("fail to fork");exit(1);}else if(pid > 0) //父进程的代码区{printf("This is parent peocess\n");//使用pause阻塞等待捕捉信号pause();}else //子进程的代码区 {printf("This is son process\n");sleep(3);kill(getppid(), SIGINT);}return 0;
}

signal函数

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
void handler(int sig);
int main(int argc, char const *argv[])
{//以默认的方式处理信号
#if 0if(signal(SIGINT, SIG_DFL) == SIG_ERR){perror("fail to signal");exit(1);}if(signal(SIGQUIT, SIG_DFL) == SIG_ERR){perror("fail to signal");exit(1);}if(signal(SIGTSTP, SIG_DFL) == SIG_ERR){perror("fail to signal");exit(1);}
#endif//以忽略的方式来处理信号
#if 0if(signal(SIGINT, SIG_IGN) == SIG_ERR){perror("fail to signal");exit(1);}if(signal(SIGQUIT, SIG_IGN) == SIG_ERR){perror("fail to signal");exit(1);}if(signal(SIGTSTP, SIG_IGN) == SIG_ERR){perror("fail to signal");exit(1);}//注意:SIGKILL和SIGSTOP这两个信号只能以默认的方式处理,不能忽略或者捕捉// if(signal(SIGKILL, SIG_IGN) == SIG_ERR)// {//     perror("fail to signal");//     exit(1);// }
#endif    //以用户自定义方式处理信号
#if 1if(signal(SIGINT, handler) == SIG_ERR){perror("fail to signal");exit(1);}if(signal(SIGQUIT, handler) == SIG_ERR){perror("fail to signal");exit(1);}if(signal(SIGTSTP, handler) == SIG_ERR){perror("fail to signal");exit(1);}
#endif  while(1){printf("hello world\n");sleep(1);}return 0;
}
void handler(int sig)
{if(sig == SIGINT){printf("SIGINT正在处理\n");}if(sig == SIGQUIT){printf("SIGQUIT正在处理\n");}if(sig == SIGTSTP){printf("SIGTSTP正在处理\n");}
}

查看pid号 ps -aux|grep a.out
Kill -9 加上进程ID号 用来杀死进程。
signal函数的返回值

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void *ret_handler;
void handler(int sig)
{printf("**********************\n");printf("nihao beijing\n");printf("welcome to 1000phone\n");printf("**********************\n");if(signal(SIGINT, ret_handler) == SIG_ERR){perror("fail to signal");exit(1);}
}
int main(int argc, char const *argv[])
{if((ret_handler = signal(SIGINT, handler)) == SIG_ERR){perror("fail to signal");exit(1);}while(1){printf("hello world\n");sleep(1);}return 0;
}

可重入函数

可重入函数是指函数可以由多个任务并发使用,而不必担心数据错误 可重入函数就是可以被中断的函数,当前函数可以在任何时刻中断它,并执行另一块代码, 当执行完毕后,回到原本的代码还可以正常继续运行。
编写可重入函数:
1、不使用(返回)静态的数据、全局变量(除非用信号量互斥)。
2、不调用动态内存分配、释放的函数。
3、不调用任何不可重入的函数(如标准I/O函数)。
注:
即使信号处理函数使用的都是可重入函数(常见的可重入函数),也要注意进入处理函数时,首先要保存errno的值,结束时,再恢复原值。因为信号处理过程中,errno值随 时可能被改变。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
void handler(int sig)
{printf("SIGINT\n");
}
int main(int argc, char *argv[])
{signal(SIGINT, handler);//案例1:
#if 0//sleep是一个可重入函数,但是当执行信号处理函数之后,不会回到原本的位置继续睡眠//sleep(10);//alarm函数是一个可重入函数,当他执行时,如果有信号产生并执行信号处理函数,执行完毕后,会继续运行alarm(10);while(1){printf("hello world\n");sleep(1);}
#endif//案例2:
#if 1char buf[32] = "";//read也是一个可重入函数,在等待终端输入时,如果产生信号并执行信号处理函数,信号处理//函数执行完毕后,可以继续输入数据,read可以读取到信号处理函数之后的数据if(read(0, buf, 20) == -1){perror("fail to read");exit(1);}printf("buf = [%s]\n", buf);
#endif  return 0;
}

linux进程间通讯-信号相关推荐

  1. Linux 进程间通讯(IPC)方式 ------- 共享内存

    Linux 进程间通讯(IPC)方式有以下几种: 1->管道(pipe)和有名管道(fifo). 2->消息队列 3->共享内存 4->信号量 5->信号(signal) ...

  2. Linux 进程间通讯方式 pipe()函数

    Linux 进程间通讯方式有以下几种: 1->管道(pipe)和有名管道(fifo). 2->消息队列 3->共享内存 4->信号量 5->信号(signal) 6-&g ...

  3. linux无名管道实验代码,Linux 进程间通讯之创建无名管道和读写无名管道

    Linux进程间通讯的方式: 1. 管道(pipe)和有名管道(FIFO). 2. 信号(signal) 3. 消息队列 4. 共享内存 5. 信号量 6. 套接字(socket) 管道通讯: 无名管 ...

  4. linux进程间通讯的几种方式的特点和优缺点,和适用场合。

    http://blog.csdn.net/kakaka2011/article/details/6636661 1. 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有 ...

  5. linux命名管道进程间通信,Linux进程间通讯--命名管道

    IPC安全 前面总结了匿名管道,如今来看命名管道:因为匿名管道的一个限制就是:只能是有血缘关系的进程间才能够通讯,好比:有两个同祖先的子进程,父子进程等:为了突破这一个限制,想让没有任何关系的两个进程 ...

  6. Linux 进程间通讯详解一

    进程间的通讯 两台主机间的进程通讯 --socket一台主机间的进程通讯 --管道(匿名管道,有名管道) --System V进程间通信(IPC)包括System V消息队列,System V信号量, ...

  7. linux进程间通讯-有名管道

    文章目录 阻塞和非阻塞概念 通过fcntl函数设置文件的阻塞特性 文件描述符概述 文件描述符的复制 有名管道 有名管道的创建 有名管道的基本读写操作 有名管道实现进程间通信 有名管道的读写规律(阻塞) ...

  8. Linux进程间通讯

    最初Unix IPC包括:管道.FIFO.信号: System V IPC包括:System V消息队列.System V信号灯.System V共享内存区: Posix IPC包括: Posix消息 ...

  9. linux进程间通讯-无名管道

    文章目录 无名管道 无名管道的创建 -- pipe函数 无名管道的读写规律 无名管道 无名管道概述 管道(pipe)又称无名管道. 无名管道是一种特殊类型的文件,在应用层体现为两个打开的文件描述符.任 ...

最新文章

  1. SSM登陆拦截器实现
  2. 能详细地讲讲stm32该怎么学吗?
  3. java编写学生管理系统_Java实现学生管理系统
  4. Python中单个下划线“_”变量的目的是什么?
  5. javascript编程风格(粗略笔记)
  6. python supervisor flask_supervisor配合uwsgi部署flask应用
  7. 第 1-2 课:你不知道的基础数据类型和包装类 + 面试题
  8. [leetcode]687. Longest Univalue Path
  9. 高级JAVA - 动态代理的实现原理和源码分析
  10. L2-002 链表去重(链表+模拟)
  11. Python机器学习算法基础概述
  12. 拼图复原_玩过上百款拼图后,我总结出这份超详细的拼图年龄对照表!(收藏贴)...
  13. 服务器卡顿修改dns,电视/盒子太卡了怎么办?教你修改DNS解决卡顿问题
  14. C语言链表的简单的尾插法
  15. 网络流量采集(一)概述
  16. 1位全加器设计—— 原理图与VHDL设计初步
  17. C++左值,右值例子理解
  18. 5G与WiFi6空口技术对比
  19. 转载:那些年他(她)们做过的“蠢事”
  20. 矩阵乘法与点乘的区别

热门文章

  1. VSLAM中的特征点三角化
  2. windows启动mysql8服务_MySQL8.0服务启动(windows10)
  3. 一篇虚拟试穿的论文介绍
  4. Nature | 机器学习在药物研发中的应用
  5. APK 签名中应该注意的一些点 (未完待续)
  6. Cell子刊:粘上你-细菌生长素介导的植物根部细菌定殖
  7. Gut:华中科大蔺蓉组开发简单便捷无创肠道生物信息获取方法
  8. ISME:昆士兰大学郭建华组-人造甜味剂会促进细菌耐药性的传播
  9. QIIME 2用户文档. 12数据筛选Filtering data(2019.7)
  10. PNAS | 根际植保素合成调控细菌对植物的促生长作用