首页 > 系统相关 >Linux多进程08-进程间通信与管道

Linux多进程08-进程间通信与管道

时间:2023-05-17 18:45:40浏览次数:43  
标签:int 08 pipefd 间通信 管道 进程 include buf

进程间通信

进程是一个独立的资源分配单元,不同进程(这里所说的进程通常指的是用户进程)之间的资源是独立的,没有关联,不能在一个进程中直接访问另一个进程的资源。

不同的进程需要进行信息的交互和状态传递(如: 数据传输/通知事件/资源共享/进程控制) , 因此需要进程间通信(IPC: Inter Processes Communication)

image

管道

管道也叫无名(匿名)管道,它是是 UNIX 系统 IPC(进程间通信)的最古老形式,所有的 UNIX 系统都支持这种通信机制。

image

统计一个目录中文件的数目命令:ls | wc –l,为了执行该命令,shell 创建了两
个进程来分别执行 ls 和 wc。

管道特点

  • 管道的存储能力有限
  • 管道有文件的特质:读和写, 匿名管道没有文件实体, 有名管道有文件实体, 但不存储数据
  • 从管道读取数据的进程可以读取任意大小的数据块,而不管写入进程写入管道的数据块的大小是多少
  • 通过管道传递的数据是顺序的,从管道中读取出来的字节的顺序和它们被写入管道的顺
    序是完全一样的。
  • 传递方向是单向的, 一端写一端读, 半双工
  • 据一旦被读走,它就从管道中被抛弃,释放空间以便写更多的数据
  • 匿名管道只能在具有公共祖先的进程(父进程与子进程,或者两个兄弟进程,具有亲缘关系)之间使用。
    image

获取管道大小

/*
#include <unistd.h>

long fpathconf(int fd, int name);
*/

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    int pipefd[2];
    int ret = pipe(pipefd);

    //获取管道的大小
    long size = fpathconf(pipefd[0], _PC_PIPE_BUF);
    printf("size: %ld\n", size); //size: 4096

    return 0;
}

匿名管道

#include <unistd.h>
int pipe(int pipefd[2]);
    功能:创建一个匿名管道,用来进程间通信
    参数: int pipefd[2] 这个数组是传出参数
        pipefd[0] 对应管道读端
        pipefd[1] 对于管道写端
    返回值:
        成功 0
        失败 -1

    注意:  匿名管道只能用于具有关系的进程之间的通信
        管道默认是阻塞的,如果管道中没有数据,read阻塞,如果管道满了,write阻塞
//子进程发送数据给父进程,父进程读取数据输出
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    //在fork之前创建管道
    int pipefd[2];
    int ret = pipe(pipefd);
    if (ret == -1)
    {
        perror("pipe err");
        exit(0);
    }

    //创建子进程
    pid_t pid = fork();
    if (pid > 0)
    {
        //父进程
        printf("i am parent process, pid: %d\n", getpid());

        char buf[1024] = {0};
        while (1)
        {
            //从管道的读取端读取数据
            int len = read(pipefd[0], buf, sizeof(buf));
            printf("parent recv: %s, pid = %d\n", buf, getpid());
            //读不用暂停,如果管道中没数据,会阻塞在那里

            //向管道中不断写入数据
            char *str = "hello, i am parent";
            write(pipefd[1], str, strlen(str));
            sleep(1);
        }
    }
    else if (pid == 0)
    {
        //子进程
        printf("i am child process, pid: %d\n", getpid());
        char buf[1024] = {0};
        while (1)
        {
            //向管道中不断写入数据
            char *str = "hello, i am child";
            write(pipefd[1], str, strlen(str));
            sleep(1);

            //从管道的读取端读取数据
            int len = read(pipefd[0], buf, sizeof(buf));
            printf("child recv: %s, pid = %d\n", buf, getpid());
            bzero(buf,1024);
        }
    }

    return 0;
}

设置管道为非阻塞

/*
    设置管道非阻塞
    int flags = fcntl(fd[0], F_GETFL); //获取原来的flag
    flags != O_NONBLOCK;    //修改flag的值
    fcntl(fd[0], F_SETFL,flags);    //设置新的flag

*/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>

