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

    进程简单来讲就是一个程序的一次执行,这里说的进程一般都指的是运行在用户态的进程,而处于用户态的不同进程之间是彼此相互隔离的,它们必须通过某种方式来进行通信,具体理由如下:

    (1)数据传输:有时候一个进程需要将它的数据发送给另一个进程。 

    (2)资源共享:有时候多个进程之间需要共享同样的资源。

    (3)通知事件:有时候一个进程需要向另一个或一组进程发送消息,通知它们发生了某个事件。

    (4)进程控制:有些进程希望能够完全控制另一个进程的执行,此时控制进程希望能够拦截另一进程的所有操作,并能够及时知道它的状态改变。 


    1.进程间通信的方式:

    进程间通信的方式总共有6种:

    (1)管道通信(包括有名管道和无名管道)

    (2)信号通信 

    (3)消息队列通信

    (4)共享内存通信 

    (5)信号量通信 

    (6)套接字(socket)通信 


    2.管道通信:

    提到管道这个词,个人认为你可以把其想象成一根运输数据的管子,它把一个进程的输出和另一个进程的输入联系在一起,一个进程从管道的尾部写入数据,另一个进程从管道的头部读取数据,管道中的数据被进程读出来以后将被从管道中删除,其他的进程将不能再次读到这些数据。

    管道提供了简单的流控制机制,进程试图读取没有数据的空管道时将会被阻塞,同样如果管道中数据的容量已满时,如果这时候有一个进程试图向这个管道写数据时,那么同样也会被阻塞。 

    管道通信有两种方式:一是无名管道通信,二是有名管道通信,无名管道只能用于父进程和子进程之间的通信,而有名管道能用于同一系统中任意两个进程之间的通信。

    

    2.1 无名管道的创建:

    创建无名管道可以使用系统调用pipe,其原型为:

    int pipe(int filedis[2])

    filedis[2]:管道的文件描述符,我们可以把管道的头部和尾部各看作是一个文件,其中向管道尾部写数据所对应的文件描述符为filedis[1],向管道头部读取数据所对应的文件描述符为filedis[0]。

    无名管道创建成功时返回0,否则返回-1 。

    使用pipe系统调用创建无名管道时需要包含的头文件为:

    #include <unistd.h> 


    2.2 无名管道的关闭:

    关闭一个无名管道只需要使用close系统调用将filedis[0]和filedis[1]这两个文件描述符关闭即可。


    2.3 无名管道的读写:

    无名管道只能用于父进程和子进程之间进行通信,因此对无名管道进行读写时,通常会先通过pipe创建一个无名管道,然后再使用fork创建一个子进程,该子进程会继承父进程所创建的管道。

    注:必须在fork之前就调用pipe函数创建一个无名管道,否则就会出现子进程和父进程各会创建一个管道,父子两个进程都没有共用同一根管道,在这种情况下是无法通信的。

    无名管道的读写模型

技术分享

    

    2.4 无名管道编程:

/*在父进程中创建一无名管道,并创建子进程来读该管道,父进程来写该管道*/
#include <unistd.h>
#include <sys/types.h>
#include <errno.h> 
#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 

