linux pipe 阻塞,Linux pipe():從管道中讀取數據並不總是能夠解阻塞寫入器

I have a problem using pipe under Linux. I would like to fill a pipe to make further write's call blocking. An other process should be able to read some characters from the pipe that should allow the other process to write.

在Linux下使用管道有問題。我想填充一個管道以進行進一步的寫調用阻塞。其他進程應該能夠從管道中讀取一些字符,這些字符應該允許其他進程寫入。

The example code:

示例代碼:

#include

#include

#include

#include

int main()

{

int pipefd[2];

int size = 65535;

int total = 0;

// Create the pipe

if(pipe(pipefd) == -1)

{

perror("pipe()");

exit(EXIT_FAILURE);

}

// Fill in (almost full = 65535 (full - 1 byte))

while(total < size)

{

write(pipefd[1], &total, 1);

total++;

}

// Fork

switch(fork())

{

case -1:

perror("fork()");

exit(EXIT_FAILURE);

case 0:

// Close unused read side

close(pipefd[0]);

while(1)

{

// Write only one byte, value not important (here -> total)

int ret = write(pipefd[1], &total, 1);

printf("Write %d bytes\n", ret);

}

default:

// Close unused write side

close(pipefd[1]);

while(1)

{

int nbread;

scanf("%4i", &nbread);

char buf[65535];

// Read number byte asked

int ret = read(pipefd[0], buf, nbread);

printf("Read %d bytes\n", nbread);

}

}

return 0;

}

I don't understand the behavior below. The process write one last time because I didn't fill the pipe completely, normal. But afterwards, the write is blocking (pipe full) and any read should unblock the waiting write call.

我不理解下面的行為。這個過程最后一次寫,因為我沒有完全填滿管道,正常。但是之后,寫操作被阻塞(管道已滿),任何讀操作都應該解除等待的寫調用的阻塞。

test@pc:~$./pipe

Write 1 bytes

4095

Read 4095 bytes

1

Read 1 bytes

Write 1 bytes

Write 1 bytes

Write 1 bytes

Write 1 bytes

Write 1 bytes

Write 1 bytes

...

Instead, the write call is unblocked only after having read 4096 bytes... WHY????

相反,寫入調用只有在讀取4096字節之后才被解除阻塞。為什么? ? ? ?

Normally, after a read success of X bytes, there should be X bytes of space available in the pipe and so the write should be able to write up to X bytes, no?

通常,在讀取X字節成功之后,管道中應該有X字節的可用空間,因此寫入應該能夠寫入X字節,不是嗎?

How can I have the behavior "read 1 byte, write 1 byte, etc" instead of "read 1 byte, read 1, read 10, read 2000, ...(until 4096 byte read), write 4096" ?

如何讓我的行為“讀1字節,寫1字節,等等”而不是“讀1字節,讀1字節,讀10字節,讀2000字節,……(讀到4096字節),寫4096" ?

2 个解决方案

#1

3

Why it doesn't work the way you think

So basically what I understand is that your pipe is associated with some kind of linked list of kernel buffers. Processes waiting to write to your pipe are waken up only when one of these buffer is emptied. It happens that in your case these buffers are 4K in size.

基本上我所理解的是,你的管道與某種內核緩沖區的鏈表相關聯。等待寫入管道的進程只有在其中一個緩沖區被清空時才會被喚醒。在這種情況下,這些緩沖區的大小是4K。

參見:http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/fs/pipe.c?id=HEAD

Specifically line: 281 Where the test on the buffer size is done and line: 287 where the decision to wake up other processes is done.

具體來說,行:281,其中對緩沖區大小進行了測試,行:287,其中喚醒其他進程的決策完成。

The size of the pipe buffer is indeed dependent on the memory page size, see man fcntl

管道緩沖區的大小確實取決於內存頁大小,請參見man fcntl

F_SETPIPE_SZ (int; since Linux 2.6.35)

Change the capacity of the pipe referred to by fd to be at least arg bytes. An unprivileged process can adjust the pipe capacity to any value between the system page size and the limit defined in /proc/sys/fs/pipe-max-size (see proc(5)). Attempts to set the pipe capacity below the page size are silently rounded up to the page size. Attempts by an unprivileged process to set the pipe capacity above the limit in /proc/sys/fs/pipe-max-size yield the error EPERM; a privileged process (CAP_SYS_RESOURCE) can override the limit. When allocating the buffer for the pipe, the kernel may use a capacity larger than arg, if that is convenient for the implementation. The F_GETPIPE_SZ operation returns the actual size used. Attempting to set the pipe capacity smaller than the amount of buffer space currently used to store data produces the error EBUSY.

How to make it work

The pattern you try to achieve is classical. But it is used the way around. People starts with an empty pipe. Process waiting for an event, does read the empty pipe. Process wanting to signal an event, write a single byte to the pipe.

你試圖達到的模式是經典的。但它是用在周圍的方式。人們從一個空管子開始。進程等待事件,讀取空管道。進程想要發送一個事件,將一個字節寫入管道。

I think I seen that in Boost.Asio but I'm too lazy to find the correct reference.

我想我是在Boost中看到的。Asio但是我太懶了,找不到正確的參考資料。

#2

0

Pipe uses 4kB pages for buffer and write is blocked until there is an empty page for write and then do not block until it is full again. It is well described in fjardon's answer. If you would like to use the pipe for signalling you are looking for opposite scenario.

Pipe使用4kB頁作為緩沖區,並阻塞寫,直到有一個空白頁用於寫,然后不要阻塞,直到再次填滿。fjardon的回答很好地描述了這一點。如果您想使用管道進行信號發送,您正在尋找相反的場景。

#include

#include

#include

#include

int main()

{

int pipefd[2];

// Create the pipe

if(pipe(pipefd) == -1)

{

perror("pipe()");

exit(EXIT_FAILURE);

}

// Fork

switch(fork())

{

case -1:

perror("fork()");

exit(EXIT_FAILURE);

case 0:

// Close unused write side

close(pipefd[1]);

while(1)

{

char c;

// Read only one byte

int ret = read(pipefd[0], &c, 1);

printf("Woke up\n", ret);

fflush(stdout);

}

default:

// Close unused read side

close(pipefd[0]);

size_t len = 0;;

char *str = NULL;

while(1)

{

int nbread;

char buf[65535];

while (getline(&str, &len, stdin)) {

if (sscanf(str, "%i", &nbread)) break;

};

// Write number byte asked

int ret = write(pipefd[1], buf, nbread);

printf("Written %d bytes\n", ret);

fflush(stdout);

}

}

return 0;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值