进程通信(IPC-Inter Process Communication)

news2024/11/25 7:08:38

进程之间的通信通过内核空间实现

IPC技术

管道(匿名管道/命名管道-FIFO队列) ②System V IPC(消息队列、信号量和共享内存)  ③套接字(UNIX套接字&Internet套接字)

※信号

软中断,信号提供了一种处理异步事件的方法,作为进程通信的一种机制,由一个进程发送给另一个进程。<signal.h>

信号的产生情况

①用户在终端按下一个组合键;

②硬件异常; //①②硬件问题

③进程调用 kill 函数发送信号;

④当检测到某软件产生异常时产生信号;      //③④软件问题

信号处理

阻塞信号/捕获信号/忽略信号/执行默认动作

【SIGKILL&SIGSTOP 信号是无法捕捉和忽略的】

查看信号 kill -l / trap -l;

信号操作的函数

(1) int kill (pid_t pid,int sig);     //向指定进程发送信号

头文件:<sys/types.h>   <sysnal.h>

pid : ①>0; 发送指定pid的进程 

②=0;信号发送给和目前进程在同一个进程组的所有进程

③=-1;广播到系统内所有进程 

④<0;  发送信号给PID为pid绝对值的进程

(2) int alarm(int second);    //定时器发送信号SIGALRM,默认处理是终止当前进程;

头文件:<unistd.h>

函数的返回值是0/设定闹钟还余下的秒数

(3) int raise(int sig);  //发送信号给当前的进程

头文件:<signal.h>

sig参数主要是信号参数

执行成功返回0,失败返回-1;

等价于 kill (getpid(),sig);

(4) void signal(*signal(it signum,void(*handler)(int)))(int);

头文件:#include <signal.h>

signal()会按照signum指定的信号编号来设置信号的处理函数。当指定的信号到达就会处理*handler指定的函数执行;若该函数不在,则需要是以下的两个常数之一:

SIG_IGN 忽略参数signum指定的信号

SIG_DFL 将参数signum指定的信号重设为 核心预设的信号处理方式 

(5) 信号集操作函数--#include <signal.h>

int sigemptyset(sigset_t *set);             //清空信号集 成功返回0 错误返回-1

int sigfillset(sigset_t *set);                  //初始化信号集  成功返回0 失败返回-1

int sigaddset(sigset_t *set,int signo);   //将signo信号加入到信号集set 成功返回0 失败返回-1

int sigdelset(sigset_t *set,int signo);       //将指定信号从信号集中添加或者删除

int sigismember(const sigset_t *set,int signo); 

                                          //判断指定信号是否包含在信号集中 (不)包含返回1(0) 失败返回-1

int sigprocmask(int how,const sigset_t *set,sigset_t *old) //查询/设置信号掩码

   //how参数:   --成功返回0,失败返回-1

 //SIG_BLOCK: 新的信号掩码由目前的信号掩码和set指定的信号掩码的并集

 //SIG_UNBLOCK:将目前的信号掩码删除set指定的信号掩码

 //SIG_SETMASK: 目前的信号掩码设置成set指定的信号掩码

信号发送例:设计一个程序,要求用户进程创建一个子进程,父进程向子进程发出SIGKILL信号,子进程收到此信号,结束子进程的运行;

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{
        pid_t sonpid;
        int ret;
        sonpid=fork();  //fork()函数执行一次,返回两次;在父进程中,fork返回新建进程ID;在子 
                        //进程中fork返回0,错误返回负值;
        int newret;
        if(sonpid<0)
        {
                perror("创建进程失败!");
                exit(1);  //异常结束
        }
        else if(sonpid==0)
        {
                raise(SIGSTOP);   //如果是子进程,发送一个不能被阻塞、处理或阻塞的暂停信号;
                exit(0);  //正常结束
        }
        else
        {
                printf("子进程的进程号是%d\n",sonpid);
                if((waitpid(sonpid,NULL,WNOHANG))==0)
                {
                        if(ret=kill(sonpid,SIGKILL)==0)
                        {
                                printf("用kill函数返回值是:%d,发出的SIGKILL信号结>束的进程进程号:%d\n",ret,sonpid);
                        }
                        else
                        {
                                perror("kill函数结束子进程失败");
                        }
                }
        }
}

 信号处理例:要求程序运行后进入无限循环,当用户按下中断键(Ctrl+C)时,进入程序的自定义信号处理函数,当用户再次按下中断键(Ctrl+C)后,结束程序运行;

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <signal.h>
void fun_ctrl_c();    //自定义信号函数
int main()
{
        (void)signal(SIGINT,fun_ctrl_c);
        printf("主程序:主程序进入一个循环...\n");
        while(1)
        {
                printf("这是一个无限的循环(退出请按‘Ctrl+C’)\n");
                sleep(3);
        }
        exit(0);
}

