进程与线程(三)

进程与线程(三)

  • 进程间通信
  • 传统间的进程间通信机制
    • 无名管道
      • 无名管道的特征
      • 无名管道的创建
      • 父子进程通信
      • 测试管道的大小
      • 管道读写易出现的问题
    • 有名管道
      • 创建有名管道
      • 有名管道的写端代码
      • 有名管道的读端代码
    • 信号
      • 信号的特征
      • 产生信号
        • 硬件来源
        • 软件来源
          • 发送信号的函数:kill() raise()
          • 定时器信号
      • 响应信号
      • 信号安装:
        • 忽略信号
        • 执行缺省操作
        • 捕捉信号

进程间通信

在这里插入图片描述
常用的进程间通信方式
1、传统的进程间通信方式
无名管道(pipe)、有名管道(fifo)和信号(signal)
2、System V IPC对象
共享内存(share memory)、消息队列(message queue)和信号灯(semaphore)
3、BSD
套接字(socket)

传统间的进程间通信机制

无名管道

在这里插入图片描述

无名管道的特征

这里所说的管道主要指无名管道,它具有如下特点:
1、只能用于具有亲缘关系的进程之间的通信
2、半双工的通信模式,具有固定的读端和写端
3、管道可以看成是一种特殊的文件,对于它的读写可以使用文件IO如read、write函数。

在这里插入图片描述

无名管道的创建

在这里插入图片描述

父子进程通信

(1)创建无名管道
(2)创建子进程
(3)完成父子通信业务
通信流程:子进程发消息给父进程
思路:fd[0]读端 fd[1]写端 ----》固定的
子进程:发消息(fd[1]有效) fd[0]关闭掉
父进程:收消息(fd[0]有效) fd[1]关闭掉

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
int main(int argc, const char *argv[])
{//1,创建无名管道//定义数组,存储读写的文件描述符int fd[2];if(pipe(fd) < 0){perror("pipe error");return -1;}printf("pipe ok!\n");//2,创建子进程pid_t pid = fork();if(pid < 0){perror("fork error");close(fd[0]);close(fd[1]);return -1;}else if(0 == pid){//子进程的地址空间//关闭读端close(fd[0]);//发消息(write)//定义消息文本char Message[100] = {0};printf("请输入需要发送的消息:");fgets(Message, sizeof(Message), stdin);//使用write写入到管道中write(fd[1], Message, strlen(Message));printf("send ok!\n");}else{//父进程的地址空间//关闭写端close(fd[1]);//收消息(read)//定义接收消息的地址空间char Message[100] = {0};read(fd[0], Message, sizeof(Message));printf("来自于子进程:%s\n",Message);}return 0;
}

在这里插入图片描述

测试管道的大小

思路:往管道中不管写入,直到写不进去,此时写入的字节数就是管道容量大小

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
int main()
{//测试管道大小//创建管道int fd[2];if(pipe(fd) < 0){return -1;}printf("create pipe ok!\n");//写入数据,不断写入//1KB == 1024bytechar buf[1024] = {0};int size = 0;while(1){int wr_count = write(fd[1], buf, 1024);//每次写入1024个字节 即:1KB大小if(wr_count < 0){perror("write error");break;}else if(0 == wr_count){printf("nothing wai be written...\n");break;}else{size++;printf("%dKB\n",size);}}close(fd[0]);close(fd[1]);return 0;
}

在这里插入图片描述

管道读写易出现的问题

当管道中无数据时,读操作会阻塞
向管道中写入数据时,linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将会一直阻塞。
只有在管道的读端存在时,向管道中写入数据才有意义。否则,向管道中写入数据的进程将收到内核传来的SIGPIPE信号(通常Broken pipe错误)。

在这里插入图片描述
在这里插入图片描述

案例:验证管道断裂

#include <stdio.h>
#include <sys/types.h>
#inckude <unistd.h>
#include <string.h>
int main()
{int fd[2];if(pipe(fd) < 0){return -1;}printf("pipe ok!\n");//关闭读端 (保证写入管道时,读端不存在---》才会出现管道断裂)close(fd[0]);pid_t pid = fork();if(pid < 0){return -1;}else if(0 == pid){//子进程//写入数据到管道char data[1024];printf("Please input:");fgets(data, sizeof(data), stdin);write(fd[1], data, strlen(data));printf("写入管道成功!\n");exit(100);}else{//父进程//使用wait阻塞等待子进程退出时回收其资源int wstatus;//保存子进程的退出状态值wait(&wstatus);//当执行该40行时,说明子进程已经退出(收到信号异常退出还是正常执行exit(100)退出?)//分类讨论if(WIFSIGNALED(wstatus)){//收到信号导致子进程退出printf("导致子进程退出的信号是:%d\n",WTERMSIG(wstatus));//SIGPIPE==13号}else if(WIFEXITED(wstatus)){//正常退出---》执行了exit(100)printf("子进程的退出状态数值为:%d\n",WEXITSTATUS(wstatus));//父进程char buf[1024] = {0};read(fd[0], buf, sizeof(buf));printf("读取的结果是:%s\n",buf);}}return 0;
}

