Linux 基础入门操作 第九章 进程间通信之管道

news/2024/9/24 8:51:07/

第九章 进程间通信

进程通信有如下一些目的:
A、数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几 M 字节之间
B、共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程,应该立刻看到。
C、通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
D、资源共享:多个进程之间共享同样的资源。为了作到这一点,需要内核提供锁和同步机制。
E、进程控制:有些进程希望完全控制另一个进程的执行(如 Debug 进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

现在 linux 使用的进程间通信方式:
1)管道(pipe)和有名管道(FIFO)
2)信号(signal)
3)消息队列
4)共享内存
5)信号量
6)套接字(socket)

9.1 管道

管道是 Linux 支持的最初 Unix IPC 形式之一,具有以下特点:
(1)管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;
(2)只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程);
(3)单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的
文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。
(4)数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添
加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。

#include <unistd.h>
int pipe(int fd[2])

返回的 fd[0]用于读,fd[1]用于写。因此,一个进程在由 pipe()创建管道后,一般再 fork 一个子进程,然后通过管道实现父子进程间的通信(因此也不难推出,只要两个进程中存在亲缘关系,这里的亲缘关系指的是具有共同的祖先,都可以采用管道方式来进行通信)。

管道的局限:

  • 只支持单向数据流;
  • 只能用于具有亲缘关系的进程之间;
  • 没有名字;
  • 管道的缓冲区是有限的(管道制存在于内存中,在管道创建时,为缓冲区分配一个页面大小);
  • 管道所传送的是无格式字节流,这就要求管道的读出方和写入方必须事先约定好数据的格式。

9.1.1 单项管道

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
void read_from_pipe(int fd)
{char message[100];read(fd,message,100);printf("The messiage from parent is %s\n",message);
}void write_to_pipe(int fd)
{char *message = "hello world\n";write(fd,message,strlen(message)+1);
}
int main()
{int fd[2];pid_t pid;int stat_val;if(pipe(fd)){printf("error");exit(1);}pid=fork();switch(pid){case -1:printf("fork error");exit(1);case 0:printf("The child process is:%d\n",getpid());close(fd[1]);read_from_pipe(fd[0]);exit(0);default:printf("The father process is:%d\n",getpid());close(fd[0]);write_to_pipe(fd[1]);wait(&stat_val);exit(0);}
return 0;
}

9.1.2 全双工通讯,两个通道

