操作系统实验——进程通信

目录

1.匿名管道

2.命名管道

3.软中断


1.匿名管道

匿名管道没有与缓冲区绑定的名字,主要用于有亲缘关系的进程间通信,

通过调用pipe函数创建管道

#include <unistd.h>

int pipe(int filedes[2]);

pipe函数在内核开辟一块用于通信的缓冲区(缓冲区没有名字,故称匿名管道)

管道有一个读端一个写端,通过参数传出给两个文件描述符,分别对应管道的读写端:filedes[0]指向管道的读端;filedes[1]指向管道的写端

用户程序通过read(filedes[0]);或者write(filedes[1]),向该文件读写数据其实就是读写内核缓冲区。

pipe函数调用成功返回0,调用失败返回-1。

例5-20:匿名管道的使用

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

int main(){
	int pipe_fd[2];    
	if(pipe(pipe_fd)<0){        
		printf("pipe create error\n");
		return -1;      
	}//if 
	printf("pipe create success\n");
	if (fork()>0)  {        //father
		int r;
		char buf[15+1]; 
		printf("***Father want to read from son\n"); 
		r=read(pipe_fd[0],buf,15);    //从管道读端读取数据到buf
		buf[r]=0;
		printf("***Father got strings: %s\n",buf);       
	}
	else  {        //son
		const char *test="a test string from son ";
		printf("Son sleep:\n");
		sleep(5);//父亲要读,必须等儿子写完
		printf("Son write after sleep: %s\n",test);
		write(pipe_fd[1],test,strlen(test));    //从管道写端写入test      
	}
	close(pipe_fd[0]);
	close(pipe_fd[1]);        
}//main

由于儿子里的sleep(5),父亲要读管道里的内容,必须等儿子沉睡后醒来,然后儿子写入内容

能观察到在输出Son sleep:后停顿5秒

例5-21:匿名管道的大小

#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(){
	int pfd[2],i=0;
	pid_t pid;
	if(pipe(pfd)<0){
		printf("pipe create error\n");
		return -1;
	}
	printf("pipe create success\n");
	pid=fork();
	if(pid<0){
		printf("fork create error\n");
		return -1;
	}
	if(pid>0){//father
		close(pfd[0]);//关闭读
		char buf[]="a";
		while(1){
			write(pfd[1],buf,sizeof(buf[0]));//把buf写入管道
			printf("write pipe %d, total size %dB\n", i+1, (i+1)*sizeof(buf[0]));
			i++;		
		}
	}
	else{//son
		close(pfd[1]);//关闭写
		pause();
		return 0;
	}
}//main

 Linux默认的PIPE缓冲区大小为64KB

2.命名管道

通信的多进程间不存在亲缘关系时,通过提供一个有路径名的管道文件对应的缓冲区实现通信

如何创建?(通信各方要有权访问管道文件)

  • 利用mkfifo命令
  • 利用代码建管道

shell中对应命名管道操作的命令:mkfifo

特点:

  • 允许无亲缘关系进程间的通信。
  • FIFO以文件形式存在于文件系统中,但不通信的话文件内没有数据
  • 文件操作方式基于“先进先出”原理
  • 提供灵活多样的同步机制

例5-22命名管道的使用

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
//读管道
int main(int argc,char *argv[]){
    if (argc !=2) {
        printf("not enough params,give    pipefile name\n");       
       exit(1);}    
    char *pipefile;    
    pipefile=argv[1];    
    char buf[100];    
    int fd,i;    
    printf("read open namedpipe!!\n");
    fd=open(pipefile,O_RDONLY,0);
    if (fd<0) {
        printf("no such namedpipe file !!\n");
        exit(-1); }
    printf("OK!namedpipe opened for read!\n");    
    i=read(fd,buf,100);    
    buf[i]=0;    
    printf("OK!readed from namedpipe: %s!\n",buf);      
    return 0;  
}
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
//写管道
int main(int argc,char *argv[]){      
    if (argc !=2){ 
        printf("not enough params,give pipefile name\n");       
        exit(1);}        
    char *pipefile;
    pipefile=argv[1];    
    int fd;    
    char *teststr="test strings!";  
    printf("OPEN namedpipe--%s for write!\n",pipefile);    
    fd=open(pipefile,O_WRONLY,0);
    
    printf("OK!namedpipe--%s opened for write!\n",pipefile);    
    sleep(5);    
    write(fd,teststr,strlen(teststr));    
    printf("OK!namedpipe write successfully!\n");    

    exit(0);
}

(1)用命令创建管道

  也可以用代码创建管道,在命令行输入管道名和路径,不输入路径默认在当前路径下建立

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
int main (int argc, char *argv[] ){
	if (argc != 2)  {
		printf("USEMSG: create_fifo {fifoname}\n");
		exit (1); 
	} //if
	char *pipefile;     
	pipefile=argv[1];     //命令行传入参数做文件名
	
	if ((mkfifo (pipefile, 0666 )) < 0) {//权限是rwx,6对应rw-
		perror("mkfifo failed");
		exit(1);      
	}//if
	printf("mkfifo successed,name is %s\n",pipefile);
	return 0;                    
}

(2)打开读端:读端等待写端

(3)打开写端:写入

(4)读端完成读

 

 例5-23命名管道非阻塞模式通信

注意权限,最好su root

