回顾:

1. 信号的作用

2. 理解信号:

软中断

可靠与不可靠信号

kill -l

3. 信号发送与注册

kill/raise

alarm

setitimer

signal

4. 信号的屏蔽

sigprocmask

sigemptyset

sigfillset

5. 信号屏蔽的切换

sigpending

sigsuspend = pause + 指定屏蔽信号

pause与sigsuspend都会被信号中断。中断的是pause与sigsuspen函数,不是进程中其他代码

sigsuspend放在sigprocmask环境中思考:

5.1.sigsuspend是否影响sigprocmask屏蔽的信号呢?

影响。使原来的屏蔽信号全部失效。当sigsuspend返回,恢复原来的屏蔽信号.

5.2.sigsuspend什么时候使用?

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>void handle(int s)
{printf("外部用户中断处理...!\n");sleep(3);printf("外部用户中断处理完毕!\n");
}main()
{int sum=0;int i;sigset_t sigs,sigt,sigu;sigemptyset(&sigs);sigemptyset(&sigt);sigemptyset(&sigu);sigaddset(&sigs,SIGINT);//sigfillset(&sigs);signal(SIGINT,handle);sigprocmask(SIG_BLOCK,&sigs,0);for(i=0;i<10;i++){printf("正在拷贝电影<%d>!\n",i);sleep(5);//模拟业务处理时间比较长printf("正在拷贝电影<%d>完毕!\n",i);sigpending(&sigu);if(sigismember(&sigu,SIGINT)){sigsuspend(&sigt);}}printf("所有电影拷贝完毕\n",sum);sigprocmask(SIG_UNBLOCK,&sigs,0);printf("over!\n");
}

一、最新版本的信号发送与处理

sigqueue/sigaction

1. 思考:信号中断函数调用中是否被其他信号中断。

信号函数调用中只屏蔽本身信号,不屏蔽其他信号。

#include <stdio.h>
#include <signal.h>void handle(int s)
{printf("Hello!start\n");sleep(10);printf("Hello!end\n");
}main()
{signal(SIGINT,handle);while(1);
}

2. 怎么保证函数调用中屏蔽指定的信号呢?

sigaction 可以指定处理函数调用的屏蔽信号

sigaction 在处理信号的时候,接受数据.

sigqueue 发送信号的时候,可以发送数据.

sigaction/sigqueue是signal/kill的增强版本,区别在于函数本身可以自动屏蔽和处理队列信号,不需要再次声明。四个函数可以混合使用。

3. 函数说明

使用sigaction/sigqueue有两个理由.

3.1 稳定

3.2 增强功能

int sigaction(
int sig, //被处理信号
const struct sigaction*action, //处理函数及其参数
struct sigaction*oldact //返回原来的处理函数结构体
)
返回:
0:成功
-1:失败struct sigaction
{
void (*sa_handle)(int); //函数指针
void (*sa_sigaction)(int,siginfo_t*,void*); //函数指针
sigset_t *mask; //屏蔽信号
int flags; //SA_SIGINFO,决定优先使用第一个函数还是第二个
void** //保留成员.
}

案例:

  1. 使用sigaction处理信号,使用kill发送信号
  2. 使用sigaction处理信号,使用sigqueue发送信号
  3. 发送信号的同时处理数据
#include <stdio.h>
#include <signal.h>
#include <unistd.h>/*
void handle(int s)
{printf("OOOK!\n");sleep(5);printf("KOOO!\n");
}
*/void handle(int s,siginfo_t* info,void *d)
{printf("OOOK:%d\n",info->si_int);//获取8888参数sleep(5);printf("KOOO!\n");
}main()
{struct sigaction act={0};//act.sa_handler=handle;act.sa_sigaction=handle;sigemptyset(&act.sa_mask);sigaddset(&act.sa_mask,SIGINT);act.sa_flags=SA_SIGINFO;sigaction(SIGUSR1,&act,0);while(1);
}
#include <stdio.h>
#include <signal.h>
#include <unistd.h>main()
{union sigval val;val.sival_int=8888;sigqueue(3972,SIGUSR1,val);
}

二、 IPC

IPC 进程之间通讯。最初Unix IPC包括:管道、FIFO、信号;

1. 基于文件

1.1.无序文件

1.1.有序文件

1.1.1.管道

1.1.1.1.有名

1.1.1.2.匿名

1.1.2.socket

2. 基于内存

2.1 无序内存

2.1.1.匿名内存

2.1.2.共享内存

2.2.有序内存

2.2.1.共享队列

3. 同步:基于内存IPC应用(共享内存数组)

信号量/信号灯

linux下进程间通信的几种主要手段简介:

管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信;

信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数);

报文(Message)队列(消息队列):消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。

共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。

信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。

套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。

三、 基于普通文件的IPC

IPC的技术提出的应用背景.

进程之间需要同步处理:

同步需要通信.

普通文件就是最基本的通信手段.

普通文件IPC技术的问题:

一个进程改变文件,另外一个进程无法感知.

解决方案:

一个特殊的文件:管道文件

A文件,写内容