有名管道

无名管道只能用于具有亲缘关系的进程之间,这就限制了无名管道的使用范围
1、有名管道可以使互不相关的两个进程互相通信。
2、有名管道可以通过路径名来指出,并且在文件系统中可见
3、进程通过文件IO来操作有名管道
4、有名管道遵循先进先出规则,不支持如lseek() 操作

在这里插入图片描述

创建有名管道

在这里插入图片描述
在这里插入图片描述

有名管道的写端代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{//创建有名管道(同时也创建好了管道文件)if(mkfifo("./fifo.txt", 0664) < 0 && errno != EEXIST){perror("mkfifo error");return -1;}printf("mkfifo ok!\n");//以只写方式打开创建好的管道文件int fw = open("./fifo.txt", O_WRONLY);if(fw < 0){perror("open_write error");return -1;}printf("open_write ok!\n");char buf[1024] = {0};while(1){//对于buf做清空bzero(buf, sizeof(buf));printf("客户端:");fgets(buf, sizeof(buf), stdin);//判断客户端是否办理完毕业务if(0 == strncasecmp("quit", buf, 4)){printf("业务办理结束!\n");break;}//发送消息int wr_count = write(fw, buf, strlen(buf));if(wr_count < 0){perror("write error");break;}else if(0 == wr_count){printf("未写入任何内容...\n");break;}else{printf("实际发送%d字节内容!\n",wr_count);printf("send ok!\n");}}//关闭写端close(fw);return 0;
}

有名管道的读端代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{//创建有名管道(同时也创建好了管道文件)if(mkfifo("./fifo.txt", 0664) < 0 && errno != EEXIST){perror("mkfifo error");return -1;}printf("mkfifo ok!\n");//以只读方式打开创建好的管道文件int fr = open("./fifo.txt", O_RDONLY);if(fr < 0){perror("open_read error");return -1;}printf("open_read ok!\n");char buf[1024] = {0};while(1){//对于buf做清空bzero(buf, sizeof(buf));//接收消息int rd_count = read(fr, buf, sizeof(buf));if(rd_count < 0){perror("read error");break;}else if(0 == rd_count){printf("客户端业务处理结束!\n");break;}else{printf("成功接收%d个字节!\n",rd_count);printf("来自于客户端:%s\n",buf);}}close(fr);return 0;
}

在这里插入图片描述

信号

信号的特征

信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式
信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了哪些系统事件。
如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递给它;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被取消时才被传递给进程
同步:发送方发送数据,等待接收方响应之后才发下一个数据包的通讯方式
异步:发送方发送数据,不等待接收方发回响应,接着发送下一个数据包的通讯方式

产生信号

硬件来源

从键盘输入:
ctrl c : SIGINT信号 ,默认处理方式为进程终止
ctrl : SIGQUIT信号,作用与SIGINT类似,默认处理方式为进程终止
ctrl z: SIGTSTP信号,默认处理方式为进程暂停
SIGSTOP信号:只能通过kill产生,默认处理方式进程暂停

软件来源
发送信号的函数:kill() raise()

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
功能:不仅可以终止进程,也可以向进程发送其他信号
#include <signal.h>
int raise(int sig);
功能:只允许进程向自身发送信号

案例:首先使用fork创建子进程,在子进程中使用raise()向自身发送SIGSTOP信号,使子进程暂停;接下来在父进程中当时间戳超过10秒之后,调用kill()向子进程发送SIGKILL信号杀死子进程并回收子进程的退出
资源,且检查导致子进程退出是否因为收到了信号,如果是,打印出该信号是几号?如果子进程是正常退出,则打印出子进程退出的状态值是多少?(模拟正常退出:例如在终端给子进程发送SIGCONT信号来唤醒子进程,此时子进程继续执行,直到执行了exit()退出)