void fun_ctrl_c()
{
        printf("\t你按了Ctrl+C!\n");
        printf("\t此例不处理,重新恢复SIGINT信号的系统默认处理\n");
        (void) signal(SIGINT,SIG_DFL);   //重新恢复SIGINT的系统默认处理
}

 

信号阻塞例1:

要求主程序运行时,即使按下Ctrl+C也不影响正在运行的程序,即让信号处于阻塞状态,当主体程序运行完毕后才进入自定义信号处理函数;

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <sys/stat.h>
#include <unistd.h>
void fun_ctrl_c();//自定义信号函数
int main()
{
        int i;
        sigset_t set,pendset;  //定义了两个信号集
        struct sigaction action;
        (void) signal(SIGINT,fun_ctrl_c);
        if(sigemptyset(&set)<0)   //初始化set信号集
        {
                perror("初始化集合错误!\n");
        }
        if(sigaddset(&set,SIGINT)<0)   //将SIGINT信号加入到set信号集
        {
                perror("加入信号集错误\n");
        }
        if(sigprocmask(SIG_BLOCK,&set,NULL)<0)  //将当前的信号集合加入到当前进程的
                                                //阻塞集合中
        {
                perror("往信号阻塞集中增加一个信号集合错误");
        }
        else    //将当前信号集加入到阻塞集合中
        {
                for(int i=0;i<5;i++)
                {
                        printf("此文字表示程序在阻塞状态\n");
                        sleep(2);
                }
        }
        if(sigprocmask(SIG_UNBLOCK,&set,NULL)<0)  //将当前的阻塞集中删除一个信号集
        {
                perror("从信号阻塞集删除一个信号集合错误");
        }
}
void fun_ctrl_c()  //自定义信号函数
{
        printf("\t你按了Ctrl+C但是系统未处理a... ");//要求中断键不影响当前程序运行
        printf("\t信号处理函数:要处理的东西在处理函数中编程!\n");
        printf("\t这个案例不处理,直接退出!\n");
        (void) signal(SIGINT,SIG_DFL);         //恢复默认SIGINT信号的系统默认处理
}


过程:①初始化set信号集; ②将SIGINT信号加入到set信号集;  ③将set信号集加入到阻塞集;

...   ④将set信号集从阻塞集删除 ->5次循环 程序结束 SIGINT执行默认系统处理 直接退出系统;

信号阻塞例2:

信号SIGINT(Ctrl+C)和SIGTSTP(Ctrl+Z)是可以阻塞的,信号SIGQUIT(Ctrl+\)是不可以阻塞;

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
void fun_ctrl_c();
void fun_ctrl_z();
void fun_ctrl_d();
int main()
{
        int i;
        sigset_t set,pendset;
        struct sigaction action;
        (void) signal (SIGINT,fun_ctrl_c);
        (void) signal (SIGTSTP,fun_ctrl_z);
        (void) signal (SIGQUIT,fun_ctrl_d);
        if(sigemptyset(&set)<0) //初始化set信号集
        {
                perror("初始化信号集错误\n");
        }
        if(sigaddset(&set,SIGTSTP)<0)  //将SIGTSTP信号加入到set
        {
                perror("Ctrl+Z加入信号集错误\n");
        }
        if(sigaddset(&set,SIGINT)<0)  //将SIGINT信号加入到set
        {
                perror("Ctrl+C加入信号集错误\n");
        }
        if(sigprocmask(SIG_BLOCK,&set,NULL)<0)   //信号集加入到当前进程的阻塞集合
        {
                perror("加入阻塞集合失败\n");
        }
        else
        {
                printf("加入到阻塞集合成功\n");
                for(i=0;i<10;i++)
                {
                printf("Ctrl+C和Ctrl+Z信号处于阻塞,Ctrl+'\'信号未被阻塞\n");
                sleep(3);
                }
        }
        if(sigprocmask(SIG_UNBLOCK,&set,NULL)<0)  //将当前信号集从阻塞信号集中删除
        {
                perror("从阻塞信号集中删除当前信号集失败\n");
        }
}

