LinuxC编程——进程间通信(二)(信号、共享内存)

目录

  • 一、信号
    • 1.1 概念
    • 1.2 信号的响应方式⭐⭐⭐
    • 1.3 几种常见的信号
    • 1.4 函数
    • 练习
  • 二、共享内存
    • 2.1 共享内存的特点
    • 2.2 共享内存创建步骤⭐⭐
    • 2.3 共享内存创建所需函数

  • 信号主要用来通知进程异步事件的发生。最初信号设计的目的是为了处理错误,它们也用来作为最基本的IPC机制。在Linux中可以识别64种不同的信号。这些信号中的大部分都有了预先定义好的意义,但是至少有两个,SIGUSR1和SIGUSR2可以由应用程序来定义。进程可以显式地用kill 或是killg系统函数来向另一个进程或进程组发信号。此外,内核可以内应不同的事件而产生内部信号。例如,当按下终端键“Ctrl+C" 时,内核便发送一个SIGINT信号到前台的进程。
  • 作为一种IPC机制,信号有一些局限性信号的系统开销太大。发送信号的进程要进行系统调用;内核要中断接收信号的进程,而且要管理它的堆栈,同时还要调用处理程序,之后还要恢复执行被中断的进程。更重要的是,信号的数量非常有限,因为只存在有限的不同的信号,而且信号能传送的信息量十分有限,用户产生的信号不可能发送附加信息及各种
  • Linux是一个多用户、多任务的操作系统,无论是操作系统与一般进程间的通信,还是用户进程间的通信都是必要的。信号是进程间互相通信的方法之一,它用来指出某种事件的发生。
  • 在Linux系统中,针对不同的软/硬件状况,内核程序会发送出不同的信号来通知进程某个事件的发生。但是如何处理这个信号,就要由进程本身来处理。
  • 信号可以由系统内核程序发出,也能由某些进程发送,但是大部分的时候都是由内核程
    序发出的。
  • 当一个信号正在被处理时,所有同样的信号都将暂时搁置,直到这个信号处理完成。

一、信号

1.1 概念

  1. 信号是在软件层次上对中断的一种模拟,是一种异步通信方式
  2. 信号可以直接进行用户空间进程与内核进程之间的交互,内核进程也可以利用信号通知用户进程发生了哪些系统事件。
  3. 如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递给它;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被取消时才被传递给进程。

1.2 信号的响应方式⭐⭐⭐

  1. 忽略信号:对信号不做任何处理。但是有两个信号不能被忽略STGKILL和SIGSTOP
  2. 捕捉信号:定义信号处理函数,当信号发生时,执行相应的处理函数。
  3. 执行缺省操作:linux对每种信号都规定了默认操作。

1.3 几种常见的信号

Linux中可以识别的信号种类有64种,在终端可以用 kill -l 列出:
在这里插入图片描述
需要了解的信号如下:
2)SIGINT:结束进程,对应快捷方式ctrl+c
3)SIGQUIT:退出信号,对应快捷方式ctrl+
9)SIGKILL:结束进程,不能被忽略不能被捕捉
15)SIGTERM:结束终端进程,kill 使用时不加数字默认是此信号
17)SIGCHLD:子进程状态改变时给父进程发的信号
19)SIGSTOP:结束进程,不能被忽略不能被捕捉
20)SIGTSTP:暂停信号,对应快捷方式ctrl+z
26)SIGALRM:闹钟信号,alarm函数设置定时,当到设定的时间时,内核会向进程发送此信号结束进程。

1.4 函数

  1. kill
    int kill(pid_t pid, int sig);

    • 功能:信号发送
    • 参数:
      • pid:指定进程
      • sig:要发送的信号
    • 返回值:成功 0;失败 -1
  2. raise
    int raise(int sig);

    • 功能:进程向自己发送信号
    • 参数:sig:信号
    • 返回值:成功 0;失败 -1
  3. pause
    int pause(void);

    • 功能:用于将调用进程挂起(不占用CPU资源),直到收到信号为止。

函数测试:

#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>int main(int argc, char const *argv[])
{printf("start...\n");/*1.kill给指定的进程发送指定的信号*/// kill(getpid(),SIGINT);/*2.raise给自己发送信号*/// raise(2);//给自己发送信号//printf("end...\n");/*3.pause挂起进程,直到捕捉到信号才会返回*/pause(); //不占用cpu// while(1); //一直轮寻,占用CPUreturn 0;
}

