399 Star 1.4K Fork 1.3K

GVPopenEuler / kernel

 / 详情

【openEuler-1.0-LTS】pipe "packet" mode行为和主线不一致

已完成
任务
创建于  
2022-03-09 15:07

对于如下程序,4.19和主线行为不一样:

unsigned pipe_size;

static void prepare_pipe(int p[2])
{
	if (pipe2(p, O_DIRECT)) abort();

	pipe_size = fcntl(p[1], F_GETPIPE_SZ);
	static char buffer[4096];

	/* fill the pipe completely; each pipe_buffer will now have
	   the PIPE_BUF_FLAG_PACKET flag */
	for (unsigned r = pipe_size; r > 0;) {
		unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
		write(p[1], buffer, n);
		r -= n;
	}

	for (unsigned r = pipe_size; r > 0;) {
		unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
		read(p[0], buffer, n);
		r -= n;
	}
}

int main(int argc, char **argv) {
	char *data_b = malloc(pipe_size);
	int fd, i;
	char ret[5];
	loff_t offs = 0;
	int p[2];

	if (!data_b)
		return -ENOMEM;

	prepare_pipe(p);
	fd = open("file", O_RDWR | O_TRUNC);
	if (fd < 0) {
		perror("open fail");
		return 0;
	}
	for (i = 0; i < pipe_size; ++i) {
		data_b[i] = i;
	}
	if (write(fd, data_b, pipe_size) != pipe_size) {
		perror("write fail");
		return 0;
	}
	fsync(fd);

	/* clear PIPE_BUF_FLAG_PACKET flags */
	splice(fd, &offs, p[1], NULL, pipe_size, 0);

	read(p[0], ret, 5);
	printf("%d %d %d %d %d\n", ret[0], ret[1], ret[2], ret[3], ret[4], ret[5]);
	read(p[0], ret, 5);
	printf("%d %d %d %d %d\n", ret[0], ret[1], ret[2], ret[3], ret[4], ret[5]);

	close(fd);
	return EXIT_SUCCESS;
}

主线运行返回
输入图片说明
4.19运行返回
输入图片说明

评论 (4)

chengzhihao 创建了任务

Hi czh549642238, welcome to the openEuler Community.
I'm the Bot here serving you. You can find the instructions on how to interact with me at Here.
If you have any questions, please contact the SIG: Kernel, and any of the maintainers: @YangYingliang , @pi3orama , @成坚 (CHENG Jian) , @Qiuuuuu , @zhengzengkai , @gogooo , @Xie XiuQi

openeuler-ci-bot 添加了
 
sig/Kernel
标签

根因:
以O_DIRECT方式创建的pipe,通过pipe_write后对pipe->bufs中每个buf->flags设置了PIPE_BUF_FLAG_PACKET标志。之后调用pipe_read读取pipe数据就会以packet模式(每次读取一个packet长度[PIPE_BUF]的数据,如果传入的长度小于PIPE_BUF,则其余数据被discard掉,后续读取从下一个packet位置开始)读取。但是在splice(fd, p[1])写入数据到pipe之后,由于9d2231c5d74e("lib/iov_iter: initialize "flags" in new pipe_buffer")重置了buf->flags
合入前:

splice(fd, p[1])   // pipe对应buf填充来自fd的数据,buf->flags不变
read(p[0], 5)      // 读取pipe第一个packet的前5B,其余数据被discard
read(p[0], 5)      // 从pipe第二个packet读取

合入后:

splice(fd, p[1])   // pipe对应buf填充来自fd的数据,buf->flags清0
read(p[0], 5)      // 读取pipe第一个packet的前5B,其余数据仍留在pipe中
read(p[0], 5)      // 从pipe第一个packet的pos 5开始读取

预期的行为应该是主线的。根据引入packet pipe的补丁9883035ae7e("pipes: add a "packetized pipe" mode for writing")描述,每次write()写入的buf设置PIPE_BUF_FLAG_PACKET视为一个packet,每次read按照一个packet读取,来自splice的数据不应该被视为packet,也即packet模式是buf粒度的,并不意味着整个pipe的所有buf行为都是packet模式。

    When we are in the packetized mode (marked by O_DIRECT as suggested by
    Alan Cox), a write() on a pipe will not merge the new data with previous
    writes, so each write will get a pipe buffer of its own.  The pipe
    buffer is then marked with the PIPE_BUF_FLAG_PACKET flag, which in turn
    will tell the reader side to break the read at that boundary (and throw
    away any partial packet contents that do not fit in the read buffer).
#define _GNU_SOURCE
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/user.h>
#include <errno.h>

unsigned pipe_size;

static void prepare_pipe(int p[2])
{
	if (pipe2(p, O_DIRECT)) abort();

	pipe_size = fcntl(p[1], F_GETPIPE_SZ);
	static char buffer[4096];

	/* fill the pipe completely; each pipe_buffer will now have
	   the PIPE_BUF_FLAG_PACKET flag */
	for (unsigned r = pipe_size; r > 0;) {
		unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
		write(p[1], buffer, n);
		r -= n;
	}

	for (unsigned r = pipe_size; r > 0;) {
		unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
		read(p[0], buffer, n);
		r -= n;
	}
}

int main(int argc, char **argv) {
	int fd, i;
	char ret[5];
	loff_t offs = 0;
	int p[2];

	prepare_pipe(p);
	char *data_b = malloc(pipe_size);
	if (!data_b)
		return -ENOMEM;

	fd = open("file", O_RDWR | O_TRUNC);
	if (fd < 0) {
		perror("open fail");
		return 0;
	}
	for (i = 0; i < pipe_size; ++i) {
		data_b[i] = i;
	}
	if (write(fd, data_b, pipe_size) != pipe_size) {
		perror("write fail");
		return 0;
	}
	fsync(fd);

	/* clear PIPE_BUF_FLAG_PACKET flags */
	splice(fd, &offs, p[1], NULL, pipe_size, 0);

	read(p[0], ret, 5);
	printf("%d %d %d %d %d\n", ret[0], ret[1], ret[2], ret[3], ret[4], ret[5]);
	read(p[0], ret, 5);
	printf("%d %d %d %d %d\n", ret[0], ret[1], ret[2], ret[3], ret[4], ret[5]);

	close(fd);
	return EXIT_SUCCESS;
}

登录 后才可以发表评论

状态
负责人
项目
里程碑
Pull Requests
关联的 Pull Requests 被合并后可能会关闭此 issue
分支
开始日期   -   截止日期
-
置顶选项
优先级
预计工期 (小时)
参与者(2)
5329419 openeuler ci bot 1632792936
C
1
https://gitee.com/openeuler/kernel.git
git@gitee.com:openeuler/kernel.git
openeuler
kernel
kernel

搜索帮助

53164aa7 5694891 3bd8fe86 5694891