#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/wait.h>
int main(int argc, const char *argv[])
{//功能:练习kill()和raise()pid_t pid = fork();if(0 == pid){//子进程printf("child PID = %d\tparent PID = %d\n", \getpid(), getppid());//使用raise()函数给子进程自己发送SIGSTOP,让其暂停raise(SIGSTOP);printf("I am wakeup...\n");printf("hahahah\n");exit(88);}else{//父进程//定义保存时间的变量time_t start_time, current_time;time(&start_time);int wstatus;//回收子进程退出资源while(1){//获取当前的系统时间(更新的)time(&current_time);if(current_time - start_time > 10){//使用kill()函数给子进程发送SIGKILL信号,让其终止kill(pid, SIGKILL);}int exitID = waitpid(-1, &wstatus, WNOHANG);if(exitID < 0){perror("waitpid error");break;}else if(0 == exitID){printf("子进程还未结束...\n");}else{//已成功回收子进程printf("已成功回收子进程的退出资源...\n");if(WIFSIGNALED(wstatus)){printf("异常退出,造成子进程退出的信号是:%d号信号!\n",WTERMSIG(wstatus));}else if(WIFEXITED(wstatus)){printf("子进程正常退出,退出状态数值为:%d\n",WEXITSTATUS(wstatus));}break;}//每隔1秒 轮询一次sleep(1);}}return 0;
}

在这里插入图片描述

定时器信号

#include <unistd.h>
unsigned int alarm(unsigned int seconds);
参数:秒数
当定时器指定的时间到了时,它就向进程发送SIGALARM信号,信号的默认操作是结束进程.
注意:每个进程只能有一个闹钟时间。如果在调用alarm时,以前已为该进程设置过闹钟时间,而且它还没有超时,则该闹钟时间的剩余时间值作为本次alarm函数调用的值返回,以前登记的闹钟时间则被新值代换。如果有以前登记的尚未超过的闹钟时间,而新设的闹钟时间值为0,则取消以前的闹钟时间,其剩余时间值仍作为函数的返回值。
扩充:将调用进程挂起函数:
#include <unistd.h>
int pause(void);
功能:会造成进程主动挂起(处于阻塞状态,并主动放弃CPU),并且等待信号将其唤醒

案例:使用alarm()和pause()函数

#include <stdio.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{printf("hello!\n");//设定闹钟 5秒alarm(5);printf("I will.....\n");//进程挂起pause();printf("java!\n");printf("exit!\n");return 0;
}

在这里插入图片描述

响应信号

在这里插入图片描述
在这里插入图片描述

信号安装:

signal函数原型:
#include <signal.h>
void (*signal(int signum, void (*handler)(int)))(int);
优化原型:看的更清楚。
//给函数指针类型取别名
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

功能:进行信号安装
参数:
参数1:需要安装的信号名称
参数2:对于该信号的处理函数(该函数是用户自定义的,且返回值类型void ,形参是一个
int的函数的地址)
PS:参数2的位置可以传入:忽略(SIG_IGN) 执行缺省操作(SIG_DFL),信号处理函数的地址
返回值:成功安装完毕的信号处理函数的地址。

信号的处理方式可以有:
1、忽略信号
2、执行缺省操作(默认操作)
3、捕捉信号(自定义信号处理函数)

忽略信号
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{//安装信号signal(SIGINT, SIG_IGN);//忽略信号//执行一段代码while(1){printf("hello!\n");sleep(1);}return 0;
}

在这里插入图片描述

执行缺省操作
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{//安装信号//signal(SIGINT, SIG_IGN);//忽略信号signal(SIGINT, SIG_DFL);//缺省操作(信号默认处理方式)//执行一段代码while(1){printf("hello!\n");sleep(1);}return 0;
}

在这里插入图片描述

捕捉信号
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void handler_func(int signum)
{printf("不处理哦!\n");
}
int main(int argc, const char *argv[])
{//安装信号//signal(SIGINT, SIG_IGN);//忽略信号//signal(SIGINT, SIG_DFL);//缺省操作(信号默认处理方式)signal(SIGINT, &handler_func);//执行一段代码vwhile(1){printf("hello!\n");sleep(1);}return 0;
}