在还没有信号触发时,为了保证进程不立即结束,可以用while或者pause,但是while会一直轮询,占用cpu资源,而pause会将进程挂起,不占用CPU资源。

  1. signal
    typedef void (*sighandler_t)(int);
    sighandler_t signal(int signum, sighandler_t handler);
    • 功能:信号处理函数
    • 参数:
      • signum:要处理的信号
      • handler:信号处理方式
        • SIG_IGN:忽略信号
        • SIG_DFL:执行默认操作
        • handler:捕捉信号(捕捉到信号要处理的函数)
          • void handler(int sig){} //函数名可以自定义
    • 返回值:成功:设置之前的信号处理方式;失败:-1

函数测试:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>void handler(int sig)
{if (sig == SIGINT)printf("You pressed ctrl + c!\n");else if (sig == SIGTSTP)printf("You pressed ctrl + z!\n");else if (sig == SIGQUIT)printf("You pressed ctrl + \\!\n");elseprintf("end...\n");
}int main(int argc, char const *argv[])
{/*1.忽略信号*/// signal(2,SIG_IGN); //ctrl+c被忽略,终端用ctrl+\退出/*2.缺省(默认)操作*/// signal(SIGINT,SIG_DFL);/*3.捕捉信号*/signal(2, handler);       //捕捉结束进程信号(ctrl+C)signal(SIGTSTP, handler); //捕捉暂停信号(ctrl+Z)signal(SIGQUIT,handler);  //捕捉推出信号(ctrl+Q)//挂起进程,直到捕捉到信号才会返回pause();return 0;
}
  1. alarm
    unsigned int alarm(unsigned int seconds)

    • 功能:在进程中设置一个定时器
    • 参数:seconds:定时时间,单位为秒
    • 返回值:如果调用此alarm()前,进程中已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0。

    注意📢:一个进程只能有一个闹钟时间。如果在调用alarm时已设置过闹钟时间,则之前的闹钟时间被新值所代替。

函数测试:

#include <stdio.h>
#include <unistd.h>int main(int argc, char const *argv[])
{printf("start...\n");// alarm(5); //alarm不阻塞,当时间到达后结束进程printf("%d\n",alarm(5));//第一次调用alarm返回值为0//alarm不阻塞,当时间到达后结束进程printf("end...\n");sleep(3);printf("%d\n",alarm(8)); // 前面定时5秒。用sleep函数消耗1秒,alarm返回5-3=2printf("second end...\n");alarm(0); //当秒数为0时,定时被取消pause(); //pause挂起进程(用while也可以),直到捕捉到信号才会返回return 0;
}

练习

用信号的知识实现司机和售票员问题。
1)售票员捕捉SIGINT(代表开车)信号,向司机发送SIGUSR1信号,司机打印(let’s gogogo)
2)售票员捕捉SIGQUIT(代表停车)信号,向司机发送SIGUSR2信号,司机打印(stop the bus)
3)司机捕捉SIGTSTP(代表到达终点站)信号,向售票员发送SIGUSR1信号,售票员打印(please get off the bus)
4)司机等待售票员下车,之后司机再下车。—>父子进程,父:司机 子:售票员

