第十一章 进程间通信
管道PIPE
#include<unistd.h>
int pipe(int filedes[2]);
filedes[0] 通过管道接收数据时使用的文件描述符,管道出口
filedes[1] 通过管道传输数据时使用的文件描述符,管道入口
成功返0,失败返-1
通过一个管道进行数据交互:
数据进入管道后成为无主数据,谁先调用read谁先获取数据,故将数据写入管道后要sleep一段时间再read。
sleep(3);睡眠3秒是为了显示时格式不错乱。
[root@VM_0_10_centos communication]# cat pipe2.c
#include<stdio.h>
#include<unistd.h>
#define BUF_SIZE 30
int main(int argc,char * argv[]){
int fds[2];
char str1[] = "who are you?";
char str2[] = "Thank you for your message.";
char buf[BUF_SIZE];
pid_t pid;
pipe(fds);
pid = fork();
if(pid == 0){
write(fds[1],str1,sizeof(str1));
sleep(2);//非常重要
read(fds[0],buf,BUF_SIZE);
printf("Child proc output:%s\n",buf);
}else{
read(fds[0],buf,BUF_SIZE);
printf("Parent proc output:%s\n",buf);
write(fds[1],str2,sizeof(str2));
sleep(3);
}
return 0;
}
双向通信的正确打开方式:使用两个管道:
#include<stdio.h>
#include<unistd.h>
#define BUF_SIZE 30
int main(int argc,char * argv[]){
int fds1[2],fds2[2];
char str1[] = "who are you?";
char str2[] = "Thank you for your message.";
char buf[BUF_SIZE];
pid_t pid;
pipe(fds1),pipe(fds2);
pid = fork();
if(pid == 0){
write(fds1[1],str1,sizeof(str1));
read(fds2[0],buf,BUF_SIZE);
printf("Child proc output:%s\n",buf);
}else{
read(fds1[0],buf,BUF_SIZE);
printf("Parent proc output:%s\n",buf);
write(fds2[1],str2,sizeof(str2));
sleep(3);
}
return 0;
}
更完备的回声服务器端:
能够保存客户端发来的数据到指定的文件中。累积到10条内容后就不再保存了。
将保存任务委托给另外的进程:另行创建进程,从向客户端提供服务的进程读取字符串信息。
[root@VM_0_10_centos communication]# cat echo_storeserv.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<signal.h>
#include<sys/wait.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#define BUF_SIZE 30
void error_handling(char * message);
void read_childproc(int sig);
int main(int argc,char* argv[]){
int serv_sock,clnt_sock;
struct sockaddr_in serv_adr,clnt_adr;
int fds[2];
pid_t pid;
struct sigaction act;
socklen_t adr_sz;
int str_len,state;
char buf[BUF_SIZE];
if(argc != 2){
printf("Usage:%s <port> \n",argv[0]);
exit(1);
}
act.sa_handler = read_childproc;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
state = sigaction(SIGCHLD, &act, 0);
serv_sock = socket(PF_INET,SOCK_STREAM,0);
memset(&serv_adr,0,sizeof(serv_adr));
serv_adr.sin_family = AF_INET;
serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_adr.sin_port = htons(atoi(argv[1]));
if(bind(serv_sock,(struct sockaddr*)&serv_adr, sizeof(serv_adr))==-1){
error_handling("bind() error");
}
if(listen(serv_sock,5)==-1){
error_handling("listen() error");
}
pipe(fds);
pid = fork();
if(pid == 0){
FILE * fp = fopen("echomsg.txt","wt");
char msgbuf[BUF_SIZE];
int i,len;
for(i = 0;i<10;++i){
len = read(fds[0],msgbuf,BUF_SIZE);
fwrite((void*)msgbuf, 1, len, fp);
}
fclose(fp);
return 0;
}
while(1){
adr_sz = sizeof(clnt_adr);
clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz);
if(clnt_sock == -1){
continue;
}else{
puts("new client connected...");
}
pid = fork();
if(pid == -1){
close(clnt_sock);
continue;
}
if(pid == 0){
close(serv_sock);
while((str_len = read(clnt_sock,buf,BUF_SIZE)) != 0){
write(clnt_sock, buf, str_len);
write(fds[1],buf,str_len);
}
close(clnt_sock);
puts("client disconnected...");
return 0;
}else{
close(clnt_sock);
}
}
close(serv_sock);
return 0;
}
void read_childproc(int sig){
pid_t pid;
int status;
pid = waitpid(-1,&status,WNOHANG);
printf("remove proc id: %d \n",pid);
}
void error_handling(char * message){
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
不要只图快,充满功利主义。即使开始时只想学习必要部分,最后也会需要掌握所有内容。
课后习题:
1、什么是进程间通信?分别从概念和内存的角度进行说明
概括性地说,进程间通信是指两个进程之间交换数据。但是从内存的角度看,可以理解为两个进程共有内存。因为共享的内存区域存在,可以进行数据交换
2、进程间通信需要特殊的IPC机制,这是由操作系统提供的。进程间通信时为何需要操作系统的帮助?
要想实现IPC机制,需要共享的内存,但由于两个进程之间不共享内存,因此需要操作系统的帮助,也就是说,两进程共享的内存空间必须由操作系统来提供
3、“管道”是典型的IPC技术。关于管道,请回答如下问题。
(1)管道是进程间交换数据的路径。如何创建该路径?由谁创建?
管道是由pipe函数产生的,而实际产生管道的主体是操作系统
(2)为了完成进程间通信,2个进程需同时连接管道。那2个进程如何连接到同一管道?
pipe函数通过输入参数返回管道的输入输出文件描述符。这个文件描述符在fork函数中复制到了其子进程,因此,父进程和子进程可以同时访问同一管道。
(3)管道允许进行2个进程间的双向通信。双向通信中需要注意哪些内容?
管道并不管理进程间的数据通信。因此,如果数据流入管道,任何进程都可以读取数据。因此,要合理安排共享空间的输入和读取
4、编写示例复习IPC技术,使2个进程相互交换3次字符串…
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#define BUF_SIZE 30
int main(){
pid_t pid;
int i;
int fds1[2],fds2[2];
char* s1 = "hello1";
char* s2 = "hello2";
char* s3 = "hello3";
char* strs[] = {s1,s2,s3};
char buf[BUF_SIZE];
pipe(fds1),pipe(fds2);
pid = fork();
if(pid == 0){
for(i=0;i<3;++i){
write(fds1[1],strs[i],strlen(strs[i])+1);
read(fds2[0],buf,BUF_SIZE);
printf("child proc:%s\n",buf);
}
}else{
for(i=0;i<3;++i){
read(fds1[0],buf,BUF_SIZE);
printf("parent proc:%s\n",buf);
write(fds2[1],strs[i],strlen(strs[i])+1);
sleep(1);//是为了避免最后一条输出打印在了[root@VM_0_10_centos communication]# 后面
}
}
}