注意创建管道的路径

//读
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>


#define FIFO "/home/chenqiqi/文档/code/book/myfifo"
void main(int argc,char** argv){
	char buf_r[100];
	int  fd, nread;    
	// 创建管道 
	if((mkfifo(FIFO,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST))
		printf("cannot create fifoserver\n");   
 
	printf("Preparing for reading bytes...\n");    
	memset(buf_r,0,sizeof(buf_r));        
	fd=open(FIFO,O_RDONLY|O_NONBLOCK,0);   //不要阻塞,没数据也往下执行
	if(fd==-1){ //出错警告
		perror("fail");
		exit(1);
	}
     
	while(1) {
		memset(buf_r,0,sizeof(buf_r));        
		if((nread=read(fd,buf_r,100))==-1)  {//出错
			if(errno==EAGAIN)    //出错号
				printf("no data yet\n");
		}else{
			printf("read %s from FIFO\n",buf_r);//读数据
		}
        	sleep(1);    
	} //while

    pause(); //暂停,等待信号
    unlink(FIFO); //删除文件
}


#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
//写

#define FIFO "/home/chenqiqi/文档/code/book/myfifo"
void main(int argc,char** argv){
	int fd,j;
	char w_buf[100];//用于缓存参数传递的信息
	int nwrite;        
	//打开命名管道FIFO
	fd=open(FIFO,O_WRONLY|O_NONBLOCK,0);
	if(argc==1){//判断有没有输入
		printf("Please send something\n");
	      exit(-1);        
	}//if
    
	strcpy(w_buf,argv[1]);    //从agrv复制字符串到w_buf
	//连续10次向管道写入数据 
	for(j=0;j<10;j++){
		if((nwrite=write(fd,w_buf,strlen(w_buf)))==-1){  
		// 将w_buf所指内存写入strlen(w_buf)个字节,到参数fd所指的文件
			if(errno==EAGAIN)
			printf("The FIFO has not been read yet.Please try later\n");
		}else 
            	printf("write %s to the FIFO\n",w_buf);
    	}//for
}

因为读程序要创建管道,所以先执行读,此时读不到数据

再执行写,写入数据

 

例5-24多方读管道同步处理

父亲创建2个孩子,2个孩子各写10句,父亲读20句

#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(void)
{
	int r,i,p1,p2,fd[2];
	char buf[10],s[50];
	pipe(fd);          //创建管道
	while((p1=fork())==-1);        //创建子程序失败时,循环]

	if(p1==0){    //孩子1
		//lockf(fd[1],1,0);        
		sprintf(buf, "AAAAAAAAAA\n");//把字符串放入buf
		printf("child process p1 WRITE!\n");
		for(i=1;i<=10;i++){ 
			write(fd[1],buf,10);    //把buf中的字符写入管道
			sleep(1);        
		}
		//lockf(fd[1],0,0);
		exit(0);                
	} 
	else{//父亲又建立一个孩子    
		while((p2=fork())==-1);
		//创建子程序失败时,循环
	}

	if(p2==0){    //孩子2
		//lockf(fd[1],1,0);
		sprintf(buf, "BBBBBBBBBB\n");///把字符串放入buf
		printf("child process p2 WRITE!\n");
		for(i=1;i<=10;i++){    
			write(fd[1],buf,10);    //把buf中的字符写入管道
			sleep(1);    
		}
		//lockf(fd[1],0,0);
		exit(0);                
	} 

	//父亲读20次    
	printf("father read:\n");
	for(i=1;i<=20;i++) {
		if((r=read(fd[0],s,10))==-1)//从fd读到s中
			printf("can’t read pipe\n");
		else
			printf("%s",s);        
	}
	printf("father read end.\n");
	wait(0);
	wait(0);
	exit(0);

}

输出上图结果后因为sleep(1)停顿,然后再次输出父亲读的20次:

 孩子1写了一两句就被孩子2打断了,为了避免这种情况,加上管道自身的锁,运行结果如下:

孩子1先写了10句A,孩子2又写了10句B,然后父亲一起读出这20句

3.软中断

软中断是对硬件中断的一种模拟,发送软中断就是向接收进程发送一个信号。

接收进程收到软中断信号后,执行一个事先关联的处理程序。软中断处理程序必须等接收进程执行时才生效。进程可向自身发送软中断信号,以便在特殊情况下,进程能转入规定好的处理程序。

(1)信号接收方进程对软中断信号预置处理程序

软中断信号预置函数:signal (sig, function)

(2)发送信号的进程向指定进程发送某种类型的软中断信号;

发送软中断信号的函数:int kill (pid, sig)

(3)信号动作触发信号处理

例5-25软中断

#include<signal.h>
#include<sys/types.h>
int k;		//定义循环变量

//定义软中断处理函数
void mysigfunc(int sig) {	
	k=0;	//修改循环变量k的值为0
	printf("get signal!\n");
}

int main(){
//预置信号处理函数,接收到SIGHUP信号即触发mysigfunc函数
	signal(SIGHUP, mysigfunc);	
	k=1;	

//软中断处理函数通过修改循环变量使循环结束,
	while(k==1)   		
		printf("Hello! %d\n",getpid());
	printf("OK!\n");
	//结束后输出提示OK
	exit(0);
}

启动进程后,在另一个终端改为root用户,执行以下命令

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值