int main(int argc, char const *argv[])
{
    //在fork之前创建管道
    int pipefd[2];
    int ret = pipe(pipefd);
    if (ret == -1)
    {
        perror("pipe err");
        exit(0);
    }

    //创建子进程
    pid_t pid = fork();
    if (pid > 0)
    {
        //父进程
        printf("i am parent process, pid: %d\n", getpid());
        //关闭写端
        close(pipefd[1]);
        char buf[1024] = {0};

        int flags = fcntl(pipefd[0], F_GETFL); //获取原来的flag
        flags |= O_NONBLOCK;                   //修改flag的值
        fcntl(pipefd[0], F_SETFL, flags);      //设置新的flag

        while (1)
        {
            //从管道的读取端读取数据
            int len = read(pipefd[0], buf, sizeof(buf));
            printf("len : %d\n", len);
            printf("parent recv: %s, pid = %d\n", buf, getpid());
            memset(buf, 0, 1024); //清空缓冲区
            sleep(1);
        }
    }
    else if (pid == 0)
    {
        //子进程
        printf("i am child process, pid: %d\n", getpid());
        //关闭读端
        close(pipefd[0]);
        char buf[1024] = {0};
        while (1)
        {
            //向管道中不断写入数据
            char *str = "hello, i am child";
            write(pipefd[1], str, strlen(str));
            sleep(5);
        }
    }

    return 0;
}

有名管道

匿名管道,由于没有名字,只能用于亲缘关系的进程间通信。为了克服这个缺点,提出了有名管道(FIFO),也叫命名管道、FIFO文件。

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

一旦打开了 FIFO,就能在它上面使用与操作匿名管道和其他文件的系统调用一样的I/O系统调用(如read()、write()和close())。与管道一样,FIFO 也有一个写入端和读取端,并且从管道中读取数据的顺序与写入的顺序是一样的。FIFO 的名称也由此而来:先入先出。

有名管道(FIFO)和匿名管道(pipe)有一些特点是相同的,不一样的地方在于:

  1. FIFO 在文件系统中作为一个特殊文件存在,但 FIFO 中的内容却存放在内存中。
  2. 当使用 FIFO 的进程退出后,FIFO 文件将继续保存在文件系统中以便以后使用。
  3. FIFO 有名字,不相关的进程可以通过打开有名管道进行通信。
有名管道 创建fifo文件

#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);
    参数:
        - pathname: 管道名称路径
        - mode: 文件权限 和 open的mode一样 8进制数
    返回值:
        成功 0
        失败 -1
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    //判断文件是否存在
    int ret = access("fifo1", F_OK);
    if (ret == -1)
    {
        printf("管道不存在,创建管道\n");

        ret = mkfifo("fifo1", 0664);
        if (ret == -1)
        {
            perror("mkfifo");
            exit(0);
        }
    }

    return 0;
}

向有名管道中写数据:

//向管道中写数据

/*
    有名管道的注意事项:
        1.一个为只读而打开一个管道的进程会阻塞,直到另外一个进程为只写打开管道
        2.一个为只写而打开一个管道的进程会阻塞,直到另外一个进程为只读打开管道

    读管道:
        管道中有数据,read返回实际读到的字节数
        管道中无数据:
            管道写端被全部关闭,read返回0,(相当于读到文件末尾)
            写端没有全部被关闭,read阻塞等待
    
    写管道:
        管道读端被全部关闭,进行异常终止(收到一个SIGPIPE信号)
        管道读端没有全部关闭:
            管道已经满了,write会阻塞
            管道没有满,write将数据写入,并返回实际写入的字节数。
*/

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    //判断文件是否存在
    int ret = access("testfifo", F_OK);
    if (ret == -1)
    {
        printf("管道不存在,创建管道\n");

        //创建管道文件
        ret = mkfifo("testfifo", 0664);
        if (ret == -1)
        {
            perror("mkfifo");
            exit(0);
        }
    }

    //只写方式打开管道
    int fd = open("testfifo", O_WRONLY);
    if (fd == -1)
    {
        perror("open err");
        exit(0);
    }
    for (int i = 0; i < 100; i++)
    {
        char buf[1024];
        sprintf(buf, "hello, %d\n", i);
        printf("write data : %s\n", buf);
        write(fd, buf, strlen(buf));
        sleep(1);
    }
    close(fd);

    return 0;
}

