🎁个人主页:我们的五年

🔍系列专栏:Linux课程学习 

🌷追光的人,终会万丈光芒

🎉欢迎大家点赞👍评论📝收藏⭐文章

Linux学习笔记:

 https://blog.csdn.net/djdjiejsn/category_12669243.html

前言:

从本篇开始,就要来谈谈进程间通信。方式有很多,设计的模式也不一样。根据后面网络的编程的需求,某些进程间通信可能用的更广。文章会从进程间通信的介绍,匿名管道,命名管道,system V 共享内存讲解,system V可能会放在下面文章讲解。

目录

一.进程间通信介绍

1.1进程间通信的目的:

1.2进程间通信的发展:

1.3进程间通信的分类:

二.匿名管道

2.1管道的介绍:

2.2匿名管道创建:

2.2.1函数介绍:

2.2.2函数解读:

2.3匿名管道实现通信实现:

2.3.1关闭相应的端口:

2.3.2写端关闭,读端如何反应?

2.3.3读端关闭,写端如何反应? 

2.3.4其他情况:

三.命名管道

3.1命名管道的介绍:

它和匿名管道的区别主要体现在:

3.2如何创建管道?

3.2.1在命令行中创建一个命名管道:

3.2.2通过函数创建:

 四.关于Makefile文件的编写:


一.进程间通信介绍

我们知道,进程是具有独立性的,那么我们想要让一个进程从另外一个进程得到信息,我们要怎么做呢?这就有了进程间通信。

进程间通信的本质是让不同的进程看到同一份资源

这块内存的特点:由操作系统(OS)创建,是内存级空间(没必要到达磁盘这一级去)。

1.1进程间通信的目的:

🥬1.数据传输一个进程需要把它的数据发生给另外一个进程。

🥬2.资源共享:多个进程直接共享同样的资源

🥬3.通知事件:一个进程向另一个进程或者一组进程发生消息,通知它们发生某种事件。

🥬4.进程控制:有些进程希望完全控制另一个进程的执行,此时控制进程希望能够拦截另一个进程的所有陷入和异常,并且能及时知道它的状态信息。

上面所有的目的都是因为进程间通信,可以让不同进程间传递数据

1.2进程间通信的发展:

管道
System V进程间通信
POXIS进程间通信

1.3进程间通信的分类:

下面是本地通信的分类,除了本地通信,还有网络通信

管道:匿名管道(pipe),命名管道(FIFO)。

System V:System 消息队列,System V共享内存,System V 信号量。

POSXI IPC:消息队列,共享内存,信号量,互斥量,条件变量,读写锁。


二.匿名管道

2.1管道的介绍:

管道式一种古老的通信方式,它设计不要专门去设计一套通信,而是用文件进行修改就能通信的方式。

我们把一个进程连接到另一个进程的数据流称为管道

管道的特性是面向字节流的。单向的

在创建多管道的时候,会有BUG。父子进程能进程同样的文件描述符,但在创建躲管道的时候,这也是他缺点。

匿名管道也是只能用于有亲缘关系之间的进程进行通信。因为他们要进行继承父进程的文件描述符,才能让他们看到同样的空间。

在Ubuntu系统中,管道的大小一般是64KB。

2.2匿名管道创建:

2.2.1函数介绍:

头文件:
#include <unistd.h>

函数原型:

int pipe(int pipefd[2]);

2.2.2函数解读:

🎒功能:创建一无名管道。

🎒参数:传入的fd表示的int数组类型的指针。如果成功,fd[0]中表示读端的文件描述符,fd[1]表示的写端的文件表述符。

🎒返回值:成功返回0,错误返回错误代码。

2.3匿名管道实现通信实现:

下面的代码实现的是,打开一个匿名管道。然后返回的是文件描述符。通过创建子进程,子进程继承父进程的文件描述符。这样就让父子进程看到同一个文件。因为要保证单向性,最好就是把没有用的端关闭。这样尽量保证安全性。

2.3.1关闭相应的端口:

比如父进程只进行读取数据,那么他就把写入端关闭。子进程进行写入,那么子进程就把读端关闭。

2.3.2写端关闭,读端如何反应?

另外如果管道没有东西,读端就阻塞住,等待写端写入数据。如果写端关闭,那么读端把管道里的数据读完以后,就会读到空。返回的就是0。这样就可以判断可以关闭读端了。

2.3.3读端关闭,写端如何反应? 

读端关闭,写端进程会发生错误,管道里的数据也会被处理。匿名管道读端关闭后,写端进程继续写入数据会面临错误返回或者收到终止信号等情况,并且管道内剩余未读的数据也会被相应处理掉。

