Liunx-进程通信-2

本文介绍了Linux进程通信中的两种主要方式:无名管道和有名管道,包括它们的特点和使用示例。无名管道适用于父子进程之间的简单通信,而有名管道通过文件系统提供了一种可在不同进程间通信的途径。
摘要由CSDN通过智能技术生成

进程间的通信(IPC)方式:

 无名管道(PIPE)
 有名管道(FIFO)
 信号(signal)
 共享内存
 消息队列
 信号量
 套接字

(1)无名管道(PIPE)

特点:

 最原始的进程间的通信方式;
 它只能在具有亲缘关系的进程间通信,例如:父子进程,兄弟进程;
 无名管道顾名思义就是没有名字, 但是它是存在的;
 可以存在linux和windows之间的共享中,因为不会生成管道文件;
 半双工工作方式:读写端分开;
 写入操作不具有原子性,因此只能用于一对一的简单通信情形;
 不能使用lseek( )来定位。

函数原型:

#include <unistd.h>
int pipe(int fildes[2]);
参  数:fildes[2] --》 fildes[0] 读端 /  fildes[1] 写端
返回值:成功= 0 / 失败 = -1

栗子:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>
int main()
{
	pid_t myid;
	int fd1[2];
	int fd2[2];
	int ret1,ret2;
	char sbuf[100];
	char rbuf[100];
	char sbuf1[100];
	char rbuf1[100];
	
	ret1 = pipe(fd1);	// 创建无名管道
	ret2 = pipe(fd2);	// 创建无名管道
	if((ret1 == -1)||(ret2 == -1))
	{
		perror("create pipe failed!\n");
		return -1;
	}
	
	printf("Createing fork a child program!\n");
	myid = fork();
	if(myid == 0)		//子进程
	{
		while(1)		//循环读写管道信息
		{
			printf("Child program: please input a message!\n");
			fgets(sbuf,100,stdin);	
			write(fd1[1],sbuf,100);
		    read(fd2[0],rbuf1,100);
			printf("Child program: message from father is:%s\n", rbuf1);
		}
		exit(0);
	}
	else if(myid > 0)	//父进程
	{
		while(1)		//循环读写管道信息
		{
			read(fd1[0],rbuf,100);
			printf("Father progrom: message from child is:%s\n", rbuf);
			printf("Father progrom: please input a message!\n");
			fgets(sbuf1,100,stdin);
			write(fd2[1],sbuf1,100);
		}
	}
	printf("Over!\n");
	wait(NULL);
	return 0;
}

(2)有名管道(FIFO)

特点:

任意两个进程之间都可以使用;
不可以创建于linux和windows共享内存中;
有名管道不能够覆盖着创建,使用access()函数来判断是否存在;
存在于文件系统之中,提供写入原子性特征;
跟普通文件一样:使用统一的read( )/write( )来读写,但不能使用lseek( );
最先被写入FIFO的数据,最先被读出来;
不仅打开管道会有可能发生阻塞,在对管道进行读写操作时也有可能发生阻塞。

函数原型:

#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);
参  数:pathname: 有名管道的路径名 / mode:权限  0666
返回值:成功= 0 / 失败 = -1

栗子:

//只读进程
#include <stdio.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <semaphore.h>
#include <string.h>
#include <stdlib.h>
 
#define FIFO "namefifo"	//定义管道名

int main(void)
{
	int my_fd,fd;
	char r_buff[30];

	bzero(r_buff,30);
	if(access(FIFO,F_OK)==-1)	//判断管道名是否存在
	{
		my_fd = mkfifo(FIFO,0664);	//创建有名管道
		if(my_fd == -1)
		{
			perror("failed!\n");
			return -1;
		}
	}

	fd = open(FIFO,O_RDONLY);	//只读方式打开管道
	if(fd==-1)
	{
		printf("open fifo file failed!\n");
		exit(0);
	}

	while(1)
	{
		bzero(r_buff,30);
		read(fd,r_buff,sizeof(r_buff));		//从管道中读取数据
		printf("r_buff = %s\n",r_buff);
	}

	close(fd);
	return 0;
}

//只写进程
#include <stdio.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <semaphore.h>
#include <string.h>
#include <stdlib.h>
 
#define FIFO "namefifo"	//定义管道名

