Linux C与C++一线开发实践之四 Linux进程间的通信

Posted 夜色魅影

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux C与C++一线开发实践之四 Linux进程间的通信相关的知识,希望对你有一定的参考价值。

Linux中的进程为了能在同一项任务上协调工作,它们彼此之间必须能够进行通信。下面主要介绍Linux常用的3种通信方式:信号、管道和消息队列。 效果差别不大,熟练一种基本可以应对一般的一线开发场景了。(这里讲的进程间通信是指同一台物理机上多个进程间通信,跨机器一般是socket通信)

信号

信号可以说是最早引入UNIX系统中的进程间通信方式之一,Linux同样支持这种通信方式。信号是很短的信息,可以被发送到一个进程或者一组进程,一般就是标志信号的一个数表示。它可以从键盘中断产生,进程在虚拟内存的非法访问等错误环境下也会产生,也可以被shell程序用来向其子进程发送任务控制命令等。可以简单理解为系统预设的一系列特定事件触发的监听处理方式。
这些信号可以用kill -l查看:

上面列表中,编号1~31的信号为传统UNIX支持的信号,是不可靠信号(非实时的),编号32~64的信号是后面扩展的,称可靠信号(实时信号)。不可靠信号与可靠信号的区别在于前者不支持排队,可能会造成信号丢失,而后者不会。每个信号含义自行wiki。
进程接收到某信号可以采取下面3中行为之一:

  1. 忽略信号(SIGKILL和SIGSTOP这两个信号不能被忽略)
  2. 执行与这个信号相关的默认操作。
  3. 调用自定义的信号处理函数
    由此可见信号作为进程间通信只能处理一些预定义的系统特定事件,且无法传递数据。实际项目一般用于处理程序异常或特定shell命令操作。

管道

管道,用于连接读进程和写进程,以实现它们之间通信的共享文件。 它能传送大量数据,数据先进先出,而且一般是单向的。写进程将数据写入管道,读进程从管道读取数据,腾出空间以便写进程写入新的数据,所有数据只能读取一次。Linux管道是一个固定大小的缓冲区,一般为一个页面,即4kb。
所以管道是读写成对的,必须要同步,如果一个进程试图写入一个已满的管道,在默认情况,系统会自动堵塞该进程,直到管道能够有空间接收数据。同样,如果试图读一个空管道,进程也会阻塞,直到管道有可读的数据。此外,如果一个进程以读方式打开一个管道,而没有另外的进程以写方式打开该管道,则同样会造成该进程阻塞。有写没有读同理。所以管道使用读写必须是成对的。 而且只能在有公共祖先的进程间使用管道。
在Shell下,可以通过符合 “|” 来使用管道。例如:ls -l | grep cxx,ls是列出当前文件夹下所有目录和文件,现在把本来要输出到屏幕上的数据通过管道输出到grep进程中,作为grep的输入,然后grep进程对输入信息进行筛选,把存在cxx字符串的那行打印在屏幕上。
下面我们来看一个父子进程使用管道的通信示例:

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

void sys_err(const char *str)

	perror(str);
	exit(1);


int main(void)

	pid_t pid;
	char buf[1024];
	int fd[2];
	char *p = "test for pipe\\n";
	// 创建管道, 同时创建了读写描述符
	if(pipe(fd) == -1) 
		sys_err("pipe");
	
	// 创建子进程
	pid = fork();
	if(pid < 0) 
		sys_err("fork err");
	 else if(pid == 0) 
		close(fd[1]); //子进程关闭写描述符
		printf("child process wait to read:\\n");
		int len = read(fd[0], buf, sizeof(buf));
		write(STDOUT_FILENO, buf, len);
		close(fd[0]);
	 else 
		close(fd[0]); // 父进程关闭读描述符
		write(fd[1], p, strlen(p));
		wait(NULL);  //等待其子进程中断或结束,不然一直阻塞自己
		close(fd[1]);
	
	return 0;

消息队列

从许多方面来看,消息队列类似于有名管道,但是没有像管道那样那么多限制与复杂的关联。消息队列提供了一种在两个不相关的进程之间传递数据的简单高效的方法。独立于发送与接收进程,消息队列也是一块数据块,每一个数据块可以看作有一个类型,而接收进程可以独立接收具有不同类型的数据块。完全不用考虑像管道那种必须读写的同步问题。这应该是一种比管道更好的解决方案。
消息队列的发送和接收示例:

// 接收程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct my_msg_st

    long int my_msg_type;
    char some_text[BUFSIZ];
;
int main()

    int running = 1;
    int msgid;
    struct my_msg_st some_data;
    long int msg_to_receive = 0;

	//设置消息队列:
    msgid = msgget((key_t)1234,0666|IPC_CREAT);
    if(msgid == -1)
    
        fprintf(stderr,"msgget failed with error: %d\\n", errno);
        exit(EXIT_FAILURE);
    
    
  	//接收消息队列中的消息直到遇到一个end消息。最后,消息队列被删除:
    while(running)
    
        if(msgrcv(msgid, (void *)&some_data, BUFSIZ, msg_to_receive, 0) == -1)
        
            fprintf(stderr, "msgrcv failed with errno: %d\\n", errno);
            exit(EXIT_FAILURE);
        

        printf("You wrote: %s", some_data.some_text);
        if(strncmp(some_data.some_text, "end", 3)==0)
        
            running = 0;
        
    
 
    if(msgctl(msgid, IPC_RMID, 0)==-1)
    
        fprintf(stderr, "msgctl(IPC_RMID) failed\\n");
        exit(EXIT_FAILURE);
    
    exit(EXIT_SUCCESS);


// 发送程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MAX_TEXT 512
struct my_msg_st

    long int my_msg_type;
    char some_text[MAX_TEXT];
;

int main()

    int running = 1;
    struct my_msg_st some_data;
    int msgid;
    char buffer[BUFSIZ];
    msgid = msgget((key_t)1234, 0666|IPC_CREAT);
    if(msgid==-1)
    
        fprintf(stderr,"msgget failed with errno: %d\\n", errno);
        exit(EXIT_FAILURE);
    
     while(running)
    
        printf("Enter some text: ");
        fgets(buffer, BUFSIZ, stdin);
        some_data.my_msg_type = 1;
        strcpy(some_data.some_text, buffer);
        if(msgsnd(msgid, (void *)&some_data, MAX_TEXT, 0)==-1)
        
            fprintf(stderr, "msgsnd failed\\n");
            exit(EXIT_FAILURE);
        
        if(strncmp(buffer, "end", 3) == 0)
        
            running = 0;
        
    
     exit(EXIT_SUCCESS);



以上是关于Linux C与C++一线开发实践之四 Linux进程间的通信的主要内容,如果未能解决你的问题,请参考以下文章

Linux C与C++一线开发实践之一 Linux概述与Linux C++开发

Linux C与C++一线开发实践之二 Linux文件系统

Linux C与C++一线开发实践之三 Linux多进程

Linux C与C++一线开发实践之三 Linux多进程

Linux C与C++一线开发实践之六 多线程高级编程

Linux C与C++一线开发实践之六 多线程高级编程