读有名管道的数据

//从管道中读数据
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char const *argv[])
{

    //打开管道文件
    int fd = open("testfifo", O_RDONLY);
    if (fd == -1)
    {
        perror("open err");
        exit(0);
    }

    //读数据
    while (1)
    {
        char buf[1024] = {0};
        int len = read(fd, buf, sizeof(buf));
        if (len == 0)
        {
            printf("写端断开链接了...\n");
            break;
        }
        printf("recv buf: %s\n", buf);
    }
    close(fd);

    return 0;
}

标签:int,08,pipefd,间通信,管道,进程,include,buf
From: https://www.cnblogs.com/anqwjoe/p/17409732.html

相关文章

  • Linux多进程11-内存映射
    内存映射(Memory-mappedI/O)是将磁盘文件的数据映射到内存,用户通过修改内存就能修改磁盘文件。mmap#include<sys/mman.h>void*mmap(void*addr,size_tlength,intprot,intflags,intfd,off_toffset);功能:将一个文件或者设备的数据映射到内存中参数:......
  • Linux多进程10-有名管道实现简单版聊天功能
    chatA.c//有名管道实现简单版聊天功能#include<unistd.h>#include<stdio.h>#include<sys/types.h>#include<sys/stat.h>#include<stdlib.h>#include<fcntl.h>#include<string.h>intmain(intargc,charconst*argv[]){......
  • Linux多进程13-kill,raise,abort函数
    #include<sys/types.h>#include<signal.h>intkill(pid_tpid,intsig);-功能:给某个进程pid,发送某个信号sig-参数:-pid:>0:将信号发送给指定的进程=0:将信号发送给当前的进程组=-1:将信号发送给每一个......
  • Linux多进程12-信号
    信号概念信号是Linux进程间通信的最古老的方式之一,是事件发生时对进程的通知机制,有时也称之为软件中断,它是在软件层次上对中断机制的一种模拟,是一种异步通信的方式。信号可以导致一个正在运行的进程被另一个正在运行的异步进程中断,转而处理某一个突发事件。发往进程的诸多信......
  • Linux多进程01-进程概述
    程序与进程程序是包含一系列信息的文件,这些信息描述了如何在运行时创建一个进程进程是正在运行的程序的实例。是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。......
  • excel-08-Excel函数由浅入深-Excel函数
    Excel函数填充定义:是Excel中的内置函数,使用时,不区分大小写且每个函数都有其特定的功能和用途结构:以“=”为引导,函数名称开始,后接左括号,然后以逗号分隔输入的参数,最后是右括号。分类:共包含11类,分别是数据库函数,日期与时间函数、工程函数、财务函数、信息函数、逻辑函数、查询......
  • AdventureWorks2008R2示例数据字典
    表1:人力资源.雇员——HumanResources.Employee 键字段类型空属性引用字段说明1主键BusinessEntityIDint非空 Person.Person雇员记录主键,外键:BusinessEntity.BusinessEntityID。2惟一NationalIDNumbernvarchar(15)非空  唯一的国民识别号码,如社......
  • 华普物联HP-IOCAT-088 CAT1/4G网络IO控制器
    产品概述HP-0CAT-088CAT.1/4G网络10控制器,实现数字信号以及RS485设备数据的采集并通过CAT.1/4G网络进行远程管理控制;支持8路继电器输出、8路输入,支持50CKET连接远程服务器,支持主动上报功能;遵循华普云主动上报协议;支持两种工作模式:主机模式、从机模式,主机模式支持RS485级联......
  • 直播软件开发,Android实现根据进程名杀死特定进程
    直播软件开发,Android实现根据进程名杀死特定进程首先你实现功能的进程必须是系统进程,在AndroidManifest里面加入如下标签。 android:sharedUserId="android.uid.system"​调用ActivityManager的forceStopPackageAsUser方法 ActivityManagermActivityManager=(Activity......
  • c# 获取指定进程的命令行
    usingSystem;usingSystem.Management;classExample{staticvoidMain(string[]args){stringprocessName="javaw";//进程名称,可根据实际情况进行修改ManagementObjectSearchersearcher=newManagementObjectSearcher(&q......