int main(int argc,char*argv[])
{
	/*存储创建无名管道后返回的两个文件描述符
	pipe_fd[0]用于读管道,pipe_fd[1]用于写管道*/
	int pipe_fd[2]; 
	pid_t pid;
	char buffer_r[100]; //缓冲区 
	int real_read_num; 
	memset(buffer_r,0,sizeof(buffer_r));//把缓冲区清0 
	
	if(pipe(pipe_fd)<0) //创建无名管道
	{
		printf("create pipe failure!\n");
		exit(1);
	}
	else
	{
		printf("create pipe sucess!\n");
	}
	
	pid=fork(); 
	
	if(pid==-1)
	{
		printf("create fork process error!");
	}
	else if(pid==0)//子进程 
	{
		//printf("This is the child process!\n");
		close(pipe_fd[1]); //关闭管道写端 
		sleep(2); //子进程睡眠2秒,先让父进程执行往管道里面写入数据 
		if((real_read_num=read(pipe_fd[0],buffer_r,100))>0) //读取管道里面的数据 
		{
			printf("%d numbers read from the pipe is %s\n",real_read_num,buffer_r);
		}
		close(pipe_fd[0]);//子进程读取管道结束,关闭管道读端  
		exit(0);
	}
	else //父进程     
	{
		//printf("This is the father process!\n");
		close(pipe_fd[0]);//关闭管道读端 
		if(write(pipe_fd[1],"Hello ",6)!=-1)//往管道里面写入数据 
		{
			printf("father process write1 Hello\n");
		}
		if(write(pipe_fd[1],"Pipe!",5)!=-1)
		{
			printf("father process write2 Pipe!\n");
		}
		close(pipe_fd[1]);
		//sleep(10);//此时子进程开始执行,去管道里面去读取数据   
		waitpid(pid,NULL,0);//等待子进程结束   
		exit(0);
	}
	return 0;
}


    2.5 有名管道的创建:

    创建有名管道可以使用系统调用mkfifo,其原型为:

    int mkfifo(const char*pathname,mode_t mode) 

    pathname:创建的有名管道的名字,可以包含路径,缺省情况下为当前路径(有名管道实质上就是一个文件)

    mode_t:创建的文件的属性(有名管道实质上就是一个文件)

    创建有名管道成功时,函数返回0,否则返回-1 。

    使用mkfifo系统调用来创建一个有名管道应该包含头文件:

    #include <sys/types.h> 

    #include <sys/stat.h> 


    2.6 有名管道的关闭:

    要关闭有名管道,同样是使用系统调用close关闭有名管道所对应的文件描述符即可。

    

    2.7 有名管道编程:

    fifo_write.c :创建一个有名管道并往里面写入一些数据   

#include <sys/types.h>
#include <sys/stat.h> 
#include <errno.h> 
#include <fcntl.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h>
#define FIFO_SERVER "./myfifo" //创建的有名管道保存在当前目录下   

int main(int argc,char*argv[])
{
	int fifo_fd; 
	char buffer[100];//缓冲区  
	/*创建有名管道*/
	 if((mkfifo(FIFO_SERVER,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST))
	 {
		 printf("can‘t create fifo_server!\n");
		 exit(1);
	 }
	 /*打开有名管道,有名管道实质上就是一个文件*/
	 fifo_fd=open(FIFO_SERVER,O_RDWR|O_NONBLOCK);
	 if(fifo_fd==-1)
	 {
		 printf("Open fifo_server error!\n");
		 exit(1);
	 }
	 /*判断运行程序时是否有参数传入*/
	 if(argc==1) 
	 {
		 printf("Please send something!\n");
		 exit(1);
	 }
	 strcpy(buffer,argv[1]); //把传入的参数复制到缓冲区 
	 /*将缓冲区里的数据写进有名管道*/
	 if(write(fifo_fd,buffer,100)==-1)
	 {
		 printf("write fifo_server error!maybe the fifo_server is full!\n");
		 exit(1);
	 }
	 else
	 {
		 printf("write %s to the fifo_server!\n",argv[1]);
	 }
	 close(fifo_fd); //关闭有名管道   
	return 0;
}

    fifo_read.c :从fifo_write.c文件创建的有名管道中读取数据 

#include <sys/types.h> 
#include <sys/stat.h> 
#include <errno.h> 
#include <fcntl.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#define FIFO 	"./myfifo" 

int main(int argc,char*argv[])
{
	char buffer[100];
	int fifo_fd;
	int real_read_num;
	
	fifo_fd=open(FIFO,O_RDONLY|O_NONBLOCK);//打开有名管道 
	if(fifo_fd==-1)
	{
		printf("Open fifo error!\n"); 
		exit(1);
	} 
	/*设计一个死循环不断从有名管道里读取数据*/
	while(1)
	{
		memset(buffer,0,100); //将缓冲区清0    
		if((real_read_num=read(fifo_fd,buffer,100))==-1)
		{
			printf("read from fifo_server error! the fifo_server has no data!\n");
			exit(1);
		}
		else 
		{
			printf("read %s from fifo_server!\n",buffer);
			sleep(1);
		}
	}
	close(fifo_fd);
	pause();
	unlink(FIFO);
	return 0;
}

    

    暂时写到这里,待续。 

    











    

本文出自 “止不住的思考” 博客,请务必保留此出处http://9110091.blog.51cto.com/9100091/1605410

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。