Linux系统编程之进程间通信



今天我们接着谈Linux系统编程中的进程间的通信,上一节我们讨论了进程的基本操作。这一节我们来讨论一下进程间的通信。
        常见的进程间的通信方式有:无名管道、命名管道、信号、共享内存、消息队列、信号量、套接字。



接下来我们先来谈:
                一、无名管道:
                      1、管道是UNIX系统的IPC的最古老方式,并且多数unix系统都提供此种通信方式。、
                      2、管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
                      3、虽然管道是半双工的,但是我们可以通过创建两个无名管道实现父子进程间的相互通信。我们来看下图常用的编程模型:

4、函数原型:

       #include <unistd.h>

       int pipe(int pipefd[2]);

5、上面就是无名管道的一些基本语法,当使用无名管道通信的时候,在父进程创建子进程之前,我们创建的数据都是共享的,所以,通过在fork之前创建管道就可以实现父子进程之间的通信,这里给出一个测试用例,帮助理解如何使用管道:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>

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

#define BUFSIZE 1024

int main()
{
    pid_t pid = -1;
    char str[BUFSIZE] = {0};
    
    //先实现单双工,父写子读
    int fd[1];
    int res = -1;

    res = pipe(fd);

    if(-1 == res)
    {
        perror("pipe");
        exit(1);
    }

    pid = fork();

    if(pid > 0)
    {
        //父进程,父写子读,就需要在父进程关闭读端,0读1写
        printf("I am Parent\n");
        memset(str,0,BUFSIZE);
        int status = 0;

        //关闭读端
        close(fd[0]);
        strcpy(str,"Hello World Hello Linux\n");
        //向管道写数据
       res =  write(fd[1],str,strlen(str));

       if(-1 == res)
       {
           perror("write");
           exit(1);
       }

       res = wait(status);

       if(-1 == res)
       {
           perror("wait ");
           exit(1);
       }
    }
    else if(0 == pid)
    {
        //子进程,父写子读,在子进程里关闭写端
        printf("I am child\n");

        close(fd[1]);
        memset(str,0,BUFSIZE);

        res = read(fd[0],str,BUFSIZE);
        if(-1 == res )
        {
            perror("resd ");
            exit(1);
        }
        res = write(STDOUT_FILENO,str,strlen(str));
        if(-1 == res)
        {
            perror("write");
            exit(1);
        }
    }

    return 0;
}

上边给出了半双工的无名管道的通信方式,所谓全双工的测试代码,就是在创建进程之前创建两个管道,在父子进程中分别关掉一个读写端,这样就可以实现父子进程间通信。虽然这样实现了父子进程间的通信,我们也能看到一个问题,有名管道只能用到有亲缘关系的父子进程间通信。这样是很不方便的,下来我们来看有名管道:



二、 FIFO(有名管道)

a、基础概念:

FIFO有时被命名为命名管道。管道只能由相关进程使用,这些相关进程的共同祖先进程创建了管道。通过FIFO不相关的进程也能交换数据。
                    FIFO是一种文件类型。stat结构成员st_mode的编码指明文件是否是FIFO类型,可以用S_ISFIFO宏对其进行测试。
                    b、函数原型:
                        创建FIFO类似于创建文件。
                        #include <sys/types.h>
                        #include <sys/stat.h>
                        int mkfifo(const char *pathname, mode_t mode);
                   一旦已经用了mkfifo创建了一个FIFO,就可用open打开它。其实,一般的文件I/O函数(close、read、write、unlink等)都可以用。 
                    c、FIFO的两种用途:
                        1、FIFO由shell命令使用以便将数据从一条管线传送到另一条,为此无需创建中间临时文件。
                        2、FIFO用于客户进程-服务器进程应用程序中,一再客户进程和服务器进程之间传递数据
                    d、 实例:
                        1、FIFO复制输出流:
                            FIFO可以复制串行管道命令之间的输出流,于是也就不需要写数据到中间磁盘文件中。管道只能用于进程间的线性连接,然而FIFO可以用来非线性连接。
                        2、客户-服务器进程
                    FIFO的另一个应用就是在客户进程和服务器进程之间传输数据。如果有一个服务器进程,它与多个客户进程都可以将其请求写到一个该服务器进程创建的众所周知的FIFO中,这中类型的客户进程-服务器进程通信中使用FIFO的问题是:服务器如何将回答送回各个客户进程。
                不能使用单个FIFO,因为该服务器进程发出对各个客户进程的请求响应,而请求者却不能知道什么时候去读才能恰如其分的读到对它的相应。一种解决方法是每个客户进程都在其请求中包含它的进程ID。然后服务器进程为每个进程创建一个FIFO,所使用的路径名是以客户进程的进程ID为基础的。