/*
练习一:
用信号的知识实现司机和售票员问题。
1)售票员捕捉SIGINT(代表开车)信号,向司机发送SIGUSR1信号,司机打印(let's gogogo)
2)售票员捕捉SIGQUIT(代表停车)信号,向司机发送SIGUSR2信号,司机打印(stop the bus)
3)司机捕捉SIGTSTP(代表到达终点站)信号,向售票员发送SIGUSR1信号,售票员打印(please get off the bus)
4)司机等待售票员下车,之后司机再下车。
*/
/*
分析:各个进程要捕捉和忽略的信号
售票员(子进程):捕捉:SIGINT,SIGQUIT,SIGUSR1忽略:SIGTSTP
司机(父进程):捕捉:SIGTSTP,SIGUSR1,SIGUSR2忽略:SIGINT,SIGQUIT
*/
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <stdlib.h>pid_t pid; //pid设置为为全局变量void saller(int arg)
{if(arg == SIGINT)kill(getppid(),SIGUSR1);if(arg == SIGQUIT)kill(getppid(),SIGUSR2);if(arg == SIGUSR1){printf("售票员:please get off the bus\n");exit(0);}
}
void driver(int arg)
{if(arg == SIGUSR1)printf("司机:let's gogogo\n");if(arg == SIGUSR2)printf("司机:stop the bus\n");if(arg == SIGTSTP){kill(pid,SIGUSR1);//要注意pid要为全局变量wait(NULL);//阻塞回收子进程资源exit(0);}
}int main(int argc, char const *argv[])
{pid = fork();//创建父子进程if(pid < 0){perror("fork err");return -1;}else if(pid == 0) //售票员(子进程){//子进程要捕捉的信号signal(SIGINT,saller);signal(SIGQUIT,saller);signal(SIGUSR1,saller);//子进程要忽略的信号signal(SIGTSTP,SIG_IGN);}else //司机(父进程){//父进程要捕捉的信号signal(SIGTSTP,driver);signal(SIGUSR1,driver);signal(SIGUSR2,driver);//父进程要忽略的信号signal(SIGINT,SIG_IGN);signal(SIGQUIT,SIG_IGN);}while(1)pause();//这样比只有一个while占用CPU更少return 0;
}

📢要明白:最后的while(1) pause(); 的用意→如果只用一个pause(),验证一个信号后程序立即结束;如果只用while(1)进行阻塞,虽然可以起到从终端循环输入信号并打印出结果,但是cpu占用资源较高;而使用while(1)和pause()则解决以上弊端,因为这样程序运行起来后,进while,在pause处程序挂起,不再占用CPU,直到产生信号解除pause阻塞进入下一次while。
pause()作用:用于将调用进程挂起,直到收到信号为止

二、共享内存

2.1 共享内存的特点

  1. 共享内存是一种最为 高效 的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝
  2. 为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间
  3. 进程可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高的效率。
  4. 由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等

2.2 共享内存创建步骤⭐⭐

  1. 创建key值(ftok)
  2. 创建或打开共享内存(shmget)
  3. 映射共享内存到用户空间(shmat)
  4. 取消映射(shmdt)
  5. 删除共享内存(shmctl)

2.3 共享内存创建所需函数

  1. ftok
    key_t ftok(const char *pathname, int proj_id);

    • 功能:产生一个独一无二的key值
    • 参数:
      • pathname:已经存在的可访问文件的名字
      • proj_id:一个字符(因为只用低8位)
    • 返回值:成功:key值;失败:-1
      在这里插入图片描述
      运行结果:
      在这里插入图片描述
      前两位是ftok第二个字符参数的,ASCII码值的十六进制表示形式
      在这里插入图片描述
      中间这两位大多情况是01
      在这里插入图片描述
      查看文件inode👇
      在这里插入图片描述
      将文件的inode转换为十六进制👇,可以看到其后四位确实是key值十六进制的后四位
      在这里插入图片描述
  2. shmget
    int shmget(key_t key, size_t size, int shmflg);

    • 功能:创建或打开共享内存
    • 参数:
      • key 键值
      • size 共享内存的大小
      • shmflg IPC_CREAT|IPC_EXCL(判错)|0666
    • 返回值:成功 shmid;出错 -1

    例:
    在这里插入图片描述
    在这里插入图片描述
    查看系统共享内存:ipcs -m
    在这里插入图片描述
    删除共享内存:ipcrm -m 对应的shmid
    在这里插入图片描述

  3. shmat
    void *shmat(int shmid,const void *shmaddr,int shmflg);

    • 功能:映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
    • 参数:
      • shmid 共享内存的id号
      • shmaddr 一般为NULL,表示由系统自动完成映射;如果不为NULL,那么由用户指定
      • shmflg:
        • SHM_RDONLY就是对该共享内存只进行读操作
        • 0 可读可写
    • 返回值:
      • 成功:完成映射后的地址,
      • 失败:-1的地址

    用法:if((p = (char *)shmat(shmid,NULL,0)) == (char *)-1)
    常用下面:

char *p = shmat(shmid,NULL,0);
if(p==(void *)-1)
{perror("shmat err");return -1;
}
  1. shmdt
    int shmdt(const void *shmaddr);

    • 功能:取消映射
    • 参数:要取消的地址
    • 返回值:成功:0 ;失败:-1
  2. shmctl
    int shmctl(int shmid,int cmd,struct shmid_ds *buf);

    • 功能:(删除共享内存),对共享内存进行各种操作
    • 参数:
      • shmid :共享内存的id号
      • cmd
        • IPC_STAT 获得shmid属性信息,存放在第三参数
        • IPC_SET 设置shmid属性信息,要设置的属性放在第三参数
        • IPC_RMID:删除共享内存,此时第三个参数为NULL即可
    • 返回:成功0 ;失败-1

    用法:shmctl(shmid,IPC_RMID,NULL);

例:创建共享内存,通过共享内存完成读写

/*共享内存创建及读写
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <unistd.h>int main(int argc, char const *argv[])
{/*1.创建key值*/key_t key;key = ftok("./6kill.c", 'a'); //第二个参数是任意字符if (key < 0){perror("ftok err");return -1;}printf("%#x\n", key); //'#'会在打印结果中添加前缀0x/*2.创建或打开共享内存*/int shmid = shmget(key, 128, IPC_CREAT | IPC_EXCL | 0666);if (shmid <= 0) //=0不使用{//若已创建,则用shmget打开,重新给shmid赋值if (errno == 17)shmid = shmget(key, 128, 0666);else{perror("shmget err");return -1;}}printf("shmid:%d\n", shmid);/*3.映射共享内存到用户空间 */char *p = shmat(shmid, NULL, 0);if (p == (void *)-1){perror("shmat err");return -1;}//读写操作read(0, p, 32);write(1, p, 32);//printf("buf:%s\n",p);/*4.撤销映射*/shmdt(p);/*5.删除共享内存*/shmctl(shmid, IPC_RMID, NULL);return 0;
}

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

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

