Допустим перед нами задача по ipc взимодействовать с одним единственным дочерним процессом. Взаимодействие в стиле запрос-родителя-ответ-ребенка, не более.
Но ребенок может вообще ничего не ответить или ответить слишком много, намного превышающим лимит PIPE_BUF (man 7 pipe), поэтому выходом будет процесс чтения защитить таймаутом. Не блокирующее или асинхронное чтение для решения данной проблемы не нужно - достаточно select-а.
Самый простой вариант видится таким:
// k_systemDependetLimitation == PIPE_BUF
struct timeval tv;
tv.tv_sec = k_ipcWaitDataDelay;
tv.tv_usec = 0;
std::string ret;
int returnCode;
while (true) {
    if ((returnCode = select(m_istance(client).m_readFromChildProccess[0] + 1,
        &m_istance(client).m_readFromChildWait, nullptr, nullptr, &tv)) > 0) {
        char rawBuffer[k_bufferLength] = {0};
        ssize_t readLegth;
        if ((readLegth = read(m_istance(client).m_readFromChildProccess[0], rawBuffer, k_systemDependetLimitation)) > 0) {
            ret.append(rawBuffer, rawBuffer + readLegth);
            if (readLegth == k_systemDependetLimitation) {
                continue;
            }
            break;
        } else {
            releaseIpcAndswitchToErrorState(client);
            break;
        }
    } else if (!returnCode) {
        FD_ZERO(&m_istance(client).m_readFromChildWait); // reinit for select (see man select)
        FD_SET(m_istance(client).m_readFromChildProccess[0], &m_istance(client).m_readFromChildWait);
        break;
    } else {
        releaseIpcAndswitchToErrorState(client);
        break;
    }
}
По скольку чтение блокирующее мы не можем быть уверены что после readLegth == k_systemDependetLimitation что-то есть или чего-то нет
поэтому тут неизбежно нужно запрашивать select, если что-то есть select сразу же вернёт управление и read опять начнёт читать, если ничего нет - повисит за зря (а что делать?) и вернет управления по таймайту else if (!returnCode).
Если прочли меньше, значит конец чтения.
Но я хотел бы читать больше чем 4096 за раз. Мотивация: не хочу лишние переключения контекста. И вообще хотел бы например использовать select только изначально, и дальше с помощью каких-то техник определять стоит ли запускать read еще или нет, но похоже это не возможно?
Дело в том что читать >4096 сложно. Даже при условии что пишет в канал только один процесс и даже с гарантией что он пишет ровно тогда когда буффер пайпа полностью свободен (родитель все ранее записанное, от прошлого запроса, прочитал).
Казалось бы можно просто указать читать не 4096 а больше, и всё. Но в редких случаях ядро остановит чтение на границе 4096 Т.е. как раз на границе атомарной передачи по каналу.
Это можно попытаться отловить:
Например:
//k_bufferLength == 9000
...
        if ((readLegth = read(m_istance(client).m_readFromChildProccess[0], rawBuffer, k_bufferLength)) > 0) {
            ret.append(rawBuffer, rawBuffer + readLegth);
            if (readLegth == k_bufferLength) {
                // здесь мы по прежнему ничего не можем сказать о том что больше нечего читать
                continue;
            }
            // если чтение прервано не на границе PIPE_BUF то оно конечно
            if (readLegth % k_systemDependetLimitation) { 
                break;
            }
        } else {
            releaseIpcAndswitchToErrorState(client);
            break;
        }
    }
...
На одних и тех же тестовых запусках над одними и теми же тестовыми данными данный код в подавляющем большинстве случаяв работал, я расчитваю
что может быть прочтено только:
или ==9000 (т.е. 2 полных атомарных цикла записи в пайп и один не атомарный)
или <9000, но не кратный границе 4096 тогда типа явно всё прочитано, потому что типа ядро не прервёт такое, потому что граница атомарности (судя по размеру прочитанного уже пройдена - а значит то что не лежит на такой границе - ядро не прерывает)
или кратный_атомарному - значит тут есть возможность того что ядро первало.
Но такие выводы, оказались не верны, крайне редко но бывает:
что сначала прочитали 9000, потом вместо например 4132 (что бывает в большинстве случаев и тогда ок)
мы прочитали скажем 3800 - и все - тогда код прерывает чтение, а по факту еще осталось читать (4132-3800 байт).
И так есть ли какой-то красивый способ без дополнтельного вызова select пусть и с минимальными миллисекунндыми таймаутами - определять что ядро все передало что пишет дочерний.
Или таких способов нет, и едиственным способом (чтобы передавать много с минимальным дроблением на итерации (ну т.е. читать не по 4096 а по много)) был бы в начале передчи передавать размер передаваемого данного (чтобы размер укладывался в первые байты передачи) или что еще лучше - просто ввести маркер начала передачи и конца, и пока в принятой последовательности нет маркера конца - читать ещё.
но вот фишка в том что то что отправлят дочерний я менять не могу и там таких маркеров нет :)






