Linux 进程间通信编程
Linux 进程间通信编程(单机)
- 一、进程间通信概述
- 二、管道通信(默认无名管道)
- 1.创建无名管道函数pipe()
- 2.使用无名管道读取的代码练习
- 三、命名管道
- 1.创建有名管道函数mkfifo()
- 2.有名管道 无关系进程之间的读取代码练习
- a.写入的代码
- b.读取的代码
- 四、信号通信
- 1.信号机制
- 2.信号发送kill函数与raise函数
- 3.alarm函数
- 4.pause函数
- 5.信号处理:
- 6.signal信号处理函数
- 7signal处理信号的具体实例代码:
- 9.signal函数信号处理和kill函数信号发送练习代码
- 五、共享内存
- 1.共享内存使用步骤
- 2.创建共享内存
- 3.映射共享内存
- 4.解除映射
- 5.共享内存代码练习题
- 6.无血缘关系的进程使用共享内存通信代码练习
- 六、消息队列
- 七、信号量
一、进程间通信概述
1、目的:
1)数据传输:
一个进程需要将它的数据发送给另一个进程
2)资源共享
多个进程之间共享同样的资源
3)通知事件
一个进程需要向另一个或一组进程发送消息,通知他们发生了某种事件
4)进程控制
有些进程希望完全控制另一个进程的执行,此时控制进程希望能够拦截另一个进程的所以操作,并能够及时知道它的状态改变
2.通信方式:
管道(pipe)和有名管道(FIFO)
信号(signal)
消息队列
共享内存
信号量
套接字(socket)
二、管道通信(默认无名管道)
管道是单向的、先进先出的,它把一个进程的输出和另一个进程的输入连接在一起。一个进程(写进程)在管道的尾部写入数据,另一个进程(读进程)从管道的头部读出数据
无名管道 只有有亲缘关系的进程来使用 因为管道无名很难找到 所以如果父进程创建管道 然后创建子进程 子进程可以继承父进程的变量 才能找到管道
头部与尾部用文件描述符来表示
**数据被一个进程读出后,将被从管道中删除,其他读进程将不能在读到这些数据。**管道提供了简单的流控制机制,进程试图读空管道时,进程将阻塞,同样,管道已满时,进程再试图向管道写入数据,进程将阻塞
1.创建无名管道函数pipe()
头文件是#include<unistd.h>
创建成功返回0 不成功返回-1
读使用read( ) 写使用write( )
上图 需要关闭父进程的读 关闭子进程的写 使它成为单向
2.使用无名管道读取的代码练习
父进程向无名管道写入小写字母 子进程读出后转换为大写字母写入管道 父进程再将其读出 具体代码(可运行)
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/wait.h>
#include <ctype.h>int main(){int fd[2];pid_t pid;char buffer[100];//缓存区int ret,i;int ret_num;memset(buffer,0,sizeof(buffer));//清空缓存区ret = pipe(fd);//创建无名管道 返回0表示创建成功if(ret == -1){perror("pipe error!\n");exit(-1);}pid = fork();//创建子进程if(pid < 0){//创建失败关闭管道出入口perror("fork error!\n");close(fd[0]);close(fd[1]);exit(-1);}else if(pid > 0){//父进程if((write(fd[1],"hello",5)) != -1){//向无名管道中写入hello fd【1】是管道写端printf("father write success!\n");}if((write(fd[1],"pipe",5)) != -1){//再次写printf("father write success!\n");}wait(NULL);//等待子进程结束read(fd[0],buffer,10);//读取管道内容进入缓冲区printf("father read butter:%s\n",buffer);close(fd[0]);close(fd[1]);}else{//子进程if((ret_num = read(fd[0],buffer,10)) != -1){//读取管道中的内容 存放在缓存区 成功 返回读取个数printf("child:read num is %d , buffer:%s\n",ret_num,buffer);}for(i = 0;i < ret_num -1;i++){buffer[i] = toupper(buffer[i]);//将缓冲区小写转为大写}write(fd[1],buffer,ret_num);//将缓冲区内容写入管道close(fd[0]);close(fd[1]);}return 0;}
三、命名管道
命名管道和无名管道基本相同,但也有不同点:无名管道只能由有血缘关系的进程使用;但通过命名管道,不相关的进程也能交换数据 因为可以根据名字找到管道 无需继承
1.创建有名管道函数mkfifo()
第一个参数 管道名 其余进程 可通过管道名打开管道
第二个参数 常见属性:S_IRUSE 可读 S_IWUSR可写 S_IXUSR可执行 S_IXRWU 可读可写可执行
int mkfifo(const char * pathname,mode_t mode);
函数说明
mkfifo()会依参数pathname建立特殊的FIFO文件,该文件必须不存在,而参数mode为该文件的权限(mode%~umask),因此 umask值也会影响到FIFO文件的权限。Mkfifo()建立的FIFO文件其他进程都可以用读写一般文件的方式存取。当使用open()来打开 FIFO文件时,O_NONBLOCK旗标会有影响
1、当使用O_NONBLOCK 旗标时,打开FIFO 文件来读取的操作会立刻返回,但是若还没有其他进程打开FIFO 文件来读取,则写入的操作会返回ENXIO 错误代码。
例 fd = open (FIFO_SEVER,O_RDONLY|O_NONBLOCK);
2、没有使用O_NONBLOCK 旗标时,打开FIFO 来读取的操作会等到其他进程打开FIFO文件来写入才正常返回。同样地,打开FIFO文件来写入的操作会等到其他进程打开FIFO 文件来读取后才正常返回
打开方式 与文件相同 头文件#include<fcntl.c>
fd = open(FIFO_SEVER,O_WRONLY);
2.有名管道 无关系进程之间的读取代码练习
a.写入的代码
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#include<memory.h>
#include<unistd.h>#define FIFO_SEVER "/home/2022/0305/fifosever"int main(){int fd;int ret;char w_buf[1000];memset(w_buf,0,1000);if((mkfifo(FIFO_SEVER,O_CREAT|O_EXCL) < 0) && (errno != EEXIST)){printf("cannt create fifosever!\n");exit(0);}fd = open(FIFO_SEVER,O_WRONLY);if( fd == -1){if(errno == ENXIO)printf("open error,no reading process\n");}printf("please input \n");scanf("%s",w_buf);ret = write(fd,w_buf,strlen(w_buf));if(ret == -1){printf("write fifo error try later!\n");}else{printf("write fifo success ret num is %d\n",ret);}return 0;
}
b.读取的代码
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include <errno.h>
#include<memory.h>
#include<unistd.h>#define FIFO_SEVER "/home/2022/0305/fifosever"int main(){int fd;char r_buf[1000];int ret;fd = open(FIFO_SEVER,O_RDONLY);if(fd == -1){printf("open fifosever fail!\n");exit(0);}memset(r_buf,0,sizeof(r_buf));ret = read(fd,r_buf,100);if(ret == -1){if(errno == EAGAIN)printf("no data avalible\n");}else{printf("read fifo success! read is %s\n",r_buf);sleep(1);}unlink(FIFO_SEVER);return 0;
}
四、信号通信
1.信号机制
信号机制是Unix系统中最为古老的进程间通讯机制,很多条件可以产生一个信号:
1.当用户按某些按键时,产生信号
2.硬件异常产生信号:除数为0、无效的存储访问等等。这些情况通常由硬件检测到,将其通知内核,然后内核产生适当的信号通知进程,例如,内核对正在访问一个无效存储区的进程参数一个SIGSEGV信号
3。进程用kill函数将信号发送给另一个进程
4.用户可用kill命令将信号发送给其他进程
2.信号发送kill函数与raise函数
raise默认发给自己
kill第一个参数是指定发生给谁
如果>0 就是指定的进程的ID
=0 把这个信号发生给这个进程所在进程组的每一个进程
<-1 取绝对值 看哪个进程组的ID等于这个绝对值 然后发送信号给这个进程组中所有进程
=-1 发送给除自己以外 所有的进程
当第二个参数 写0 没有信号发送 因为64个信号中没有序号为0的
没有错 意义是检测我有没有权限向指定的pid发送信号
3.alarm函数
注意alarm函数 如果没有给予执行动作的话 默认动作是终止进程
每个进程只能有一个闹钟时间,如果在调用alarm时,以前已为该进程设置过闹钟时间,而且它还没有超时,以前登记的闹钟时间则被新值代换
如果有以前等级的尚未超过的闹钟时间,而这次seconds值是0,则表示取消以前的闹钟
alarm函数可以写多个 但程序运行时 只能有一个生效
并不是只能写一个 可以错峰使用alarm
返回值是没进行完的剩余时间
4.pause函数
5.信号处理:
1.忽略信号
2.执行用户希望的动作
通知内核再某种信号发送时,调用一个用户函数,再用户函数中,执行用户希望的处理
3.执行系统默认动作
对大多数信号的系统默认动作是终止该进程
其中信号集函数组不常用
6.signal信号处理函数
第一个参数 是想捕捉哪个信号
第二个参数是指向函数的指针
所以要传 你想要调用的执行函数的地址(函数名)或者SIG_IGN表示忽略此信号 或者SIG_DFL表示系统默认方式处理(一般系统默认方式 是结束此进程)
7signal处理信号的具体实例代码:
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<unistd.h>void func(int signal){if(signal == SIGINT){//当接收到的信号是SIGINT时 执行printf中的语句printf("I get SIGNAL!\n");}else if(signal == SIGQUIT){//同上printf("I get SIGQUIT!\n");}
}int main(){//使用signal函数 处理信号 执行自己想要的动作 即func函数printf("waiting for signal SIGINT or SIGQUIT!\n");signal(SIGINT,func);//当接收到SIGINT信号后 执行func里的动作signal(SIGQUIT,func);//同上pause();//进程阻塞 等待一个信号的产生 进程才会终止return 0;
}
执行结果:
9.signal函数信号处理和kill函数信号发送练习代码
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<signal.h>
#include<sys/types.h>
#include<unistd.h>int main(){pid_t pid;int select;void func(int signal){if(signal == SIGINT){printf("I get SIGINT!\n");}else if(signal == SIGQUIT){printf("I get SIGQUIT!\n");exit(1);}}pid = fork();if(pid == 0){signal(SIGINT,func);signal(SIGQUIT,func);pause();}else{printf("please input your select 1 or 2\n");scanf("%d",&select);if(select == 1){kill(pid,SIGINT);//向pid这个进程发送ISGINT这个信号}else{kill(pid,SIGQUIT);}sleep(1);//让父进程睡1s 防止子进程成孤儿进程} return 0;
}
五、共享内存
共享内存是被多个进程共享的一部分物理内存,共享内存是进程间共享数据的一种最快的方法,一个进程向共享内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。
1.共享内存使用步骤
1.创建共享内存函数shmget( )
2.映射内核中的共享内存的地址到进程中去 函数为shmat( )
3.使用共享内存 怎么用 直接用
4.有进程不用再使用共享内存 不使用共享内存的进程 用shmdt ( )解除映射
5.所有进程都不用了 有一个进程删除共享内存就可以 使用shmctl( )
2.创建共享内存
key 键值 指向我想创建共享内存的id(相当于给要创建的共享内存的文件名 而返回值是文件描述符)
用于区分不同的共享内存 创建成功后就再也不使用key值了 而使用函数返回的ID值即共享内存标识符。
第二个参数 指定共享内存大小 大小在4k以下 系统都是给4k个字节
第三个参数 表示创建的共享内存的模式 IPC_CREAT不存在就创建
加上|IOC_EXCL表示存在就出错 也可以加权限
3.映射共享内存
第一个参数就是共享内存标识符
第二个参数 是字节地址 是将共享内存的起始地址放在这个参数中 多数情况下写NULL 这是让系统帮我在进程中找个地址存放映射共享内存的地址,而函数返回值 就是这个共享内存映射的地址 以后使用共享内存 就用这个返回值;
第三个参数 取0 表示映射了共享内存在我的进程中是可读可写的
如果映射失败 返回-1 所以以后使用这个函数 不能将返回值直接跟-1比
因为-1是整数 而返回值是char 要将-1 强转为void**
4.解除映射
解除映射函数参数是 映射函数返回值;
5.共享内存代码练习题
父进程向共享内存输入字符 子进程判断是否是自己要接收的信息 是的话 输出共享内存中的信息
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/shm.h>
#include<sys/ipc.h>
#include<sys/wait.h>#define BUFFER_SIZE 2048int main(){pid_t pid;int shmid;//存放共享内存描述符 类似与文件描述符char *shm_addr;//用于存放映射共享内存的地址char flag[] = "parent";//后期将其放入共享内存中 用以区分是否是子进程要接收的信息char buff[BUFFER_SIZE];//缓存区if((shmid = shmget(IPC_PRIVATE,BUFFER_SIZE,0666)) < 0){//创建共享内存perror("shmget!\n");exit(1);}else{printf("create share memory success!\n");}pid = fork();//创建子进程if(pid < 0){perror("fork!\n");exit(1);}else if(pid == 0){//子进程shm_addr = shmat(shmid,NULL,0);//映射共享内存if(shm_addr == (void*)-1){//判断是否映射成功perror("shmat!\n");exit(1);}else{printf("child:Attach share memory success address is:%p\n",shm_addr);//输出映射地址}while(strncmp(shm_addr,flag,strlen(flag))){//比较共享内存信息是否是子进程要接收的printf("child:not my data waiting later...\n");//不等于0的话 睡3s等待sleep(3);}strcpy(buff,shm_addr + strlen(flag));//是为要接收的信息 跳过parent这个字符串 接收其后面的信息printf("child: share memory:%s\n",buff);//输出共享内存的信息if(shmdt(shm_addr) < 0){//解除映射perror("child:shmdt error!\n");exit(1);}else{printf("child:detached share memory\n");}}else{//父进程sleep(1);if((shm_addr = shmat(shmid,NULL,0)) == (void*)-1){//映射共享内存perror("father:shmat error!\n");exit(1);}else{printf("father attach share memory is %p\n",shm_addr);//输出映射地址}sleep(1);printf("input string:\n");fgets(buff,BUFFER_SIZE - strlen(flag),stdin);//输入信息进入缓存区 要去掉parent的大小 因为共享内存空间需要存在parent字符串 不能完全输入缓存区的全部大小strncpy(shm_addr + strlen(flag),buff,strlen(buff));//给parent字符串留位置 向共享内存输入信息strncpy(shm_addr,flag,strlen(flag));//向共享内存开头输入parent字符串if(shmdt(shm_addr) < 0){//解除映射perror("father:shmdt error!\n");exit(1);}else{printf("father:detached share memory\n");}waitpid(pid,NULL,0);//等待子进程结束if(shmctl(shmid,IPC_RMID,NULL) == -1){//释放共享内存perror("shmtlc error!\n");exit(1);}else{printf("delete share memory\n");}}return 0;
}
6.无血缘关系的进程使用共享内存通信代码练习
将共享内存类型转换为结构体类型 其中两个成员一个是written用以区分是读还是写 还有一个用来存放信息
共享内存结构体
#define TEXT_SZ 2048struct shared_use_st
{int written;char text[TEXT_SZ];
};
写进程
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/shm.h>
#include "shmdata.c"
#include <string.h>#define BUFFSIZE 2048int main()
{int running = 1;void *shm = NULL;//存放映射共享内存地址struct shared_use_st * shared = NULL;//定义共享内存结构体类型指针char buffer[BUFFSIZE + 1];int shmid;shmid = shmget((key_t)1234,sizeof(struct shared_use_st),0666|IPC_CREAT);//有就打开 没有就创建if(shmid == -1){fprintf(stderr,"shmget failed\n");exit(EXIT_FAILURE);}shm = shmat(shmid,0,0);// 映射共享内存if(shm == (void*)-1){fprintf(stderr,"shmat failed\n");exit(EXIT_FAILURE);}printf("\n memary attached at %p\n",shm);shared = (struct shared_use_st *)shm;//将映射地址强转为共享内存结构体类型 并让share指向它//shared->written = 0;while(running){while(shared->written == 1)//当wirtten为1时表示是另一个进程正在读共享内存 此时需要等待{sleep(1);printf("waitting...\n");}while(running){printf("Enter some text:\n");fgets(buffer,BUFFSIZE,stdin);// 向缓存区写入数据strncpy(shared->text,buffer,TEXT_SZ);//将缓存区的信息写入共享内存shared->written = 1;//写完 将written改为1 这要另一个进程得到written为1表示它可以读了if(strncmp(shared->text,"end",3) == 0)//如果输入end 结束循环running = 0;}}if(shmdt(shm) == -1)//解除映射{fprintf(stderr,"shmdt failed\n");exit(EXIT_FAILURE);}sleep(2);exit(EXIT_SUCCESS);return 0;
}
读进程
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/shm.h>
#include "shmdata.c"
#include <string.h>int main()
{int running = 1;void *shm = NULL;struct shared_use_st * shared;int shmid;shmid = shmget((key_t)1234,sizeof(struct shared_use_st),0666|IPC_CREAT);//有1234这个共享内存就打开 没有就创建if(shmid == -1){fprintf(stderr,"shmget failed\n");exit(EXIT_FAILURE);}shm = shmat(shmid,0,0);if(shm == (void*)-1){fprintf(stderr,"shmat failed\n");exit(EXIT_FAILURE);}printf("\n memary attached at %p\n",shm);shared = (struct shared_use_st *)shm;shared->written = 0;while(running){if(shared->written != 0){printf("you wrote : %s\n",shared->text);sleep(rand()%3);shared->written = 0;//读完将weitten改为0 另一个进程得到written=0 就可以写了if(strncmp(shared->text,"end",3) == 0)running = 0;}elsesleep(1);}if(shmdt(shm) == -1){fprintf(stderr,"shmdt failed\n");exit(EXIT_FAILURE);}if(shmctl(shmid,IPC_RMID,0) == -1){fprintf(stderr,"stmctl failed\n");exit(EXIT_FAILURE);}exit(EXIT_SUCCESS);return 0;
}
六、消息队列
第一个参数是消息队列的ID即创建时返回值
第二第三个参数是要配合的 第二个参数给出了要开始写的地址 第三个参数表示写多少个字节
第四个参数 是flag 不需要写0(阻塞)
查参数
释放
七、信号量
Linux 进程间通信编程相关推荐
- Linux进程间通信编程
一.进程间通信概述 基本目的: 1.数据传输 一个进程需要将它的数据发送给另一个进程 2.资源共享 多个进程之间共享同样的资源 3.通知事件 一个进程需要向另一个或一组进程发送 ...
- Linux网络编程--进程间通信(一)
进程间通信简介(摘自<Linux网络编程>p85) AT&T 在 UNIX System V 中引入了几种新的进程通讯方式,即消息队列( MessageQueues),信号量( s ...
- linux进程间通信:system V 信号量 生产者和消费者模型编程案例
生产者和消费者模型: 有若干个缓冲区,生产者不断向里填数据,消费者不断从中取数据 两者不冲突的前提: 缓冲区有若干个,且是固定大小,生产者和消费者各有若干个 生产者向缓冲区中填数据前需要判断缓冲区是否 ...
- 实验六 Linux进程编程,Linux系统编程实验六:进程间通信
<Linux系统编程实验六:进程间通信>由会员分享,可在线阅读,更多相关<Linux系统编程实验六:进程间通信(10页珍藏版)>请在人人文库网上搜索. 1.实验六:进程间通信l ...
- 【Linux系统编程】进程间通信之无名管道
00. 目录 文章目录 00. 目录 01. 管道概述 02. 管道创建函数 03. 管道的特性 04. 管道设置非阻塞 05. 附录 01. 管道概述 管道也叫无名管道,它是是 UNIX 系统 IP ...
- 【Linux系统编程】进程间通信概述
00. 目录 文章目录 00. 目录 01. 进程间通信概述 02. 进程间通信目的 03. 进程间通信机制 04. 附录 01. 进程间通信概述 进程是一个独立的资源分配单元,不同进程(这里所说的进 ...
- 【Linux | 系统编程】Linux系统编程(文件、进程线程、进程间通信)
文章目录 Linux系统编程 文件IO open/close函数 read/write函数 文件描述符 阻塞.非阻塞 fcntl函数 lseek函数 传入传出参数 文件系统 文件存储 文件操作 sta ...
- Linux系统编程【文件IO、进程、进程间通信、信号、线程、互斥】
linux系统编程 个人通过学习,手打了一份48000字的Linux系统编程的笔记,包含了[文件IO.进程.进程间通信.信号.多线程.互斥]等知识点,并给出了大量的代码案例对每个重要的知识点进行了代码 ...
- c语言系统编程八:Linux进程间通信之消息队列
Linux进程间通信之消息队列 一 消息队列概述 二 消息队列的特点 三 消息队列的创建和使用 3.1 获取系统唯一的key值 3.2 创建消息队列 3.3 查看消息队列和删除消息队列的shell命令 ...
最新文章
- codevs——2894 Txx考试(背包)
- Xcode 环境变量(绝对路径与相对路径)
- make 编译可执行
- NDK 与 JNI 的关系
- Vue.js 状态管理
- 树莓派实现AD转换(pcf8591模块)
- leetcode-49-字母异位词分组
- 设置 Xcode 自动生成代码片段
- python request url编码_Python 爬虫 (requests) 发送中文编码的 HTTP POST 请求
- HomeBrew 更换为国内源--提高brew命令操作速度
- 数据结构----出栈顺序有效性的判断
- Android之notificaction使用
- 【三维路径规划】基于matlab遗传算法无人机三维路径规划【含Matlab源码 1526期】
- 关于指针赋初值为NULL的问题
- (中英)作文 —— 标题与小标题
- spring boot中的banner制作
- 无线ap安全dhcp服务器,AC+AP时代——办公区内满足安全性又够人性化的WiFi漫游设置攻略...
- 适合苹果的蓝牙耳机推荐,音质超好的蓝牙耳机推荐
- 基于JSP技术的游泳馆管理系统
- 原谅帽大作战游戏程序
热门文章
- VS+Qt应用开发-Qt+Halcon显示图片,实现鼠标缩放、移动图片
- python frozenset_Python中set与frozenset方法和区别详解
- 调薪之后该思考的问题
- ps cc2018启动界面无响应解决方案
- Java实现 LeetCode 799 香槟塔 (暴力模拟)
- 贵州大学计算机科学与技术学院电子信息,贵州大学计算机科学与技术学院官网...
- 手机显示android怎么办,安卓手机卡顿怎么办?你必须要知道的办法!
- oauth2 feign 报401的错误
- 用python画多个圆_Python用图例在网格上绘制多个圆
- @webservlet注解详解