相关文章

【设计模式】前端控制器模式

前端控制器模式&#xff08;Front Controller Pattern&#xff09;是用来提供一个集中的请求处理机制&#xff0c;所有的请求都将由一个单一的处理程序处理。该处理程序可以做认证/授权/记录日志&#xff0c;或者跟踪请求&#xff0c;然后把请求传给相应的处理程序。以下是这种…

Java Review - 关于代理的二三事儿

文章目录 Pre概述静态代理概述Code 动态代理概述实现方式一 - JDK代理或接口代理概述Code 实现方式二 - CGLib 子类代理 (Code Generation Library)概述pom依赖Code Pre Java-JDK动态代理 Java-CGLib动态代理 概述 代理模式是一种结构型设计模式&#xff0c;其目的是为其他对…

我们常说这个pycharm里有陷阱,第三方库导入失败,看这里!

最近有小伙伴遇到了明明安装了 python 第三方库&#xff0c;但是在 pycharm 当中却导入不成功的问题。 ​ 一直以来&#xff0c;也有不少初学 python 的小伙伴&#xff0c;一不小心就跳进了虚拟环境和系统环境的【陷阱】中。 本文就基于此问题&#xff0c;来说说在 pycharm 当…

PPT颜色又丑又乱怎么办?

一、设计一套PPT时&#xff0c;可以从这5个方面进行设计 二、PPT颜色 &#xff08;一&#xff09;、PPT常用颜色分类 一个ppt需要主色、辅助色、字体色、背景色即可。 &#xff08;二&#xff09;、搭建PPT色彩系统 设计ppt时&#xff0c;根据如下几个步骤&#xff0c;依次选…

基于vue3+webpack5+qiankun实现微前端

一 主应用改造&#xff08;又称基座改造&#xff09; 1 在主应用中安装qiankun(npm i qiankun -S) 2 在src下新建micro-app.js文件&#xff0c;用于存放所有子应用。 const microApps [// 当匹配到activeRule 的时候&#xff0c;请求获取entry资源&#xff0c;渲染到containe…

threejs点击模型实现模型边缘高亮的选中效果--更改后提高帧率

先来个效果图 之前写的那个稍微有点问题&#xff0c;帧率只有30&#xff0c;参照官方代码修改后&#xff0c;帧率可以达到50了&#xff0c;在不全屏的状态下&#xff0c;帧率60 1.首先需要导入库 // 用于模型边缘高亮 import { EffectComposer } from "three/examples/js…

Leetcode-每日一题【剑指 Offer 25. 合并两个排序的链表】

题目 输入两个递增排序的链表&#xff0c;合并这两个链表并使新链表中的节点仍然是递增排序的。 示例1&#xff1a; 输入&#xff1a;1->2->4, 1->3->4输出&#xff1a;1->1->2->3->4->4 限制&#xff1a; 0 < 链表长度 < 1000 解题思路 1…

http协议详解

HTTP是什么&#xff1f; HTTP 是一种用作获取诸如 HTML 文档这类资源的协议。它是 Web 上进行任何数据交换的基础&#xff0c;同时&#xff0c;也是一种客户端—服务器&#xff08;client-server&#xff09;协议&#xff0c;也就是说&#xff0c;请求是由接受方——通常是浏览…