void fun_ctrl_c()  //自定义信号
{
        int n;
        printf("\t你已经按了Ctrl+C 系统未处理...");
        for(n=0;n<4;n++)
        {
                printf("\t正在处理Ctrl+C信号处理函数");
        }
}
void fun_ctrl_z()  //自定义信号
{
        int n;
        printf("\t你已经按了Ctrl+Z 系统未处理...");
        for(n=0;n<6;n++)
        {
                printf("\t正在处理Ctrl+Z信号处理函数");
        }
}
void fun_ctrl_d()
{
        int n ;
        printf("\t你已经按了Ctrl+'\' 系统处理了该信号!!\n");
        for(n=0;n<2;n++)
        {
                printf("\t正在处理Ctrl+'\'信号处理函数");
        }
}

※管道

 无名管道pipe  &  FIFO管道(命名管道),都是通过内核缓冲区实现数据的传输;

pipe用于父进程和子进程之间的通信,通过pipe()系统调用创建并打开;

FIFO在磁盘上有对应的结点,但是有数据块,通过mknod()系统调用或mkfifo()函数来建立;一旦建立,任何进程都可以通过文件名将其打开进行读写;

管道实质是一个内核缓冲区,以先进先出的方式从缓冲区写读数据;

无名管道

建立管道用pipe函数,管道操作:

①父进程用pipe开辟管道,得到的两个文件描述符指向管道的两端;

②父进程用fork创建子进程,子进程也有两个文件描述符指向管道两端;、

③父(子)进程关闭读(写) 端,就可以进行写(读)操作;--读read函数 /  写write函数

(1)pipe函数--#include <unistd.h>

int pipe(int filedes[2]);

filedes[0]管道读取端;  filedes[1]管道写入端;  成功执行返回0,错误返回-1;

(2)memset函数--#include<string.h>

void *memset(void *s,int c,size_t n); 

s指向的内存区域内前n个字节以参数c填入,返回指向s的指针;c虽然声明是int,但是必须是unsigned char,范围在0-255; 

例:要求创建一个管道,复制进程【创建子进程】,父进程往管道中写入字符串,子进程从管道中读取前字符串;

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
int main()
{
        pid_t result;  //子进程返回的进程号
        int r_num;
        int pipe_fd[2]; //两个文件描述符
        char buf_r[100],buf_w[100];     //读写字符数组
        memset(buf_r,0,sizeof(buf_r));   //初始化数组设置为0
                                         //
        if(pipe(pipe_fd)<0)
        {
                perror("创建管道失败\n");
                return -1;
        }

        result = fork();//创建子进程,复制进程

        if(result<0)
        {
                perror("创建子进程失败\n");
                exit(-1);
        }
        else if(result==0)  //子进程
        {
                close(pipe_fd[1]);  //关闭写
                if((r_num=read(pipe_fd[0],buf_r,100))>0)   //进行读
                {
                        printf("子进程从管道中读取%d个字符,读取的字符内容是:%s\n",r_num,buf_r);
                }
                close(pipe_fd[0]);   //关闭读
                exit(0);//正常退出
        }
        else   //父进程
        {
                close(pipe_fd[0]);   //关闭读
                printf("请从键盘输入要写入管道的字符串\n");
                scanf("%s",buf_w);
                if(write(pipe_fd[1],buf_w,strlen(buf_w))!=-1)  //进行写
                {
                        printf("父进程向管道写入:%s\n",buf_w);
                }
                close(pipe_fd[1]);     //关闭写

                waitpid(result,NULL,0);  //waitpid,阻塞父进程,等待子进程退出;

                exit(0);
        }
}

注意:空字符不读取;

命名管道

命名管道的名字对应磁盘的索引节点,用该文件名,任何进程都有相应的权限对其进行访问。

创建命名管道的方式:mkfifo()和mknode()函数

例:设计两个程,要求用命名管道FIFO实现简单的聊天功能。

高级管道设计

:设计一个程序,要求用popen创建管道,实现“ls -l|grep 7-9c”的功能;