OS通过信号杀死进程。通过kill -13杀死进程。IPC!!!

2.3.4其他情况:

管道为空&&管道正常,那么read阻塞

管道为满&&管道正常,那么write阻塞

写入的数据量不大于PIPE-BUF时,Linux保证写入的原子性

写入的数据大于PIPE-BUF时,Linux保证写入的原子性

#include <iostream>
#include <unistd.h>

// child send message to father
int main()
{
    int fd[2];

    int n = pipe(fd);
    // 匿名管道创建失败,成功返回0,失败返回非0
    if (n != 0)
    {
        std::cerr << "pipe error!\n"
                  << std::endl;
        return 1;
    }

    // 创建子进程,并且父子进程关闭对应的文件fd。
    int id = fork();
    if (id < 0)
    {
        std::cerr << "fork error!\n"
                  << std::endl;
    }

    // child  关闭读端
    if (id == 0)
    {
        ::close(fd[0]);

        int num = 0;
        while (1)
        {
            std::string s;
            std::cin >> s;
            num += ::write(fd[1], s.c_str(), s.size()-1);
            std::cout << "num:" << num << std::endl;
        }
    }

    // father  关闭写端
    else
    {
        ::close(fd[1]);

        while (1)
        {
            char buf[1024];
            int end = ::read(fd[0], buf, sizeof(buf));

            if (end > 0)
            {
                buf[end] = '\0';
            }
            else if (end == 0)
            {
                close(fd[0]);
                break;
            }
            printf("father:%s\n\n", buf);
        }
    }
    return 0;
}

 2.4多个匿名管道产生的BUG

在我们pipe创建多个管道的时候,在第一次把一个管道创建好以后,我们会进行把某个进程的读端打开,某个进程的写端关闭。然后再去创建下一个匿名管道。

这个时候我们fork出来的子进程,还会继承父进程的一系列的文件描述符,其中就包括了指向上一个匿名管道的读端或者写端。所以我们要进行一一的关闭。

创建一个管道,对应要关一次,创建两个匿名管道,就要关两次了,以此类推。


三.命名管道

3.1命名管道的介绍:

命名管道不再受血液的限制,可以让任意两个进程进行通信。命名管道是一种特殊的管道。命名管道会有一个真实的文件在指定文件夹中。

命名管道其实就是一个文件,只是这个文件被OS经过特殊的处理,只占用内核的空间,不会去进行磁盘级的操作。

它和匿名管道的区别主要体现在:

1.匿名有pipe函数创建并且打开。

2.命名管道由mkfifo创建,由open打开。

除了这些打开方式的区别以外,匿名管道和命令管道的用法基本相同

3.2如何创建管道?

3.2.1在命令行中创建一个命名管道:

在命令行中输入下面的命令:

mkfifo share

 mkfifo表示创建一个命名管道,后面的是命名管道的名称。

3.2.2通过函数创建:

头文件:

#include <sys/types.h>

 #include <sys/stat.h>

函数原型:
int mkfifo(const char *pathname, mode_t mode);

函数解读:

pathname表示的是创建命名管道的路径,可以是./fifofile这样的路径,然后就会在当前路径创建命名管道。命名管道其实就是一个文件,只是这个文件被OS经过特殊的处理,只占用内核的空间,不会去进行磁盘级的操作。

但命令管道被创建,先打开的是写,而读端还没启动,那么就会阻塞住。不然直接就是写端有,读端没有,就会被OS杀死。所以在这一点,还是进行了特殊处理的。


 四.关于Makefile文件的编写:

BIN表示的是目标程序,SRC是源文件,OBJ是经过预处理,编译和汇编以后的文件。

CC表示所用的编译器,FLAGSLDFLAGS是选项。

test是查看所有的.o和.cc。

BIN=pipe
SRC=$(wildcard *.cc)
OBJ=$(SRC:.cc=.o)

CC=g++
FLAGS=-c -Wall -std=c++11 
LDFLAGS=-o

$(BIN):$(OBJ)
	$(CC) $(LDFLAGS) $@ $^

%.o:%.cc
	$(CC) $(FLAGS) $<

.PHONY:clean
clean: 
	rm -rf $(BIN) $(OBJ)

.PHONY:test
test:
	@echo $(SRC)
	@echo $(OBJ)

文章已经接近尾声,后续会进行更新System V共享内存,可以关注一下新的博客。如果看懂文章欢迎到下面进行投票。你的支持是我前进的最大动力。

🌷🌷🌷🌷🌷🌷🌷🌷🌷🌷🌷🌷🌷🌷🌷🌷🌷🌷🌷🌷🌷🌷🌷🌷

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