[C++ 网络协议] 套接字和地址族、数据序列

目录 1. 套接字 1.1 在Linux平台下构建套接字 1.1.1 用于接听的套接字(服务器端套接字) 1.1.2 用于发送请求的套接字(客户端套接字) 1.2 在Windows平台下构建套接字 1.2.1 Winsock的初始化 1.2.2 用于接听的套接字(服务器端套接字) 1.2.3 用于发送请求的套接字(客户端套…

Web菜鸟教程-Springboot使用MyBatis生成器生成代码

SpringBoot大大简化了Web开发流程。可以这么说&#xff0c;做Web后来开发大部分时间就是在做配置文件修改。Web开发中&#xff0c;终端的运算能力越来越强&#xff0c;大部分场景就是数据库的操作&#xff0c;只有少部分逻辑会放在Web端处理。而这些增删查改基本属于标准的格式…

Nginx反向代理配置+负载均衡集群部署

文章目录 负载均衡反向代理基础环境部署&#xff1a;什么是代理实验环境图流量过程 环境部署准备两台Web服务器安装Nginx准备页面内容添加主机名 代理服务器配置 修改windos hosts文件测试&#xff1a;终端浏览器 负载均衡反向代理基础环境部署&#xff1a; 什么是代理 正向代…

Python爬虫——selenium的安装和基本使用

1.什么是selenium&#xff1f; selenium是一个用于web应用程序测试的工具selenium测试直接运行在浏览器中&#xff0c;就像真正的用户在操作一样支持通过各种driver&#xff08;FrifoxDriver&#xff0c;ItenrentExploreDriver&#xff0c;OperaDriver&#xff0c;ChromeDrive…

Android模板设计模式之 - 构建整个应用的BaseActivity

1. 模式介绍 模式的定义 定义一个操作中的算法的框架&#xff0c;而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 模式的使用场景 1.多个子类有公有的方法&#xff0c;并且逻辑基本相同时。 2.重要、复杂的算法&#xff0c;可…

threejs中gltf模型出现的问题(黑色,颜色不协调,太小)和解决方案

模型一片漆黑 如下图 可能原因&#xff0c;没有灯光&#xff0c;加下以下代码&#xff1a; // 4、加入灯光 const lightness new THREE.HemisphereLight(0xffffff, 0x444444); lightness.position.set(0, 20, 0); scene.add(lightness); const shadowLight new THREE.Direct…

实现同时查找多个关键词——KeywordCrafter - 关键词匠心

具体功能&#xff1a;同时查找多个关键词&#xff0c;高亮加粗显示&#xff0c;并关键词显示出现次数。 &#x1f9d0;碎碎念&#xff1a;最近在写文案的时候&#xff0c;总是要避免出现一个敏感词汇&#xff0c;利用 (commandF) or (CtrF) 查找&#xff0c;只能一个一个单词去…

中国艺术孙溟㠭篆刻作品《得大自在》

关汉卿《四块玉闲适》&#xff1a;“适意行&#xff0c;安心坐。渴时饮&#xff0c;饥时餐&#xff0c;醉时歌。困来时就向莎茵卧。日月长&#xff0c;天地阔&#xff0c;闲快活。” 整理/释门

JS逆向系列之猿人学爬虫第14题-备而后动-勿使有变

文章目录 题目地址参数分析参考jspython 调用往期逆向文章推荐题目地址 https://match.yuanrenxue.cn/match/14题目难度标的是困难,主要难在js混淆部分。 参数分析 初始抓包有无限debugger反调试,可以直接hook 函数构造器过掉无限debugger Function.prototype.__construc…

[FPAG开发]使用Vivado创建第一个程序

1 打开Vivado软件&#xff0c;新建项目 选择一个纯英文路径 选择合适的型号 产品型号ZYNQ-7010xc7z010clg400-1ZYNQ-7020xc7z010clg400-2 如果型号选错&#xff0c;可以单击这里重新选择 2 创建工程源文件 可以看到文件创建成功 双击文件打开&#xff0c;插入代码 modul…

IOC容器

DI&#xff08;依赖注入&#xff09;&#xff1a;DI&#xff08;Dependency Injection&#xff09;是一种实现松耦合和可测试性的软件设计模式。它的核心思想是将依赖关系的创建与管理交给外部容器&#xff0c;使得对象之间只依赖于接口而不直接依赖于具体实现类。通过依赖注入…