#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>main()
{int *p;int fd;int i;fd=open("tmp",O_RDWR|O_CREAT,0666);ftruncate(fd,4);p=mmap(0,4,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);i=0;while(1){sleep(1);*p=i;i++;}close(fd);
}

B文件:读内容

#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>main()
{int *p;int fd;fd=open("tmp",O_RDWR);p=mmap(0,4,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);while(1){sleep(1);printf("%d\n",*p);}close(fd);
}

四、 管道文件

  1. 创建管道mkfifo
  2. 体会管道文件特点

案例:

fifoA                            fifoB

建立管道文件

打开管道                     打开管道

写数据                         读数据

关闭管道                      关闭管道

删除管道

建立管道文件:

使用linux的指令mkfifo

fifoA 文件

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <signal.h>
#include <stdlib.h>int fd;
int i;void  end(int s)
{//关闭管道close(fd);//删除管道unlink("my.pipe");exit(-1);
}main()
{signal(SIGINT,end);//建立管道mkfifo("my.pipe",0666);//打开管道fd=open("my.pipe",O_RDWR);//shutdown(fd,SHUT_RD);//关闭读权限i=0;while(1){//每隔1秒写数据sleep(1);write(fd,&i,4);i++;}
}

fifoB文件

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <signal.h>
#include <stdlib.h>int fd;
void end(int s)
{//关闭管道close(fd);exit(-1);
}main()
{int i;//打开管道signal(SIGINT,end);fd=open("my.pipe",O_RDWR);//shutdown(fd,SHUT_WR);//关闭写权限while(1){read(fd,&i,4);printf("%d\n",i);}
}

总结:

  1. read时,如果没有数据则read阻塞,而且read后数据是被删除
  2. 数据有序
  3. 打开的描述符号可以读写(two-way双工)
  4. 管道文件关闭后,数据不持久(数据消失,无法被读到).
  5. 管道的数据存储在内核缓冲中,而没有写到文件中。

五、 匿名管道

发现有名的管道的名字仅仅是内核识别是否返回同一个fd的标示。所以当管道名失去表示作用的时候,实际可以不要名字.

在父子进程之间:打开文件描述后创建进程。父子进程都有描述符号。所以管道文件没有价值。所以在父子进程中引入一个没有名字的管道:匿名管道,但在其他进程之间还是需要有名管道。

结论:

匿名管道只能使用在父子进程.

  1. 创建匿名管道
  2. 使用匿名管道

案例:

匿名管道的创建

体会匿名管道的特点

int pipe(int fd[2]);//创建匿名管道.打开管道.拷贝管道.关闭读写

fd[0]:只读(不能写)

fd[1]:只写(不能读)

