嵌入式学习---应用程序设计---进程---管道篇

发布于:2023-01-12 ⋅ 阅读:(212) ⋅ 点赞:(0)

1、管道的定义:

管道是传统UNIX进程之间的通信机制,是进程之间进行通信时的产物,,像是进程在相互沟通时的漂流瓶,每个进程可以对漂流瓶进行读写,如下图:

 A,B分别是两个不同的进程,他们可以通过kernel(内核)来沟通交流,而在kernel中会产生图中一条白色的管道。

2、管道的分类:

管道在传统UNIX进程通信中可分为 :(1)无名管道;

                                                            (2)有名管道;

2.1、无名管道:

2.1.1、定义:

无名管道是一种具有“血缘”(可以是父子也可以是亲兄弟)进程间的通信方式,无名管道是LINUX中管道通信的一种原始方法。

2.1.2、特点:

(1)只能在具有亲缘关系的进程间使用(父子进程//兄弟进程间的通信)

(2)是一个半双工(允许数据在两个方向上传输,但是同一时间数据只能在一个方向上传输,其实际上是切换的单工。例如:对讲机)的通信模式,具有固定的读端和固定的写端

(3)管道也可以看成是一种特殊的文件,对于它的读写也可以使用普通的read()//write()等函数,但是它不属于任何文件系统,并且只存在于内存中。

(4)父子进程间,只要是fork()出来的,就会完美复制父进程的数据,如果在fork()之前创建管道,并获取管道的操作接口,子进程就能使用管道。由于管道存在于kernel中,因此,亲缘进程可以通过管道实现通信。

2.1.3、无名管道的创建:

创建无名管道代码如下:

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

int main()
{
	int fds[2] = {-1,-1};
	int ret = pipe(fds);
	pid_t pid = fork();
	if(pid > 0)
	{
		sleep(4);
		printf("this is parent:\n");
		char buf[20] = {0};
		gets(buf);
		write(fds[1],buf,strlen(buf));
		close(fds[0]);
		close(fds[1]);
	}
	else if(pid == 0)
	{
		printf("this is child:\n");
		char buf[20];
		lseek(fds[0], 5, SEEK_SET);
		ret = read(fds[0],buf,20);
		if(ret > 0)
		{
			puts(buf);
		}
		close(fds[0]);
		close(fds[1]);
	}
}

2.1.4、管道通信中特殊的名词:

(1)、读阻塞:

      当管道中没有数据可以读取时,会产生读阻塞(进程阻塞)

(2)、写阻塞:

      当管道已经满了,再往管道中写入数据时,会产生写阻塞,直到有空间可以写入时。

(3)、管道破裂:

      只有写端口,没有读端。

(4)、管道中不能使用lseek函数。

 2.2、有名管道:

2.2.1:有名管道的提出:

    提出有名管道的说法,目的是为了克服无名管道的不足之处:

  • 无名管道只能是用于具有亲缘关系的进程之间,这就限制了无名管道的使用范围。
  • 有名管道可以使互不相关的两个进程互相通信,有名管道可以通过路径名来指出。并在文件系统课件为了这种有名管道,Linux中专门设立了一个专门的特殊文件系统-管道文件,以FIFO的形式存在于文件系统中,这样,即使与FIFO的创建者不存在亲缘关系的进程,只要访问该路径,就能彼此通过FIFO相互通信,因此,通过FIFO不相关的进程也能交换数据,但在磁盘只是一个节点,而文件的数据只存在内存缓冲页面上,与普通管道一样。

2.2.2:有名管道的创建:

$ mkfifo myfifo

//命令行创建方式
//参数是一个普通的路径名,也就是创建后FIFO名字。

int mkfifo(cosnt char *path, mode_t mode);

//在程序中,利用函数创建
//参数与打开普通文件的open函数中的mode参数相同(文件的读写权限),如果mkfifo的一个参数是一个已经存在路径名时,会返回EEXIST错误,所以一般典型的调用代码会检查是否返回该错误,如果确实返回该错误,那么要调用打开FIFO的函数open就可以了。

2.2.3:创建有名管道的代码如下:

(写端口):


#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
 
int main(int argc, const char *argv[])
{
    int fd, nwrite;
    char buf[1024] = "\0";
    if (access(argv[1], F_OK) != 0)
    {
        int ret = mkfifo(argv[1], 0666);
        if (ret == -1)
        {
            perror("mkfifo");
            exit(-1);
        }
    }
    fd = open(argv[1], O_WRONLY);
    if (fd == -1)
    {
        perror("open");
        return -1;
    }
    while (1)
    {
        fgets(buf, sizeof(buf), stdin);
        buf[strlen(buf) - 1] = '\0';
        nwrite = write(fd, buf, strlen(buf));
        if (nwrite == -1)
        {
            perror("write error");
            return -1;
        }
        if (!strncmp(buf, "quit", 4))
            break;
    }
    return 0;
}

(读端口):


#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
int main(int argc, const char *argv[])
{
    int ret, fd, nread;
    char buf[1024] = "\0";
    if (access(argv[1], F_OK) != 0)
    {
        ret = mkfifo(argv[1], 0666);
        if (ret == -1)
        {
            perror("mkfifo");
            exit(-1);
        }
    }
    fd = open(argv[1], O_RDONLY);
    if (fd == -1)
    {
        perror("open");
        exit(-1);
    }
    while (1)
    {
        nread = read(fd, buf, sizeof(buf));
        if (nread == -1)
        {
            perror("read errro");
            exit(-1);
        }
        printf("read from fifo is %s\n", buf);
        if (!strncmp(buf, "quit", 4))
            break;
        memset(buf, '\0', sizeof(buf));
    }
    return 0;
}

3、无名管道和有名管道的区别与联系:

3.1:区别:

(1)FIFO 在文件系统中作为一个特殊的文件而存在,但 FIFO 中的内容却存放在内存中。

(2)当使用 FIFO 的进程退出后,FIFO 文件将继续保存在文件系统中以便以后使用。

(3)FIFO 有名字,不相关的进程可以通过打开命名管道进行通信。

3.2:联系:

(1)假如 FIFO 里没有数据,调用 read() 函数从 FIFO 里读数据时 read() 也会阻塞。这个特点和无名管道是一样的。
(2)通信过程中,读进程退出后,写进程向命名管道内写数据时,写进程也会(收到 SIGPIPE 信号)退出。(以可读可写方式 open 管道时,此特点不存在。)
(3)调用 write() 函数向 FIFO 里写数据,当缓冲区已满时 write() 也会阻塞。


4、总结:

    进程的学习虽然代码的编写要求不是特别高,但是进程的内容特别多,学习的时候应该将重心放在学习理解其内容,将内容掌握了以后,代码自然而然就会编写了。第二次的博客分享就到此结束了,分享的内容有什么错误或者不足的地方,还大家批评指正,感谢观看。


网站公告

今日签到

点亮在社区的每一天
去签到