//高级管道设计
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
int main()
{
        FILE *fp; //文件指针
        int num;
        char buf[5000];  //字符缓冲区

        memset(buf,0,sizeof(buf));   //将buf所指向的内存区域的前sizeof(buf)得到>的字节
                                              //设置为0,初始化清空的操作
        printf("建立管道...\n");
        fp=popen("ls -l","r");   //调用popen函数,建立读管道
        if(fp!=NULL)
        {
                num=fread(buf,sizeof(char),5000,fp);
                /*
                if(num>0)
                {
                        printf("第一个命令是'ls-l',执行结果如下:\n");
                        printf("%s\n",buf);
                }
                */
                if(num<0)
                {
                        perror("读命令失败!\n");
                        exit(-1);
                }
                pclose(fp);
        }
        else
        {
                printf("用popen创建管道失败!\n");
                return 1;
        }
        fp=popen("grep insert.c","w");  //建立写管道
        printf("第二个命令是grep insert.c,运行结果是:\n");
        fprintf(fp,"%s\n",buf);
        pclose(fp);
        return 0;
}

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

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

相关文章

D455相机RGB与深度图像对齐,缓解相机无效区域的问题

前言 上一次我们介绍了深度相机D455的使用&#xff1a;intel深度相机D455的使用-CSDN博客&#xff0c;我们也看到了相机检测到的无效区域。 在使用Intel深度相机D455时&#xff0c;我们经常会遇到深度图中的无效区域。这些无效区域可能由于黑色物体、光滑表面、透明物体以及视…

大学国学搜题软件?分享7个软件和公众号,来对比看看吧 #经验分享#微信#媒体

在大学里&#xff0c;高效的学习工具可以帮助我们更好地管理时间和资源&#xff0c;提高学习效果。 1.彩虹搜题 这是个老公众号了 多语言查询支持&#xff0c;满足国际用户需求。全球通用&#xff0c;无障碍搜题。 下方附上一些测试的试题及答案 1、某酸碱指示剂的&#xf…

java线程池介绍

在Java中&#xff0c;线程池是用来管理和复用线程的一种机制&#xff0c;它可以显著提升程序性能&#xff0c;特别是在大量短期异步任务的场景下。以下是创建和使用线程池的基本步骤&#xff1a; 1.创建线程池: 使用java.util.concurrent.Executors类的静态工厂方法创建线程池&…

How to install a dataset from huggingface?

当我从抱抱脸上git clone imdb数据集时&#xff0c;plain_text里的文件是这样的&#xff1a;

【经验分享】不同内网服务器之间利用webdav互传文件

目录 0、前言1、授权webdav应用2、下载webdavclient33、替换相关代码 0、前言 最近&#xff0c;我在处理两台服务器间的文件传输问题时遇到了不少难题。这两台服务器并不处于同一内网环境&#xff0c;导致无法通过SFTP进行文件传输。由于这些服务器属于局域网&#xff0c;并且…

Python初步使用教程

1.基本输出print函数 a10 b20 print(a)#输出结束后会自动换行 print(b) print(a,b,猪猪侠)#print中sep决定三者之间会存在空格#连接方法一 print(猪猪,end) print(侠) #连接方法二&#xff08;只能是字符串和字符串连&#xff09; print(超级无敌)print(chr(67)) print(ord(猪…

PromptPort:为大模型定制的创意AI提示词工具库

PromptPort&#xff1a;为大模型定制的创意AI提示词工具库 随着人工智能技术的飞速发展&#xff0c;大模型在各行各业的应用越来越广泛。而在与大模型交互的过程中&#xff0c;如何提供精准、有效的提示词成为了关键。今天&#xff0c;就为大家介绍一款专为大模型定制的创意AI…

植物大战僵尸杂交版2024潜艇伟伟迷

在广受欢迎的游戏《植物大战僵尸》的基础上&#xff0c;我最近设计了一款创新的杂交版游戏&#xff0c;简直是太赞了&#xff01;这款游戏结合了原有游戏的塔防机制&#xff0c;同时引入新的元素、角色和挑战&#xff0c;为玩家提供了全新的游戏体验。 植物大战僵尸杂交版最新绿…

【大模型】基于Hugging Face调用及微调大模型(1)