这里用pipe1 父进程发送子线程 , ,pipe2 子线程发送父父线程

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
void child_rw_pipe(int readfd,int writefd)
{char *message1="from child process\n";write(writefd,message1,strlen(message1)+1);char message2[100];read(readfd,message2,100);printf("child read is: %s\n",message2);
}
void parent_rw_pipe(int readfd,int writefd)
{char *message1 = "from parent process\n";write(writefd,message1,strlen(message1)+1);char message2[100];read(readfd,message2,100);printf("parent read is: %s \n",message2);}
int main()
{int pipe1[2],pipe2[2];pid_t pid;int stat_val;if(pipe(pipe1)){printf("error");exit(1);}if(pipe(pipe2)){printf("error");exit(1);}pid=fork();switch(pid){case -1:printf("fork error");exit(1);case 0:printf("The child process is:%d\n",getpid());close(pipe1[1]);close(pipe2[0]);child_rw_pipe(pipe1[0],pipe2[1]);exit(0);default:printf(“Thefather process is:%d\n",getpid());close(pipe1[0]);close(pipe2[1]);parent_rw_pipe(pipe2[0],pipe1[1]);wait(&stat_val);exit(0);}
return 0;
}

9.2 有名管道

FIFO 不同于管道之处在于它提供一个路径名与之关联,以 FIFO 的文件形式存在于文件系统中。这样,即使与 FIFO 的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过 FIFO 相互通信(能够访问该路径的进程以及 FIFO 的创建进程之间),因此,通过 FIFO 不相关的进程也能交换数据。

include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
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 错误代码。
2、没有使用 O_NONBLOCK 旗标时,打开 FIFO 来读取的操作会等到其他进程打开 FIFO 文件来写入才正常返回。同样地,打开 FIFO 文件来写入的操作会等到其他进程打开 FIFO 文件来读取后才正常返回。

下面用server 发送,client 接收实现全双工通讯。

server.c

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#define FIFO_READ "readfifo"
#define FIFO_WRITE "writefifo"
#define BUF_SIZE 1024
int main(void)
{int wfd,rfd;char buf[BUF_SIZE];int len;umask(0);if(mkfifo(FIFO_WRITE,S_IFIFO|0666)){printf("can't create FIFO %s beause %s",FIFO_WRITE,strerror(errno));exit(1);}umask(0);
wfd = open(FIFO_WRITE,O_WRONLY);
if(wfd == -1){
printf("open FIFO %s error :%s",FIFO_WRITE,strerror(errno));
exit(1);
}
while((rfd = open(FIFO_READ,O_RDONLY)) == -1){
sleep(1);
}
while(1) {
printf("Server:");
fgets(buf,BUF_SIZE,stdin);
buf[strlen(buf)-1] = '\0';
if(strncmp(buf,"quit",4) == 0) {
close(wfd);
unlink(FIFO_WRITE);
close(rfd);
exit(0);
}
write(wfd,buf,strlen(buf));
len = read(rfd,buf,BUF_SIZE);
if(len > 0) {
buf[len] = '\0';
printf("Client:%s\n",buf);
}
}
}

linux中的 umask 函数主要用于:
在创建新文件或目录时,屏蔽掉新文件或目录不应有的访问允许权限。文件的访问允许权限共有9种,分别是:r w x r w x r w x(它们分别代表:用户读 用户写 用户执行 组读 组写 组执行 其它读 其它写 其它执行)。
其实这个函数的作用,就是设置允许当前进程创建文件或者目录最大可操作的权限,比如这里设置为0,它的意思就是0取反再创建文件时权限相与,也就是:(~0) & mode 等于八进制的值0666 & mode了,这样就是给后面的代码调用函数mkfifo给出最大的权限,避免了创建目录或文件的权限不确定性。

S_IFIFO|0666”指明创建一个命名管道且存取权限为0666,即创建者、与创建者同组的用户、其他用户对该命名管道的访问权限都是可读可写(这里要注意umask对生成的管道文件权限的影响)

client.c

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#define FIFO_READ "writefifo"
#define FIFO_WRITE "readfifo"
#define BUF_SIZE 1024
int main(void)
{int wfd,rfd;char buf[BUF_SIZE];int len;umask(0);if(mkfifo(FIFO_WRITE,S_IFIFO|0666)){printf("can't create FIFO %s beause %s",FIFO_WRITE,strerror(errno));exit(1);}while((rfd = open(FIFO_READ,O_RDONLY)) == -1) {sleep(1);}Wfd = open(FIFO_WRITE,O_WRONLY);if(wfd == -1){printf("open FIFO %s error :%s",FIFO_WRITE,strerror(errno));exit(1);}while(1){len = read(rfd,buf,BUF_SIZE);if(len > 0) {buf[len] = '\0';printf("Server:%s\n",buf);}printf("Client:");fgets(buf,BUF_SIZE,stdin);buf[strlen(buf)-1] = '\0';if(strncmp(buf,"quit",4) == 0) {close(wfd);unlink(FIFO_WRITE);close(rfd);exit(0);}write(wfd,buf,strlen(buf));}return 0;
}

http://www.ppmy.cn/news/1529740.html

相关文章

【通信基础】精讲通信天线种类及CAN总线和集群关系

前言 在通信行业中&#xff0c;天线的种类非常多&#xff0c;涵盖了从简单的无线电天线到复杂的相控阵天线。这些天线的种类和形态各异&#xff0c;以满足不同频率、应用场景和通信需求。以下是一些主要的天线种类&#xff1a; 1. 通信天线种类 1.1 偶极子天线 (Dipole Ant…

切换到WDDM模式,Tesla M4可以用于本地显示输出了!

正文共&#xff1a;1333 字 21 图&#xff0c;预估阅读时间&#xff1a;2 分钟 上次安装完Tesla M4显卡之后&#xff08;HPE服务器通过显卡直通安装Tesla M4&#xff0c;这算亮机成功了吗&#xff1f;&#xff09;&#xff0c;系统识别正常&#xff0c;但是不能用于显示&#x…

Nginx基础详解1(单体部署与集群部署、负载均衡、正反代理、nginx安装)

本阶段的任务 1.学会集群的操作概念 2.完成对Nginx的入门操作 3.使用Nginx实现集群和负载均衡 4.使用Nginx实现高可用的方案 目录 1.单体部署与集群部署 1.1单体部署的概念 1.2单体部署的优缺点 1.3集群部署的概念 1.4集群部署的优缺点 1.5集群部署需要注意的点 1.…

BOE(京东方)携故宫博物院举办2024“照亮成长路”公益项目落地仪式以创新科技赋能教育可持续发展

2024年9月20日&#xff0c;BOE&#xff08;京东方&#xff09;“照亮成长路”智慧教室落成暨百堂故宫传统文化公益课山西活动落地仪式在山西省太原市娄烦县实验小学隆重举行。自“照亮成长路”教育公益项目正式设立以来&#xff0c;BOE&#xff08;京东方&#xff09;持续以创新…

ES6中迭代器与生成器知识浅析

ES5及以下版本对JS几种集合&#xff0c;要存取数据一般需要用循环语句来遍历&#xff0c;就要初始化一个或多个变量来记录每一次循环在数据集合中的位置或数据值。这里容易出现超出边界问题&#xff0c;造成程序出错。另外&#xff0c;对于多次循环也需要跟踪理清各个变量关系及…

MySQL关卡任务书

基础任务 1. MySQL中char和varchar的区别&#xff0c;varchar&#xff08;100&#xff09;中的一百的含义&#xff0c;能存放多少汉字&#xff1f; CHAR类型用于固定长度的字符串。当存储数据时&#xff0c;会自动填充到指定长度&#xff0c;即使实际内容没有达到该长度。 VA…

Android中如何调用DLL文件

在 Android 设备上直接调用 DLL&#xff08;动态链接库&#xff09;文件是不可行的&#xff0c;因为 DLL 文件是 Windows 操作系统下的一种可执行文件格式&#xff0c;而 Android 操作系统基于 Linux 内核&#xff0c;两者在底层架构和 API 支持上存在根本差异。不过&#xff0…

软技能与AI技术的融合

一、引言 ----  随着人工智能&#xff08;AI&#xff09;和生成式人工智能&#xff08;AIGC&#xff09;如ChatGPT、Midjourney、Claude等大语言模型的迅速崛起&#xff0c;AI辅助编程工具已经变得越来越普遍。这不仅意味着程序员的工作方式正在发生深刻的变革&#xff0c;同…