在这里插入图片描述
注意:一个信号处理函数响应多个信号

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void handler_func(int signum)
{//按照信号的类别进行控制switch(signum){case SIGINT:{printf("paly music...\n");break;}case SIGTSTP:{printf("paly vedio...\n");break;}case SIGQUIT:{printf("quit...\n");break;}default:{printf("信号无法响应...\n");}}return;
}
int main(int argc, const char *argv[])
{//安装信号//signal(SIGINT, SIG_IGN);//忽略信号//signal(SIGINT, SIG_DFL);//缺省操作(信号默认处理方式)//signal(SIGINT, &handler_func);//捕捉信号--》自定义信号处理函数//安装3个信号signal(SIGINT, &handler_func);//播放音乐signal(SIGTSTP, &handler_func);//播放视频signal(SIGQUIT, &handler_func);//退出app//执行一段代码while(1){printf("hello!\n");sleep(1);}return 0;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/20660.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【第1章】SpringBoot实战篇之注册接口

文章目录 前言一、代码部分1. User2.UserMapper13. UserSerivce4. UserController15. Result 二、测试1.注册2.再次注册 总结 前言 下面介绍用户注册接口。 一、代码部分 1. User package org.example.springboot3.bigevent.entity;import com.baomidou.mybatisplus.annotat…

开发板uboot与virtualbox虚拟机、windows11网络互通

环境&#xff1a;virtualbox中ubuntu22.04.4&#xff0c;开发板通过网线再经过拓展坞usb网卡跟windows11连接。连接如下&#xff1a; 1、关闭windows防火墙(重要) 2、先在VirtualBox的工具选项创建两个网络【仅主机(Host-Only)网络】和【NAT网络】 仅主机(Host-Only)网络的ip:…

Linux下Qt Creator无法输入中文(已解决)

1. 首先确保安装了搜狗输入法&#xff0c;且能正常运行。 2.克隆源码到本地。 git clone https://gitcode.com/fcitx/fcitx-qt5.git 3.检查Qt Creator版本&#xff0c;如下图所示&#xff0c;为基于Qt6的。 4. 进入源码目录&#xff0c;建立build文件夹&#xff0c;修改CMak…

css动态导航栏鼠标悬停特效

charset "utf-8"; /*科e互联特效基本框架CSS*/ body, ul, dl, dd, dt, ol, li, p, h1, h2, h3, h4, h5, h6, textarea, form, select, fieldset, table, td, div, input {margin:0;padding:0;-webkit-text-size-adjust: none} h1, h2, h3, h4, h5, h6{font-size:12px…

8、资源操作 Resource

目录 8.1、Spring Resources概述补充&#xff1a;什么是 low-level 资源&#xff1f;1. 文件系统资源2. 类路径资源3. URL资源4. 内嵌资源5. InputStream资源6. ServletContext资源示例代码结论 8.2、Resource接口8.3、Resource的实现类8.3.1、UrlResource访问网络资源1&#x…

LIO-EKF: 运行数据UrbanNav与mid360设备详细教程

一、代码连接 代码下载连接&#xff1a; YibinWu/LIO-EKF: Maybe the simplest LiDAR-inertial odometry that one can have. (github.com) 编译步骤&#xff1a; cd srcgit clone gitgithub.com:YibinWu/LIO-EKF.gitcatkin_makesource devel/setup.bash 运行步骤&#xff1a; …

java并发处理机制

在Java中&#xff0c;并发处理机制主要是通过线程来实现的。Java提供了丰富的类和接口来支持多线程编程&#xff0c;主要集中在 java.util.concurrent 包中。以下是一些关键的并发处理机制&#xff1a; 1.线程创建&#xff1a;可以通过继承 Thread 类或实现 Runnable 接口来创建…

公园【百度之星】/图论+dijkstra

公园 图论dijkstra #include<bits/stdc.h> using namespace std; typedef long long ll; typedef pair<ll,ll> pii; vector<ll> v[40005]; //a、b、c分别是小度、度度熊、终点到各个点的最短距离 ll a[40005],b[40005],c[40005],dist[40005],st[40005]; void…

java 远程调试

1.远程启动时 jdk1.8-32\jre\bin\java.exe -Dfile.encodingUTF-8 -Djava.library.pathlib -agentlib:jdwptransportdt_socket,servery,suspendn,address5005 -jar local-com.yuetai.service-0.0.1-SNAPSHOT.jar --spring.config.locationapplication.yml 2.本地调试项目连接远…

JCR一区级 | Matlab实现TCN-BiGRU-MATT时间卷积双向门控循环单元多特征分类预测

JCR一区级 | Matlab实现TCN-BiGRU-MATT时间卷积双向门控循环单元多特征分类预测 目录 JCR一区级 | Matlab实现TCN-BiGRU-MATT时间卷积双向门控循环单元多特征分类预测分类效果基本介绍程序设计参考资料 分类效果 基本介绍 1.Matlab实现TCN-BiGRU-MATT时间卷积双向门控循环单元多…

一维时间序列信号的小波模极大值分解与重建(matlab R2018A)

数学上称无限次可导函数是光滑的或没有奇异性&#xff0c;若函数在某处有间断或某阶导数不连续&#xff0c;则称函数在此处有奇异性&#xff0c;该点就是奇异点。奇异性反映了信号的不规则程度&#xff0c;因为信号的奇异点和突变部分往往携带者重要信息&#xff0c;因此信号的…

全自动打包封箱机:解析其在产品质量与安全保障方面的作用

在当今快节奏的生产环境中&#xff0c;全自动打包封箱机以其高效、精准的特点&#xff0c;正逐渐成为生产线上的得力助手。它不仅提升了生产效率&#xff0c;更在产品质量与安全保障方面发挥着举足轻重的作用。星派将详细解析全自动打包封箱机在产品质量与安全保障方面的作用。…

CLIP--Learning Transferable Visual Models From Natural Language Supervision

参考&#xff1a;CLIP论文笔记--《Learning Transferable Visual Models From Natural Language Supervision》_visual n-grams模型-CSDN博客 openAI&#xff0c;2021&#xff0c;将图片和文字联系在一起&#xff0c;----->得到一个能非常好表达图片和文字的模型主题&#…

网络安全-钓鱼篇-利用cs进行钓鱼

一、环境 自行搭建&#xff0c;kill&#xff0c;Windows10&#xff0c;cs 二、原理 如图所示 三、钓鱼演示 首先第一步&#xff1a;打开System Profiler-分析器功能 选择克隆www.baidu.com页面做钓鱼 之后我们通过包装域名&#xff0c;各种手段让攻击对象访问&#xff1a;h…

Java面试题:Redis1_Redis的使用场景和如何解决Redis缓存穿透问题

Redis使用场景常见问题 缓存 缓存三兄弟(穿透,击穿,雪崩) 双写一致 持久化 数据过期策略 数据淘汰策略 分布式锁 setnx,redisson 消息队列,延迟队列 … 解决Redis缓存穿透问题 缓存穿透问题 请求->redis缓存->mysql数据库 当一个新请求到来时,先会访问redi…

JVM(Java虚拟机)笔记

面试常见&#xff1a; 请你谈谈你对JVM的理解?java8虚拟机和之前的变化更新?什么是OOM&#xff0c;什么是栈溢出StackOverFlowError? 怎么分析?JVM的常用调优参数有哪些?内存快照如何抓取&#xff1f;怎么分析Dump文件&#xff1f;谈谈JVM中&#xff0c;类加载器你的认识…

前端最新面试题(基础模块HTML/CSS/JS篇)

目录 一、HTML、HTTP、WEB综合问题 1 前端需要注意哪些SEO 2 img的title和alt有什么区别 3 HTTP的几种请求方法用途 4 从浏览器地址栏输入url到显示页面的步骤 5 如何进行网站性能优化 6 HTTP状态码及其含义 7 语义化的理解 8 介绍一下你对浏览器内核的理解? 9 html…

【C++】vector常见的使用方式

前言&#xff1a;在上一篇中我们讲到了string类的模拟实现&#xff0c;今天我们将进一步的去学习vector的一些常用的使用方法。 &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x1f49e; &#x1f449; 专栏分类:高质量&#xff23;学习 &#x1f448; &#x1f4af;代码仓…

命运方舟台服注册 命运方舟台服怎么注册?不会操作看这里

命运方舟台服注册 命运方舟台服怎么注册&#xff1f;不会操作看这里 命运方舟作为今年备受瞩目的一款MMORPG类型游戏&#xff0c;在上线前的预约数量已经一次又一次创下新高。这款游戏的开发商Smile gate真是给玩家们带来了一款让人眼前一亮的作品。游戏创建在虚幻引擎的基础…

用esp prog烧录ESP32-C3板踩坑

附ESP32C3的GPIO一览&#xff1a; vscode选择Jtag烧录&#xff0c;终端输出esp_usb_jtag: could not find or open device&#xff1a; D:\Devtools\Espressif\tools\openocd-esp32\v0.12.0-esp32-20230921\openocd-esp32\bin\openocd.exe -f board/esp32s3-builtin.cfgOpen O…