Ситуация такова, что у меня есть блокирование трубы или гнездо, к которому FD я хочу write()
без блокировки, поэтому я делаю select()
первый, но это еще не гарантирует, что write()
не будет блокировать.Возможно ли() + неблокирование write() на блокирующей трубе или сокете?
Вот данные, которые я собрал. Даже если select()
указывает, что запись возможна, запись более PIPE_BUF
байтов может блокироваться. Однако запись не более PIPE_BUF
байтов, кажется, не блокирует в практике, но она не обязана POSIX spec.
Это указывает только на поведение атомов. Python(!) documentation утверждает, что:
Файлы представлены как готовые для записи на
select()
,poll()
или аналогичных интерфейсов в этом модуле гарантированно не блокировать при записи до доPIPE_BUF
байт. Это значение гарантируется POSIX как минимум512
.
В следующей тестовой программы, установите BUF_BYTES
сказать 100000
блокировать в write()
на Linux, FreeBSD или Solaris после успешного выбора. I предполагают, что именованные каналы имеют аналогичное поведение с анонимными трубами.
К сожалению, это может случиться и с блокирующими гнездами. Позвоните по телефону test_socket()
в main()
и используйте довольно большой BUF_BYTES
(100000
хорошо здесь тоже). Неясно, существует ли безопасный размер буфера, например PIPE_BUF
для сокетов.
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <limits.h>
#include <stdio.h>
#include <sys/select.h>
#include <unistd.h>
#define BUF_BYTES PIPE_BUF
char buf[BUF_BYTES];
int
probe_with_select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds)
{
struct timeval timeout = {0, 0};
int n_found = select(nfds, readfds, writefds, exceptfds, &timeout);
if (n_found == -1) {
perror("select");
}
return n_found;
}
void
check_if_readable(int fd)
{
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
printf("select() for read on fd %d returned %d\n",
fd, probe_with_select(fd + 1, &fdset, 0, 0));
}
void
check_if_writable(int fd)
{
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
int n_found = probe_with_select(fd + 1, 0, &fdset, 0);
printf("select() for write on fd %d returned %d\n", fd, n_found);
/* if (n_found == 0) { */
/* printf("sleeping\n"); */
/* sleep(2); */
/* int n_found = probe_with_select(fd + 1, 0, &fdset, 0); */
/* printf("retried select() for write on fd %d returned %d\n", */
/* fd, n_found); */
/* } */
}
void
test_pipe(void)
{
int pipe_fds[2];
size_t written;
int i;
if (pipe(pipe_fds)) {
perror("pipe failed");
_exit(1);
}
printf("read side pipe fd: %d\n", pipe_fds[0]);
printf("write side pipe fd: %d\n", pipe_fds[1]);
for (i = 0; ; i++) {
printf("i = %d\n", i);
check_if_readable(pipe_fds[0]);
check_if_writable(pipe_fds[1]);
written = write(pipe_fds[1], buf, BUF_BYTES);
if (written == -1) {
perror("write");
_exit(-1);
}
printf("written %d bytes\n", written);
}
}
void
serve()
{
int listenfd = 0, connfd = 0;
struct sockaddr_in serv_addr;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(5000);
bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
listen(listenfd, 10);
connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);
sleep(10);
}
int
connect_to_server()
{
int sockfd = 0, n = 0;
struct sockaddr_in serv_addr;
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket");
exit(-1);
}
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(5000);
if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
perror("inet_pton");
exit(-1);
}
if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("connect");
exit(-1);
}
return sockfd;
}
void
test_socket(void)
{
if (fork() == 0) {
serve();
} else {
int fd;
int i;
int written;
sleep(1);
fd = connect_to_server();
for (i = 0; ; i++) {
printf("i = %d\n", i);
check_if_readable(fd);
check_if_writable(fd);
written = write(fd, buf, BUF_BYTES);
if (written == -1) {
perror("write");
_exit(-1);
}
printf("written %d bytes\n", written);
}
}
}
int
main(void)
{
test_pipe();
/* test_socket(); */
}
раздел
Есть ли причина, по которой вы не хотите просто устанавливать трубку/сокет в неблокирующий режим и выполняться с ним? (Вы всегда можете вернуть его в режим блокировки после возврата функции write(), если вы считаете, что это необходимо) –
Это в режиме исполнения на языке, поэтому изменение блокировки будет действительно недружелюбным для программиста. – melisgl
Не, если программист никогда не видит изменения ... если каждый раз, когда вы вернетесь к тому, как это было, прежде чем вернуть управление программисту, программист никогда не узнает. (Исключение было бы, если бы несколько потоков использовало тот же самый дескриптор файла одновременно, но проекты, которые делают такие вещи, обречены на горе в любом случае ...;)) –