您的位置:首页 > 运维架构 > Linux

linux 进程间通信方式总结

2014-04-12 15:19 357 查看
每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,InterProcess Communication)。如下图所示。

图 下进程间通信




1. 管道(Pipe)及有名管道(named pipe):

管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信;

管道是一种最基本的IPC机制,由
pipe
函数创建:
#include <unistd.h>

int pipe(int filedes[2]);


调用
pipe
函数时在内核中开辟一块缓冲区(称为管道)用于通信,它有一个读端一个写端,然后通过
filedes
参数传出给用户程序两个文件描述符,
filedes[0]
指向管道的读端,
filedes[1]
指向管道的写端(很好记,就像0是标准输入1是标准输出一样)。所以管道在用户程序看起来就像一个打开的文件,通过
read(filedes[0]);
或者
write(filedes[1]);
向这个文件读写数据其实是在读写内核缓冲区。
pipe
函数调用成功返回0,调用失败返回-1。

开辟了管道之后如何实现两个进程间的通信呢?比如可以按下面的步骤通信。

如下图. 管道



父进程调用
pipe
开辟管道,得到两个文件描述符指向管道的两端。

父进程调用
fork
创建子进程,那么子进程也有两个文件描述符指向同一管道。

父进程关闭管道读端,子进程关闭管道写端。父进程可以往管道里写,子进程可以从管道里读,管道是用环形队列实现的,数据从写端流入从读端流出,这样就实现了进程间通信。

简单程序实例如下:

#include <unistd.h>
​#include <stdlib.h>
#include <stdio.h>

#define MAXLINE 80

int main(void)
{
	int n;
	int fd[2];
	pid_t pid;
	char line[MAXLINE];

	if (pipe(fd) < 0) {
		perror("pipe");
		exit(1);
	}
	if ((pid = fork()) < 0) {
		perror("fork");
		exit(1);
	}
	if (pid > 0) { /* parent */
		close(fd[0]);
		write(fd[1], "hello world\n", 12);
		wait(NULL);
	} else {       /* child */
		close(fd[1]);
		n = read(fd[0], line, MAXLINE);
		write(STDOUT_FILENO, line, n);
	}
	return 0;
}


2.信号是比较复杂的通信方式,信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式。用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了
支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了 实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数);

信号的产生

A.用户在终端按下某些键时,终端驱动程序会发送信号给前台进程,例如ctr+c产生SIGINT, ctr + \产生SIGQUI信号,ctr + z产生SIGTSTP。

B.硬件异常产生信号,这些条件由硬件检测到并通知内核,然后内核向当前进程发送适当的信号。例如当前进程执行了除以0的指令,CPU的运算单元会产生异常,内核将这个异常解释为SIGFPE信号发送给进程。再比如当前进程访问了非法内存地址,MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给当前进程 。

C.一个进程调用int kill(pid_t pid,int sig)函数可以给另一个进程发送信号

D.可以用kill命令给某个进程发送信号,如果不明确指定信号则发送SIGTERM信号,该信号的默认处理动作是终止进程。
E.当内核检测到某种软件条件发生时也可以通过信号通知进程,例如闹钟超时产生SIGALRM信号,向读端已关闭的管道写数据时产生SIGPIPE信号。

进程对信号的处理

A.忽略此信号
B.执行该信号的默认处理动作
C.提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式成为捕捉(Catch)一个信号。

A.通过系统调用向一个指定的进程发送信号





参数说明:

第一个参数:指定发送信号的接收线程

第二个参数:信号的signum

案例一、
父进程从终端输入signum,然后发给子进程

#include <stdio.h>

#include <sys/types.h>

#include <signal.h>

#include <stdlib.h>

int main()

{

int pid;

if((pid = fork()) < 0)

{



perror("Fail to fork");

exit(EXIT_FAILURE);



}else if(pid == 0){



while(1);



}else{



int signum;



while(scanf("%d",&signum) == 1)

{

kill(pid,signum);

system("ps -aux | grep a.out");

}

}

return 0;

}

剩下的几种方式放在以后在说
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: