【Linux】进程间通讯---管道

    技术2022-07-10  181

    一、进程间通讯:

    1、进程都是独立的个体,每个进程之间的数据是不共享的。

    2、fork之后,父子进程可以共享fork之前打开的文件描述符。

    例题:父进程给子进程发送“hello world”

    int main() { int fd = open("a.txt",O_RDWR ); assert(fd != -1); if(pid == 0) { sleep(1);//保证父进程已经将字符串写入到文件中。 lseek(fd, 0, SEEK_SET); //要将光标设置为起始位置,因为当副进程将hello world写入后,光标是在末尾的,如果不移动光标,是无法读入到内容的。 read(fd, buff, 127); } else { write(fd, "hello world", 11); } close(fd); }

    由上述代码可发现用文件作为数据传递的劣势:

    (1)没办法保证写入数据在读取数据之前,如果文件较大无法确定sleep睡眠的时间。

    (2)操作文件时,文件的存储空间是在磁盘上的,写入数据时,要执行一次I/O操作,读取数据时执行一次I/O操作,I/O操作效率并不是很高,而且文件操作会保存数据,我们只需要将数据保存,并不需要保存

    (3)只能做到父子进程(有关系的进程)之间数据传递,而没办法做到任意两个进程之间的数据传递

    3、进程间通讯的机制:管道(有名管道、无名管道)、信号量、共享内存、消息队列

    二、管道:管道是一种半双工通信机制

    全双工:A可以到B,B可以到A(网络);

    半双工:这一时刻要么A到B,要么B到A(管道);

    单独通讯:不管什么时候,只能A到B(收音机、电视)

    1、有名管道

    (1)在磁盘有一个管道文件标识,这个管道文件只会占据一个inode结点,并且这个管道文件任何时候都不会占据block块,也就是数据存储不会存到磁盘;数据在传递过程中会缓存在内存中。

    磁盘上的inode区域对于有权限操作的用户是共享的,管道文件标识只是为了在不同进程之间都能够访问到这个文件,所以放在了磁盘上;也就是进程间要通信必须要有能共享的东西,而磁盘的管道文件就是有名管道来完成进程间通信的共享结点。管道文件仅仅是为了使得不同的进程(有权限操作的)能够共享

    (2)如何创建管道文件?(两种方法创建)

    命令: mkfifo  filename(管道文件名)库方法:int mkfifo();

    int mkfifo(const char *pathname,mode_t mode);//pathname路径,mode_t mode权限值

    (3)如何打开管道文件?如何操作管道文件?

    管道文件打开和操作的方式:open()/write()/read()/close()

    2、无名管道

     (1)没有管道文件,借助父子进程共享fork之前打开的文件描述符,来实现进程间通信,只能用于父子进程之间。

     (2)无名管道文件的创建和打开:int pipe(int fds[2]);

    (3)无名管道文件的操作:write()/read()/close()

    三、有名管道的实现

    1、管道的创建和查看

    2、 把mainA.c中用户输入的数据通过有名管道传递给mainB.c

    //mainA.c int main() { int fd = open("./FIFO", O_WRONLY);//打开当前目录下的管道文件 assert(fd != -1); printf("Write open fifo success\n"); while (1)//获取用户输入 { char buff[128] = { 0 }; printf("input: "); fgets(buff, 127, stdin); if (strncmp(buff, "end", 3) == 0) { break; } write(fd, buff, strlen(buff) - 1);//把buff的值传入管道,不要最后一个字符/n } close(fd); } //mainB.c int main() { //获取用户的输入通过有名管道传递给mainB.c int fd = open("./FIFO", O_RDONLY); assert(fd != -1); printf("Read open fifo success\n"); while (1) { char buff[128] = { 0 }; int n = read(fd, buff, 127); if (n <= 0) { break; } printf("Read: %s\n", buff); } close(fd); }

    这个程序的执行必须同时执行这两个文件,如果只执行其中一个会单方面阻塞

    3、有名管道的原理:

    (1)A进程打开管道文件,B进程打开管道文件,不占磁盘中的空间,申请一块内存,磁盘指向这个空间

    (2)open以一种方式打开管道文件会阻塞,直到有进程以另一种方式打开此管道文件,比如以读的方式打开阻塞,再以写的方式打开才会成功

    (3)如果管道对应的内存空间中没有数据,则read会阻塞,直到内存中有数据或者所有写端关闭才会返回;有数据返回值>0,写端关闭返回值=0。

    (4)如果管道对应的内存空间已满,则write就会阻塞,直到内存中有空间或者读端关闭才会返回。

    (5)内核对管道的内存空间的管理是以循环方式,如果数据已经被读,则可以在上面覆盖上面已读的数据继续写。

    (6)任何一个进程都不能同时可读可写,这样如果自己写了以后可能会自己读。

    四、无名管道的实现

    int main() { int fds[2]; int res = pipe(fds); assert(res != -1); pid_t pid = fork(); assert(pid != -1); if (pid == 0) { close(fds[1]);//子进程直接关闭管道文件的写端 while (1) { char buff[128] = { 0 }; int n = read(fds[0], buff, 127); if (n <= 0) { break; } printf("child: %s\n", buff); } close(fds[1]); } else { close(fds[0]);//父进程直接关闭管道文件的读端,防止影响后面的操作 while (1) { char buff[128] = { 0 }; printf("input: "); fgets(buff, 127, stdin); if (strncmp(buff, "end", 3) == 0) { break; } write(fds[1], buff, strlen(buff) - 1); } close(fds[1]); } }

    无名管道的原理:

    (1)如果管道对应的内存空间中没有数据,则read会阻塞,直到内存中有数据或者所有写端关闭才会返回;有数据返回值>0,写端关闭返回值=0。

    (2)如果管道对应的内存空间已满,则write就会阻塞,直到内存中有空间

    (3)如果子进程没有关闭写端,子进程的read就不会返回,因为read返回的条件是内存中有数据或者所有写端关闭,即使父进程结束,read也不会返回,因为她自带写端和读端。

    五、管道的特点

    不管是有名管道还是无名管道,写入管道的数据都在内存中。管道是一种半双工通信方式。(通信方式有单工、半双工、全双工)有名和无名的区别:有名可以在任意进程间使用;无名主要在父子进程间。

     

     

     

     

     

     

     

     

     

     

    Processed: 0.010, SQL: 9