文章目录 一、前言二、Transformer三、Hugging Face3.1 Hugging Face Dataset3. 2 Hugging Face Tokenizer3.3 Hugging Face Transformer3.4 Hugging Face Accelerate 四、基于Hugging Face调用模型4.1 调用示例4.2 调用流程概述4.2.1 Tokenizer4.2.2 模型的加载4.2.3 模型基本…

软件设计师(中级)概要笔记:基于软件设计师教程(第5版)

文章目录 作者前言1、计算机系统知识1.1、计算机系统基础知识1.1.1 计算机系统硬件基本组成1.1.2 中央处理单元1.1.3、数据表示原码、反码、补码和移码&#xff08;符号数&#xff09;符号数的应用定点数和浮点数 1.1.4、校验码奇偶校验循环冗余校验码海明码 1.2、计算机体系…

Day07 待办事项功能页面设计

​ 当前章节待办事项页面设计最终效果图: 一.布局设计 整个 待办事项页面 主要分上下布局,也就是分2行进行设计。第1 行 放搜索框和添加待办按钮,第2行 放置待办事项的内容。 那么 在视图中,怎么将页面分上下2行?就使用到Grid中 的 Grid.RowDefinitions ,就能实现将页面分…

每日5题Day18 - LeetCode 86 - 90

每一步向前都是向自己的梦想更近一步&#xff0c;坚持不懈&#xff0c;勇往直前&#xff01; 第一题&#xff1a;86. 分隔链表 - 力扣&#xff08;LeetCode&#xff09; /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;…

用HAL库改写江科大的stm32入门-输入捕获原理图示

原理与接线&#xff1a; &#xff08;输入捕获的结构&#xff09; cubeMx: PA11&#xff1a;

[ssi-uploader插件]解决如何接收服务器返回数据+修改参数名称

前言 ssi-uploader是一款非常好用的多文件上传插件&#xff0c;源码是开源的&#xff0c;在github上面即可下载&#xff1a; https://github.com/ssbeefeater/ssi-uploader 但是源码有些微小的不足&#xff0c;今天我们解决两点问题&#xff1a; 上传文件完成后&#xff0c…

12c rac dg开启日志应用报错 ora-00313 ora-00312 ora-17503 ora-15012处理

错误 当备库开启日志应用后看到告警日志报大量ora-00313\ora-00312\ora-17503等错误 处理方法 SQL> alter database clear unarchived logfile group 1; alter database clear unarchived logfile group 1 * ERROR at line 1: ORA-01156: recovery or flashback in pro…

OpenCV学习(4.7) Canndy边缘检测

1.目标 在本章中&#xff0c;我们将了解 Canny 边缘检测的概念OpenCV 的功能&#xff1a; cv.Canny&#xff08;&#xff09; Canny边缘检测是一种经典的边缘检测算法&#xff0c;由John F. Canny在1986年提出。Canny算法的目标是找到图像中真正的边缘&#xff0c;同时尽可能…

在nodeJS 中实现langchain 的Agent (实验笔记)

在nodeJS 中实现langchain 的Agent 实验过程记录如下&#xff1a; 1 构建一个Agent &#xff0c;使用两个工具 Calculator和TavilySearchResults 2 Tavily Search的API key 的获取 之前一直找不到一个合适的搜索引擎&#xff0c;Google Search 被墙&#xff0c;bing Search …

Mysql学习(六)——函数

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 三、函数3.1 字符串函数3.2 数值函数3.3 日期函数3.4 流程函数 三、函数 函数是指一段可以直接被另一段程序调用的程序或代码。 3.1 字符串函数 MySQL中内置了很…

Linux(Rocky)下 如何输入中文(切换中文输入法)教程

RockyLinux如何输入中文&#xff08;切换中文输入法&#xff09; 注意 在字符画界面的Linux系统中 默认不具备中文输入法的功能 需要SSH或其他远程工具来实现 问题 可能大家有的时候安装了一个虚拟机之后 想切换中文输入法 但是一直找不到方法 下面将利用Rocky9.2作为演示…

MT76X8 RF定频使用方法

一、从下面网址下载QA软件包&#xff0c;然后在WIN系统下安装QA环境。https://download.csdn.net/download/zhouwu_linux/89408573?spm1001.2014.3001.5503 在WINDOWS 7系统下先安装WinPcap_4_1_3.exe。 二、硬件连接。 模块上电&#xff0c;PC机 的IP配置成为10.10.18.100&a…