zoukankan      html  css  js  c++  java
  • 进程间通信及管道

    Linux 下进程间通信

    处于用户态的不同进程之间是彼此隔离。

    Linux下的进程通信方式:

    • 管道及有名管道

    管道可用于具有亲缘关系进程间的通信,
    有名管道,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。

    • 信号

    信号是在软件层次上对中断机制的一种模拟。

    • 消息队列

    消息队列是消息的链接表。具有写权限的进程可以向消息队列中按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读取消息。

    • 共享内存

    它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据的更新。这种通信方式需要依靠某种同步机制,如互斥锁和信号量等。

    • 信号量

    主要作为进程间以及同一进程不同线程之间的同步手段。

    • 套接字(socket)

    更为一般的进程间通信机制,它可用于不同机器之间的
    进程间通信,应用非常广泛。

    管道

    管道是基于文件描述符的通信方式,当一个管道建立时,它会创建两个文件描述符fds[0]和fds[1],其中fds[0]固定用于读管道,而fd[1]固定用于写管道。

    这样就构成了一个半双工的通道。

    关闭管道时,只要close两个文件描述符。

    • 创建管道

    SYNOPSIS

    #include <unistd.h>
    
    int pipe(int pipefd[2]);
    
    • 读写管道

    fork的子进程也继承了父进程的管道。父子进程分别拥有自己的读写的通道,为了实现父子进程之间的读写,只需把无关的读
    端或写端的文件描述符关闭即可。

    实例:

    #include <unistd.h>
    #include <sys/types.h>
    #include <errno.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
    	int pipe_fd[2];
    	pid_t pid;
    	char buf_r[100] = {0};
    	char* p_wbuf;
    	int r_num;
    
    	if(pipe(pipe_fd) < 0){
    		printf("pipe create error
    ");
    		return -1;
    	}
    
    	if((pid = fork()) == 0){
    		printf("
    ");
    		close(pipe_fd[1]);
    		/* make sure parent process write over */
    		sleep(2);
    
    		if((r_num = read(pipe_fd[0],buf_r,100)) > 0){
    			printf("%d numbers read from the pipe is %s
    ",r_num,buf_r);
    		}
    
    		close(pipe_fd[0]);
    		exit(0);
    	}
    	else if(pid>0){
    		close(pipe_fd[0]);
    		if(write(pipe_fd[1],"Hello",5) != -1)
    			printf("parent write1 success!
    ");
    		if(write(pipe_fd[1]," Pipe",5) != -1)
    			printf("parent write2 success!
    ");
    
    		close(pipe_fd[1]);
    		sleep(3);
    
    		waitpid(pid,NULL,0);
    		exit(0);
    	}
    }
    

    结果输出:

    xxx@xxx-pc:~/Documents$ ./a.out 
    parent write1 success!
    parent write2 success!
    
    10 numbers read from the pipe is Hello Pipe
    

    标准流管道

    标准流管道是基于文件流的管道,由popen函数创建。完成了以下事:

    • 创建一个管道
    • fork一个子进程
    • 在父子进程中关闭不必要的文件描述符
    • 执行exec函数族调用
    • 执行函数中指定的命令

    用popen创建的管道必须使用标准I/O函数进行操作,
    但不能使用前面的read、write一类不带缓冲的I/O 函数。

    SYNOPSIS

    #include <stdio.h>
    
    /* command: 指向null结尾的字符串,被送到/bin/sh以-c参数执行
     * type: "r",文件指针连接到command的标准输出;"w",文件指针连接到command的标准输入
     * return: 文件指针
     */
    FILE *popen(const char *command, const char *type);
    
    int pclose(FILE *stream);
    

    实例:

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <fcntl.h>
    
    #define BUFSIZE 1000
    
    int main()
    {
    	FILE *fp;
    	char *cmd = "ps -ef";
    	char buf[BUFSIZE] = {0};
    
    	if((fp = popen(cmd,"r")) == NULL)
    		perror("popen");
    	while((fgets(buf,BUFSIZE,fp)) != NULL)
    		printf("%s",buf);
    	pclose(fp);
    	exit(0);
    }
    

    FIFO

    有名管道它可以使互不相关的两个进程实现彼此通信。

    该管道可以通过路径名来指出,并且在文件系统中是可见的。在建立了管道之后,两个进程就可以把它当作普通文件一样进行读写操作,使用非常方便。

    在创建管道成功之后,就可以使用open、read、write这些函数了。

    在管道的读写中有阻塞的可能,open以O_NONBLOCK打开管道可以设置为非阻塞模式。

    SYNOPSIS

    #include <sys/types.h>
    #include <sys/stat.h>
    
    int mkfifo(const char *pathname, mode_t mode);
    

    实例:

    读进程中创建FIFO

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    #define FIFO "/tmp/myfifo"
    
    int main()
    {
    	int fd;
    	char buf[256] = {0};
    
    	unlink(FIFO);
    	if(mkfifo(FIFO, 0666) < 0){
    		printf("cannot create fifo
    ");
    	}
    
    	fd = open(FIFO, O_RDONLY | O_NONBLOCK, 0);
    	if(fd == -1){
    		perror("open");
    		exit(1);
    	}
    
    	while(1){
    		memset(buf, 0, sizeof(buf));
    		if(read(fd, buf, sizeof(buf)) == -1){
    			if(errno == EAGAIN){
    				printf("no data yet
    ");
    			}
    		}
    		printf("read %s from FIFO
    ", buf);
    		sleep(1);
    	}
    	return 0;
    }
    

    另外一个进程,写进程写FIFO

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    #define FIFO "/tmp/myfifo"
    
    int main(int argc, char *argv[])
    {
    	int fd;
    	char buf[256] = {0};
    
    	fd = open(FIFO, O_WRONLY | O_NONBLOCK, 0);
    	if(fd == -1){
    		perror("open");
    		if(errno == ENXIO){
    			printf("open error; no reading process
    ");
    		}
    		exit(1);
    	}
    
    	if(argc != 2){
    		return -1;
    	}
    	memcpy(buf, argv[1], strlen(argv[1]));
    
    	if(write(fd, buf, sizeof(buf)) == -1){
    		/* nonblock mod call the block operation */
    		if(errno == EAGAIN){
    			printf("The FIFO has not been read yet. Please try later
    ");
    		}
    	}else{
    		printf("write %s to FIFO
    ", buf);
    	}
    	return 0;
    }
    

    结果如下:

    先启动读进程,后启动写进程。

    FIFO文件
    xxx@xxx-pc:~/Documents$ ls -l /tmp/myfifo 
    prw-rw-r-- 1 xxx xxx 0 Apr 28 01:37 /tmp/myfifo
    
    读进程
    xxx@xxx-pc:~/Documents$ ./read
    read  from FIFO
    read my-fifo from FIFO
    
    写进程
    xxx@xxx-pc:~/Documents$ ./write my-fifo
    write my-fifo to FIFO
    

    如果先启动写进程,open FIFO管道时会报错。

    xxx@xxx-pc:~/Documents$ ./write my-world2
    open: No such device or address
    open error; no reading process
  • 相关阅读:
    cmd命令操作Mysql数据库
    可编程作息时间控制器设计
    博客第一天
    PRML 1: Gaussian Distribution
    KMP String Matching Algorithm
    Reinstall Ubuntu 14.04
    Computability 4: Decidability and R.E. Sets (I)
    Consumer-Producer Problem
    Compile the Linux Kernel
    Introduction to Linux Kernel
  • 原文地址:https://www.cnblogs.com/fuluwwa/p/6773308.html
Copyright © 2011-2022 走看看