3、下来我们来看具体代码:

我们用mkfifo实现进程间简单通信,这里存在一个进程间同步问题,通俗来讲就是当一个进程写入数据后,另一个进程没有读取之前都被自己读取了,进程间的同步我们到后边总结,这里先简单实现两个进程间的通信。

/**********************************************************
*    > File Name: mkfifo-ser-1.c
*    > Author:xiao_k
*    > Mail:[email protected] 
*    > Created Time: Wed 21 Feb 2018 09:55:21 PM CST
**********************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>

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

#define BUFSIZE 1024

int main()
{
    char buf[BUFSIZE];
    int res = -1,fd = -1;

   res =  mkfifo("./testfifo",0777);

   if(-1 == res)
   {
       perror("mkfifo ");
       exit(1);
   }

   fd = open("./testfifo",O_RDWR|O_EXCL);
   if(-1 == fd)
   {
       perror("open");
   }

   while(1)
   {
       printf("ser-> ");
       memset(buf,0,BUFSIZE);
       scanf("%s",buf);
       if(0 == strcmp(buf,"quit"))
           break;
       write(fd,buf,strlen(buf));
       sleep(2);
       read(fd,buf,BUFSIZE);
            printf("cli-> %s\n",buf);
   }

   if(open("./testfifo",O_EXCL) > 0 )
   {
        if(-1 == (res = unlink("./testfifo")))
        {
            perror("unlink");
            exit(1);
        }
    }
   else
   {
       perror("open ");
       exit(1);
   }

    return 0;
}

/**********************************************************
 *    > File Name: mkfifo-ser-1.c
 *    > Author:xiao_k
 *    > Mail:[email protected] 
 *    > Created Time: Wed 21 Feb 2018 09:55:21 PM CST
 **********************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>

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

#define BUFSIZE 1024

int main()
{
    char buf[BUFSIZE];
    int fd = -1,res= -1;

    fd = open("./testfifo",O_RDWR|O_EXCL);

    if(-1 == fd)
    {
        perror("open");
    }

    while(1)
    {
        memset(buf,0,BUFSIZE);
        read(fd,buf,BUFSIZE);
        printf("ser-> %s\n",buf);
        memset(buf,0,BUFSIZE);
        printf("cli->");
        scanf("%s",buf);
        if(0 == strcmp(buf,"quit"))
            break;
        write(fd,buf,strlen(buf));
        sleep(1);

    }

    if(open("./testfifo",O_EXCL) > 0 )
    {
        if(-1 == (res = unlink("./testfifo")))
        {
            perror("unlink");
            exit(1);
        }
    }
    else
    {
        perror("open ");
        exit(1);
    }
    return 0;
}

上边就是Linux通信的最基本的两种方式都只是做了最基本的举例,后几种我将分成专题分别总结,总结完了所有专题后,将综合运用。

原文地址:http://blog.51cto.com/967243153/2072038

时间: 2024-11-09 16:48:06

Linux系统编程之进程间通信的相关文章

linux系统编程:进程间通信-mmap

进程间通信-mmap #include <sys/mman.h> void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); int munmap(void *addr, size_t length); mmap本质是把内存与硬盘上的文件同步.某块内存中的内容会同步到硬盘文件上,即把文件映射到内存.故通过对同一文件的读写达到进程间的通信. 参数解释: addr:指定对哪儿块内存实行映射.NU

linux系统编程:进程间通信-fifo

进程间通信-fifo 进程间通信的还有一种方式是fifo. fifo是还有一种管道:有名管道.从名字能够看出.它也是队列. 使用fifo通信前,得先创建fifo $ mkfifo myfifo 随后仅仅需对myfifo像文件一样使用即可. fifo_w.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/sta

Linux系统编程之进程间通信之浅谈信号

我们接着谈Linux学习过程中一个重要的话题--信号. 一.信号的概念:       信号是一种软件中断,它提供了一种处理异步事件的方法,也是进程间唯一的异步通信方式.二.信号的来源:       1.硬件方式:             当用户按下终端上某些键时,将产生信号.             硬件异常产生信号:除0操作.访问非法空间--       2.软件方式             用户在终端下调用kill命令向进程发送任意信号        进程调用kill或者sigqueue函数

Linux系统编程@进程通信(一)

进程间通信概述 需要进程通信的原因: 数据传输 资源共享 通知事件 进程控制 Linux进程间通信(IPC)发展由来 Unix进程间通信 基于System V进程间通信(System V:UNIX系统的一个分支) POSIX进程间通信(POSIX:可移植操作系统接口,为了提高UNIX环境下应用程序的可移植性.很多其他系统也支持POSIX标准(如:DEC OpenVMS和Windows).) 现在Linux使用的进程间通信方式包括: 管道(pipe).有名管道(FIFO) 信号(signal) 消

Linux系统编程札记:进程通信(一) &nbsp; &nbsp;

进程简单来讲就是一个程序的一次执行,这里说的进程一般都指的是运行在用户态的进程,而处于用户态的不同进程之间是彼此相互隔离的,它们必须通过某种方式来进行通信,具体理由如下: (1)数据传输:有时候一个进程需要将它的数据发送给另一个进程. (2)资源共享:有时候多个进程之间需要共享同样的资源. (3)通知事件:有时候一个进程需要向另一个或一组进程发送消息,通知它们发生了某个事件. (4)进程控制:有些进程希望能够完全控制另一个进程的执行,此时控制进程希望能够拦截另一进程的所有操作,并能够及时知道它的

LINUX系统编程 由REDIS的持久化机制联想到的子进程退出的相关问题

19:22:01 2014-08-27 引言: 以前对wait waitpid 以及exit这几个函数只是大致上了解,但是看REDIS的AOF和RDB 2种持久化时 均要处理子进程运行完成退出和父进程需要做的什么事情,所以特定看了UNIX环境编程和LINUX系统编程这2本书 重新梳理下整个要点. 内容: 一般而言: 如果程序类似于下面的情况: if((pid=fork())==0) { dochildtthing(); exit(0); } else if(pid>0) { dofathertt

Linux系统编程-setitimer函数

功能:linux系统编程中,setitimer是一个经常被使用的函数,可用来实现延时和定时的功能. 头文件:sys/time.h 函数原型: int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value); 参数含义: 1.which参数用来设置定时器类型,可选的值为 (1)ITIMER_REAL : 设置定时器以系统真实所花费的时间来计时,运行指定时间后发送SIGALRM信号. (

Linux系统编程笔记

写在开篇:出于对未来职业规划的考虑(其实还是一团糟),制定了一个基本的学习方向,那就是从系统编程学习API慢慢的深入内核,这是一个比较成熟的学习路线.所以从本篇开始,在这段时间会陆续记录Linux系统编程的学习笔记,除了供学习之余复习只用,同时也期望能记录初入职场摸爬滚打的第一个3年. 第一章 文件I/O 文件访问的基本调用一般是 read()和write(),但是在访问文件之前,要做的是一项很重要的工作就是:打开,没错!通过调用 open()或create()实现 #include <sys/

linux系统编程之管道(一):匿名管道(pipe)

原文地址:http://www.cnblogs.com/mickole/p/3192210.html 一,什么是管道 管道是Linux支持的最初Unix IPC形式之一,具有以下特点: 管道是半双工的,数据只能向一个方向流动:需要双方通信时,需要建立起两个管道: 只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程): 单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中. 数据的读