注意:数据无边界.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main()
{int fd[2];int r;char buf[20];printf("%d\n",getpid());r=pipe(fd);write(fd[1],"hello",5);write(fd[1],"world",5);r=read(fd[0],buf,20);buf[r]=0;printf("%s\n",buf);write(fd[1],"louis",5);r=read(fd[0],buf,20);buf[r]=0;printf("%s\n",buf);while(1);
}
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>main()
{int fd[2];pipe(fd);if(fork()){//parentclose(fd[0]);//只负责写,以防数据混乱while(1){write(fd[1],"Hello",5);sleep(1);}}else{//childchar buf[20];int r;close(fd[1]);//只负责读,以防数据混乱while(1){r=read(fd[0],buf,20);buf[r]=0;printf("::%s\n",buf);}}
}

综合:(只为示范进程+通信,不推荐这样使用)

建立两个子进程:

一个负责计算1-5000的素数

另外一个负责计算5001-10000

父进程负责存储

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sched.h>int idx=0;
int fddata;
void handle(int s)
{int status;if(s==SIGCHLD){wait(&status);idx++;if(idx==2){close(fddata);printf("任务完成\n");exit(-1);}}
}int isprimer(int ta)
{int i=2;for(;i<ta;i++){if(ta%i==0){return 0;}}return 1;
}main()
{int a,b;int id=1;int fd[2];signal(SIGCHLD,handle);pipe(fd);while(1){if(id==1){a=2;b=50000;}if(id==2){a=50001;b=100000;}if(fork()){id++;if(id>2){break;}continue;}else{//子进程int i;close(fd[0]);for(i=a;i<=b;i++){if(isprimer(i)){write(fd[1],&i,sizeof(int));}sched_yield();}printf("%d任务完成!\n",getpid());exit(0);}}int re;char buf[20];//打开文件,准备存储close(fd[1]);fddata=open("result.txt",O_RDWR|O_CREAT,0666);while(1){read(fd[0],&re,sizeof(int));sprintf(buf,"%d\n",re);write(fddata,buf,strlen(buf));sched_yield();}
}

补充:

sched_yield()会让出当前线程的CPU占有权,然后把线程放到静态优先队列的尾端,然后一个新的线程会占用CPU。那这个和sleep()有啥区别呢?

sched_yield()这个函数可以使用另一个级别等于或高于当前线程的线程先运行。如果没有符合条件的线程,那么这个函数将会立刻返回然后继续执行当前线程的程序。而sleep则是等待一定时间后等待CPU的调度,然后去获得CPU资源(这也是usleep()为什么不准的原因)。

那么什么时候使用sched_yield()呢?man手册是这么说的

有策略的调用sched_yield()能在资源竞争情况很严重时,通过给其他的线程或进程运行机会的方式来提升程序的性能。也就是说,调用sched_yield()能让你的当前线程让出资源,通过一定的策略调用sched_yield()满足你的业务要求可以保证各个线程或进程都有机会运行。

C++学习:第六章Linux高级编程 - (七)信号、sigqueue、sigaction、IPC、管道、匿名管道相关推荐

  1. 第十六章 网络高级编程

    尽管大多数应用层协议都是基于TCP的,但除了编写像QQ服务器那样的服务端应用,很少直接使用 TCP Socket 进行编程.一般都编写基于应用层网络协议(HTTP.FTP等)的应用都是直接使用封装相应 ...

  2. 重拾Python学习(六)----------面向对象高级编程

    本文参考:廖雪峰的官方网站:https://www.liaoxuefeng.com 使用__slots__ 果我们想要限制实例的属性,比如,只允许对Student实例添加name和age属性. cla ...

  3. linux web高级编程,寒假学习 第16.17天 (linux 高级编程)

    寒假学习 第16.17天 (linux 高级编程) 笔记 总结 一.进程的基本控制(进程的同步) 1.进程的常见控制函数 pause   sleep/usleep atexit   on_exit i ...

  4. 嵌入式系统开发学习步骤(Linux高级编程学习顺序)

    2019独角兽企业重金招聘Python工程师标准>>> 嵌入式系统开发学习步骤(Linux高级编程学习顺序) 1.Linux 基础 安装Linux操作系统 Linux文件系统 Lin ...

  5. Java基础学习——第六章 面向对象编程(下)

    Java基础学习--第六章 面向对象编程(下) 一.关键词:static 1. static关键字的引入 当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new ...

  6. python归一化 增大差异_简学Python第六章__class面向对象编程与异常处理

    Python第六章__class面向对象编程与异常处理 欢迎加入Linux_Python学习群 群号:478616847 目录: 面向对象的程序设计 类和对象 封装 继承与派生 多态与多态性 特性pr ...

  7. linux教程第五版课后答案第六章,linux基础及应用第六章练习题

    linux基础及应用第六章练习题 1. 下列哪个文件的内容为当前已挂载文件系统的列表? A. /etc/inittab B. /etc/profile C. /etc/mtab D. /etc/fst ...

  8. STM32固件库(标准外设库)入门学习 第六章TIM定时器(一)

    STM32固件库(标准外设库)入门学习 第六章TIM定时器(一) 文章目录 STM32固件库(标准外设库)入门学习 第六章TIM定时器(一) 前言 一.定时器类型 1 基本定时器 2 通用定时器 3 ...

  9. C语言嵌入式Linux高级编程

    C语言本质上是编程语言的"通用语言",在今天仍具有极大的影响力.那么,C语言到底学到什么程度,才能够进行嵌入式内核.驱动的开发? 本课程为系列课程中的一个小节,入门介绍篇,介绍嵌入 ...

最新文章

  1. java线程池的工作原理_Java 线程池的介绍以及工作原理
  2. 关于交换机SVI(转)
  3. 升级PHP到5.3.3的过程及注意事项
  4. java入门第六天课程_java基础第六天
  5. python游戏编程入门 免费-Python游戏编程入门4
  6. 在Spark上用Scala实验梯度下降算法
  7. OpenCASCADE:构建配置文件
  8. boost::weak_from_raw相关的测试程序
  9. 数据结构与算法--数组:二维数组中查找
  10. 816固件a2可以升a1吗_你好,我是A2的证我在15年出的交通事故为什么不能学B2呢-免费法律咨询...
  11. Linux /proc目录详解
  12. Java 多态的实现机制
  13. Linux cannot remove 'XXX': Is a directory 解决办法
  14. 使用CrossOver的Wine配置修改容器WIndows系统版本
  15. 常见的几种推荐系统算法
  16. HTML+CSS+JavaScript实现旅游网站官网
  17. 基于矢量网络分析仪的天线近场测试方案
  18. matlab中的out模块,matlab-simulink中out模块怎么用?
  19. 【Windows7系统装什么浏览器好用】
  20. UE4 Spline的使用

热门文章

  1. 二进制与十进制数互相转换的方法及原理
  2. 可能是全网最全的移动直播 trouble shooting 手册(7)——黑屏、花屏、闪屏
  3. 考研二战备考五十天,最终成功上岸
  4. rbd 关闭互斥锁能力集
  5. jenkins的热部署_Jenkins+tomcat自动发布的热部署/重启及遇到的问题解决办法(推荐)...
  6. 微型计算机的集成在微,微型计算机的( )集成在微处理器芯片上。
  7. labuladong的算法小抄_学习数据结构和算法的框架思维
  8. 邀请函 | 3月2日 来上海参加平头哥“玄铁RISC-V生态大会”
  9. 小学计算机游戏小狐狸历险记,小狐狸历险记文字版
  10. 面试再也不用惧怕MYSQL优化,优化详解