提出疑问
每个进程有自己独立的用户地址空间,一个进程的全局变量在另一个进程中可以看到吗?
我们用下面这个程序来进行测试:
验证结果:
可以看出:在父进程中的全局变量,如果在子进程中去改变这个全局变量,则子进程中被改变的这个值不会去影响父进程,因为子进程中的所有数据都是通过写时拷贝拷自父进程的,这两个进程的地址空间就是不同的了。
进程间通信概念:进程用户空间是相互独立的,一般而言是不能相互访问的。但很多情况下进程间需要互相通信,来完成系统的某项功能。进程通过与内核及其它进程之间的互相通信来协调它们的行为。
进程间通讯应用场景:
- 数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几兆字节之间。
- 共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到。
- 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
- 资源共享:多个进程之间共享同样的资源。为了作到这一点,需要内核提供锁和同步机制。
- 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。
今天我们就来看一些进程间通讯的方式,进程间的通讯方式有5种:管道,消息队列,信号量,共享内存,socket。今天我们谈一下第一种通讯方式:管道。
管道:管道类似于一种特殊的文件(并不是属于某文件系统),它存在于内存中,进程可以对它进行读写,它提供流控制,保证进程的正确读写,即管道为空时读进程会阻塞,管道为满时写进程会阻塞,以此实现进程之间的通信。
管道可以分为两类,即:有名管道,无名管道。
1.无名管道
它是半双工的,即数据只能单向传输。它是有进程关系限制的,只能在父子进程或者兄弟进程之间使用,双方在进行通信时,需要建立起两个管道。单独构成一个独立的文件系统,管道对于两端的进程而言就是一个文件,但它并不是普通的文件,他不属于某种文件系统,并且只存在内存中。数据的读出和写入,一个进程向管道中写的内容被另一端进程读出。写入的内容每一次都添加在管道缓冲区的末尾,并且每次都是从缓冲区头部读出数据,向管道中写入数据时,Linux将不保证写入的原子性,管道缓冲区有空闲时,写进程就会写入数据,如果进程不读走,写操作会一直阻塞。
管道的原型:
#include<unistd.h>
int pipe(int fd[2]);
当一个管道建立时,它会创建两个文件描述符:fd[0]
为读而打开,fd[1]
为写而打开。如下图:
要关闭管道只需要将这两个文件描述符关掉就行了。
单个进程中的管道几乎没有任何用处。所以,通常调用 pipe 的进程接着调用 fork,这样就创建了父进程与子进程之间的 IPC 通道。如下图所示:
若要数据流从父进程流向子进程,则关闭父进程的读端(fd[0]
)与子进程的写端(fd[1]
);反之,则可以使数据流从子进程流向父进程。
#include<stdio.h>
#include<unistd.h>
int main()
{
int fd[2];
pid_t pid;
char buff[20];
if(pipe(fd) < 0) // 创建管道
printf("Create Pipe Error!\n");
if((pid = fork()) < 0) // 创建子进程
printf("Fork Error!\n");
else if(pid > 0) // 父进程
{
close(fd[0]); // 关闭读端
write(fd[1], "hello world\n", 12);
}
else
{
close(fd[1]); // 关闭写端
read(fd[0], buff, 20);
printf("%s", buff);
}
return 0;
}
2.有名管道(FIFO)
有名管道可以看成是有文件名标识的一个管道,不同于无名管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存在于文件系统中。这样即使与FIFO创建进程不存在亲缘关系,只要访问该路径,就能够彼此通过FIFO相互通信,因此,通过FIFO不相关的进程也能交换数据。FIFO严格遵守先进先出,对管道及FIFO的读总是从开始处返回数据对他们的写则添加数据到末尾。从结构上来看无名管道没有文件路径名,不占用文件目录项,因此文件目录结构中的链表,不适用于这种结构,他只是存在于打开文件结构中的一个临时文件,随其依附的进程的生存而生存,当进程终止,无名管道也随之消亡。
1、特点
2、原型
-
FIFO可以在无关的进程之间交换数据,与无名管道不同。
-
FIFO有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。
-
1.#include <sys/stat.h> 2.// 返回值:成功返回0,出错返回-1 3.int mkfifo(const char *pathname, mode_t mode);