int main(void)
{
	int my_fd,fd;
	char w_buff[30];

	bzero(w_buff,30);
	if(access(FIFO,F_OK)==-1)	//判断管道名是否存在
	{
		my_fd = mkfifo(FIFO,0664);	//创建有名管道
		if(my_fd == -1)
		{
			perror("failed!\n");
			return -1;
		}
	}
	fd = open(FIFO,O_WRONLY);	//只写方式打开管道
	if(fd==-1)
	{
		printf("open fifo file failed!\n");
		exit(0);
	}
	while(1)
	{
		bzero(w_buff,30);
		fgets(w_buff,30,stdin);
		write(fd,w_buff,strlen(w_buff));	//把数据写入管道
	}
	close(fd);
	return 0;
}

(3)信号

特点:

 非实时信号不排队,信号的响应会相互嵌套。
 如果目标进程没有及时响应非实时信号,那么随后到达的该信号将会被丢弃。
 每一个非实时信号都对应一个系统事件,当这个事件发生时,将产生这个信号。
 如果进程的挂起信号中含有实时和非实时信号,那么进程优先响应实时信号并且会从大到小依此响应,而非实时信号没有固定的次序。

 后面的31个信号(从SIGRTMIN[34] 到 SIGRTMAX[64])是Linux系统新增的实时信号,也被称为“可靠信号”,这些信号的特征是:
 实时信号的响应次序按接收顺序排队,不嵌套。
 即使相同的实时信号被同时发送多次,也不会被丢弃,而会依次挨个响应。
 实时信号没有特殊的系统事件与之对应。

    Linux中查看存在信号命令:kill -l 查看

函数原型:

A: 发送信号kill()
#include <signal.h>
int kill(pid_t pid, int sig);
	pid :进程的id
	sig :信号名字
命令发送  kill -SIGKILL 4131 == kill -信号名 pid

B: 信号的捕捉 signal()
#include <signal.h>
void (*signal(int sig, void (*func)(int)))(int);  // SIGKILL
	sig :需要捕捉的那个信号
	void (*func)(int)函数指针,回调函数,捕捉到对应的信号调用该函数
	第二个参数除了可以传递一个函数指针意外,还可以使用以下两个宏定义:
		SIG_IGN :捕捉到的信号会被忽略
		SIG_DFL :捕捉到的信号会采用系统默认的方式响应

C: 等待信号 pause()
#include <unistd.h>
int pause(void);

D: 信号的阻塞: 
#include <signal.h>
int sigemptyset(sigset_t *set);//清空信号掩码
int sigfillset(sigset_t *set);//将所有的信号添加到信号掩码中
int sigaddset(sigset_t *set, int signum);//将特定的信号添加到信号掩码中  
int sigdelset(sigset_t *set, int signum);/将特定的信号从掩码中删除
int sigismember(const sigset_t *set, int signum);//判断某个信号是不是在该掩码中
每个进程都有属于它自己的一个信号掩码,进程在运行的过程中会阻塞掉的信号就被称作信号掩码。

E: 配置信号掩码 sigprocmask()
#include <signal.h>
int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);
how:SIG_BLOCK 将set所包含的信号添加到原来的信号掩码中
	SIG_SETMASK 用set去替换原来的信号掩码
	SIG_UNBLOCK 将set中包含的信号从原来的掩码中删除
set: 新的信号掩码
oset:原本的信号掩码,原本进程中信号掩码包含了:SIGINT ,SIGCONT

F: 捕捉信号sigaction()
#include <signal.h>
int sigaction(int sig, const struct sigaction *restrict act, struct sigaction *restrict oact);
	sig :要捕捉的信号
	act :要捕捉的信号对应的响应函数就定义在这个结构体
	oact:原来的响应函数
	struct sigaction
	{
	   void(*) (int)     sa_handler 			//信号的响应函数
	   sigset_t          sa_mask 				//信号的掩码
	   int               sa_flags 				// SA_SIGINFO
		   void(*) (int,  siginfo_t * ,void )	//信号的响应函数
	}

栗子:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
// 定义信号的响应函数
void func1(int sig) 
{
	printf("now I catch signal is:%d\n",sig);
	return ;
}
int main()
{
	printf("Process PID = %d\n",getpid());
	// 捕捉信号
	signal(SIGUSR1,func1);
	signal(SIGUSR2,SIG_IGN);
	signal(SIGINT,SIG_IGN);
	signal(SIGKILL,SIG_IGN); //该信号很特殊
	// 等待外界信号的到来
	pause();
	return 0;
}

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值