вторник, 14 июля 2015 г.

Как используется оперативная память при статической и динамической линковке библиотек


Самый простой случай - статическое линкование, когда весь код и данные библиотеки включаются в файл:



То есть каждая программа, которая использует библиотеку с таким способом линковки, содержит свой собственный экземпляр библиотеки.

Теперь же рассмотрим динамическое линкование. Дела обстоят совсем по-другому:



Как видно из рисунка, программа, использующая динамическую линковку, создает только копии модифицируемых глобальных переменных библиотеки ("library data"), а непосредственно код библиотек ("library code") не копирует, а использует тот, что уже загружен в память, используя отображение (mapping) адресов функций из виртуального адресного пространства на физические адреса функций загруженной в память библиотеки. И каждая программа, вызывая функции динамически линкованной библиотеки, использует одни и те же физические адреса, что позволяет экономить память.

Подробнее здесь.

воскресенье, 23 ноября 2014 г.

asm.js vs plain c performance test

Много читал в разных местах про asm.js как утилиту, позволяющую портировать обычный код С/С++ в нечто, что можно вставить в веб-страницу и оно там будет работать без каких-либо дополнительных усилий со стороны браузера (не нужно ставить никаких плагинов, расширений и проч.), достаточно просто поддержки javascript, которая сегодня есть у всех популярных браузеров.
 
asm.js - это, по сути, обычный javascript, который активно используется в вебе. Подмножество javascript, если быть более точным. Больше ответов на другие вопросы на официальной странице и странице компилятора.

Очень захотелось лично попробовать это. Наваял небольшую программу:

#include "md5.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
 
 
int main()
{
    time_t t1;
    time(&t1);
    const unsigned int dataSize = 1024 * 1024 * 30;
    unsigned char * data;
    data = (unsigned char*) malloc (dataSize);
 
    srand((unsigned int) t1);
    for(int i = 0; i < dataSize; i++)
    {
        data[i] = rand() % 255;
    }
 
    MD5_CTX mdContext;
    for(int i = 0; i < 100; i++)
    {
        MD5Init(&mdContext);
        MD5Update(&mdContext, data, dataSize);
        MD5Final (&mdContext);
    }
 
    free(data);
 
    time_t t2;
    time(&t2);
 
    const double seconds = difftime(t2, t1);
    printf ("\n%3.2f seconds", seconds);
 
 
    return 0;
}

Что мы тут делаем. Выделяем кусок памяти в 30 мегабайт, заполняем его случайными числами, а потом 100 раз вычисляем хеш этого массива, освобождаем память. Сначала запустил как обычную консольную программу, а потом скомпилировал в html-файл, который открыл в chrome и firefox.

.pro-файл консольного приложения:
TEMPLATE = app
CONFIG += console
CONFIG -= app_bundle
CONFIG -= qt
QMAKE_CFLAGS_RELEASE += -O3 -std=c99
SOURCES += main.c


Скрипт для компилирования html-страницы:
emcc -o2  main.c  -o md5.html -s TOTAL_MEMORY=40000000 

В скрипте параметр TOTAL_MEMORY указывает сколько памяти может потребить скрипт. С этим параметром надо обращаться осторожно и не указывать слишком больше число "про запас", без необходимости.
Файл md5.h я взял здесь.

Результаты:
console app: 14 sec, 31MB RAM
firefox: 36 sec,
chrome: 58 sec, 71 MB RAM

Обращает на себя внимание, что chrome работает заметно медленнее firefox. А последний не так радикально медленнее "чистого" си - всего в 2.5 раза. Потребление памяти тоже любопытно. Консольная программа ожидаемо заняла около 31 мегабайта памяти, страница chrome заняла 71 мегабайт. Всего в 2 раза больше, чем консольная. По-моему, очень и очень неплохо. Стоит сказать большое спасибо тем, кто создал и оптимизировал emscripten до такой степени, что javascript проигрывает C в быстродействии всего лишь в 2-4 раза и в потреблении памяти в 2 раза. Это делает возможным проивзодить некоторые достаточно тяжелые вычисления прямо на веб-странице, в браузере клиента.



пятница, 21 февраля 2014 г.

Ридер для магнитных лент KDM-96XX

Ниже представлен код для карт-ридера магнитных лент KDM-96XX.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <string.h>

#define CRTSCTS  020000000000    /* flow control */


#define KDM96XX_HEADER 0x60

#define KDM96XX_CONTROL_CLA 0x43

#define KDM96XX_CONTROL_EJECT_CMD 0x31
#define KDM96XX_CONTROL_PRESPOS_CMD 0x32
#define KDM96XX_CONTROL_INIT_CMD 0x33
#define KDM96XX_CONTROL_GETVER_CMD 0x34

#define KDM96XX_MSCARD_CLA 0x4D
#define KDM96XX_MSCARD_FREAD_CMD 0x31
#define KDM96XX_MSCARD_BREAD_CMD 0x32
#define KDM96XX_MSCARD_READCLEAR_CMD 0x33

char calculate_xor(char * data, int len)
{
    char rslt = 0;
    int i;
    for(i=0; i<len; i++)
        rslt ^= data[i];
   
    return rslt;
}

int set_interface_attribs (int fd, int speed, int parity, float timeoutSec)
{
        struct termios tty;
        memset (&tty, 0, sizeof tty);
        if (tcgetattr (fd, &tty) != 0)
        {
            printf("error from tcgetattr");
            return -1;
        }

        cfsetospeed (&tty, speed);
        cfsetispeed (&tty, speed);

        tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;     // 8-bit chars
        // disable IGNBRK for mismatched speed tests; otherwise receive break
        // as \000 chars
        tty.c_iflag &= ~IGNBRK;         // ignore break signal
        tty.c_lflag = 0;                // no signaling chars, no echo,
                                        // no canonical processing
        tty.c_oflag = 0;                // no remapping, no delays
        tty.c_cc[VMIN]  = 0;            // read doesn't block
        tty.c_cc[VTIME] = (int)(timeoutSec * 10.0);// read timeout in sec

        tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl

        tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
                                        // enable reading
        tty.c_cflag &= ~(PARENB | PARODD);      // shut off parity
        tty.c_cflag |= parity;
        tty.c_cflag &= ~CSTOPB;
        //tty.c_cflag &= ~CRTSCTS;

        if (tcsetattr(fd, TCSANOW, &tty) != 0)
        {
            printf("error from tcsetattr");
            return -1;
        }
        return 0;
}

void set_blocking (int fd, int should_block, float timeoutSec)
{
        struct termios tty;
        memset (&tty, 0, sizeof tty);
        if (tcgetattr (fd, &tty) != 0)
        {
            printf ("error from tcgetattr");
            return;
        }

        tty.c_cc[VMIN]  = should_block ? 1 : 0;
        tty.c_cc[VTIME] = (int)(timeoutSec * 10.0);  // read timeout in sec

        if (tcsetattr (fd, TCSANOW, &tty) != 0)
            printf("error setting term attributes");
}

void show_char_vect_as_hex(char * buf, int size)
{
    printf("[");
    int i;
    for(i=0; i<size; i++)
    {
        printf("x%02x", (unsigned int) buf[i]);
        if(i != size-1)
            printf(", ");
    }
    printf("]");
}

void show_char_vect_as_dec(char * buf, int size)
{
    printf("[");
    int i;
    for(i=0; i<size; i++)
    {
        printf("%02d", (unsigned int) buf[i]);
        if(i != size-1)
            printf(", ");
    }
    printf("]");
}

void send_init_cmd(int fd)
{
    int len = 6;
    char data[len];
    data[0] = KDM96XX_HEADER;//header
    data[1] = 0x00;
    data[2] = 0x02;//data field size
    data[3] = KDM96XX_CONTROL_CLA;//instructions class
    data[4] = KDM96XX_CONTROL_INIT_CMD;//command
    data[5] = calculate_xor(data, len-1);
    int rslt = write(fd, data, len);   
    printf("\n'init', sent bytes: ");
    show_char_vect_as_hex(data, len);
    if(rslt < 0)
    {
        printf(" write error!!");
    }
}

void send_getver_cmd(int fd)
{
    int len = 6;
    char data[len];
    data[0] = KDM96XX_HEADER;//header
    data[1] = 0x00;
    data[2] = 0x02;//data field size
    data[3] = KDM96XX_CONTROL_CLA;//instructions class
    data[4] = KDM96XX_CONTROL_GETVER_CMD; //command
    data[5] = calculate_xor(data, len-1);
    int rslt = write(fd, data, len);   
    printf("\n'getver', sent bytes: ");
    show_char_vect_as_hex(data, len);
    if(rslt < 0)
    {
        printf(" write error!!");
    }
}
void send_report_the_presence_and_pos_cmd(int fd)
{
    int len = 6;
    char data[len];
    data[0] = KDM96XX_HEADER;//header
    data[1] = 0x00;
    data[2] = 0x02;//data field size
    data[3] = KDM96XX_CONTROL_CLA;//instructions class
    data[4] = KDM96XX_CONTROL_PRESPOS_CMD;//command
    data[5] = calculate_xor(data, len-1);   
    int rslt = write(fd, data, len);
    if(rslt < 0)
    {
        printf(" send_report_the_presence_and_pos_cmd(), write error\n");
        return;
    }
    printf("\n'presence and position', sent bytes: ");
    show_char_vect_as_hex(data, len);
}

void send_fread_cmd(int fd)
{
    int len = 6;
    char data[len];
    data[0] = KDM96XX_HEADER;//header
    data[1] = 0x00;
    data[2] = 0x02;//data field size
    data[3] = KDM96XX_MSCARD_CLA;//instructions class
    data[4] = KDM96XX_MSCARD_FREAD_CMD;//command
    data[5] = calculate_xor(data, len-1);
    int rslt = write(fd, data, len);
    if(rslt < 0)
    {
        printf("send_fread_cmd(), write error\n");
        return;
    }
    printf("\n'fread', sent bytes: ");
    show_char_vect_as_hex(data, len);
}

void send_bread_cmd(int fd)
{
    int len = 6;
    char data[len];
    data[0] = KDM96XX_HEADER;//header
    data[1] = 0x00;
    data[2] = 0x02;//data field size
    data[3] = KDM96XX_MSCARD_CLA;//instructions class
    data[4] = KDM96XX_MSCARD_BREAD_CMD;//command
    data[5] = calculate_xor(data, len-1);
    int rslt = write(fd, data, len);
    if(rslt < 0)
    {
        printf("send_bread_cmd(), write error\n");
        return;
    }
    printf("\n'bread', sent bytes: ");
    show_char_vect_as_hex(data, len);
}

void send_cread_cmd(int fd)
{
    int len = 6;
    char data[len];
    data[0] = KDM96XX_HEADER;//header
    data[1] = 0x00;
    data[2] = 0x02;//data field size
    data[3] = KDM96XX_MSCARD_CLA;//instructions class
    data[4] = KDM96XX_MSCARD_READCLEAR_CMD; //command
    data[5] = calculate_xor(data, len-1);
    int rslt = write(fd, data, len);
    if(rslt < 0)
    {
        printf("send_cread_cmd(), write error\n");
        return;
    }
    printf("\n'cread', sent bytes: ");
    show_char_vect_as_hex(data, len);
}

void read_and_show_resp(int fd)
{
    //read first 4 bytes (message header)
    int transmitted = -1;
    int headerSize = 3;
    char header[headerSize];
    memset(header, 0, headerSize);
    transmitted = read(fd, header, headerSize);
    if(transmitted < 0)
    {
        printf("read_and_show_resp(), read header error\n");
        return;
    }
    printf("\nheader: ");
    show_char_vect_as_hex(header, transmitted);
    //calculate message body size and read it
    int dataSize = ((unsigned int)header[1]) * 256 + (unsigned int) header[2] + 1; //plus 1 byte for LRC
    char data[dataSize];
    memset(data, 0, dataSize);
    transmitted = read(fd, data, dataSize);
    if(transmitted < 0)
    {
        printf("read_and_show_resp(), read message body error\n");
        return;
    }
    printf("\nbody: ");
    show_char_vect_as_hex(data, transmitted);
}

void show_help()
{
    printf("program {serial port device} {command name} [timeout in seconds] \n");
    printf("command name : fread, bread, cread, getver, init, pres \n");   
    printf("usage example: \n");
    printf("./program /dev/ttyS0 fread 2.5 \n");
}

void clear_buf(int fd)
{
    set_interface_attribs (fd, B9600, 0, 0.1);  // set speed to 9600 bps, 8n1 (no parity)
    set_blocking (fd, 0, 0.1);                // set no blocking
    int bufSize = 1024;
    char buf[bufSize];
    read(fd, buf, bufSize);
}

int main(int argc, char *argv[])
{
    float timeoutSec = 2.5;

    if(argc < 2)
    {
        printf("serial port name was omitted\n");
        show_help();
        return 0;
    }   
    if(argc < 3)
    {
        printf("command name was omitted\n");
        show_help();
        return 0;
    }   
   
    char *portname = argv[1];   // like "/dev/ttyS0"
    char *cmdStr = argv[2]; // "read", "init", "pres"
   
    if(argc >= 4) //if there is timeout in command line - set it
    {
        timeoutSec = atof(argv[3]);
    }

    int fd = open (portname, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd < 0)
    {
        printf("error opening %s, retval %d\n", portname, fd);
        return -1;
    }
   
    clear_buf(fd);

    set_interface_attribs (fd, B9600, 0, timeoutSec);  // set speed to 9600 bps, 8n1 (no parity)
    set_blocking (fd, 0, timeoutSec);                // set no blocking
       
    if(strcmp("init", cmdStr) == 0)
    {
        send_init_cmd(fd);
    }
    if(strcmp("getver", cmdStr) == 0)
    {
        send_getver_cmd(fd);
    }
    if(strcmp("pres", cmdStr) == 0)
    {
        send_report_the_presence_and_pos_cmd(fd);
    }
    if(strcmp("fread", cmdStr) == 0)
    {
        send_fread_cmd(fd);
    }
    if(strcmp("bread", cmdStr) == 0)
    {
        send_bread_cmd(fd);
    }
    if(strcmp("cread", cmdStr) == 0)
    {
        send_cread_cmd(fd);
    }
   
    read_and_show_resp(fd);

    printf("\n");
   
    close(fd);

    return 0;
}

//The values for speed are B115200, B230400, B9600, B19200, B38400, B57600, B1200

пятница, 19 апреля 2013 г.

Работа в Linux с последовательным портом.

Для работы с последовательным портом удобнее всего использовать библиотеки типа boost, но только в том случае, если писать на C++, который на данный момент менее популярен, чем старый добрый C. Еще в преимущества можно добавить тот факт, что понятность кода, использующего синтаксис, созданный пару дестяков лет назад, заметно выше, чем всякие биндинги, плейсхолдеры и модели событийного программиования, которые густо замешаны с шаблонами в библиотеке boost. Простейший пример открытия, чтения и записи см. ниже.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <string.h>
 
int openSerialPort(const char * devName)
{
    //read and write in sync mode
    int hSerial = open(devName, O_RDWR | O_NOCTTY | O_SYNC);
    if(hSerial < 0)
        printf("open device \"%s\" error: %d\n", devName, hSerial);
 
    struct termios port_settings;
    bzero(&port_settings, sizeof(port_settings));
    //
    port_settings.c_cflag |= CREAD;
    port_settings.c_cflag |= CLOCAL;
    //
    port_settings.c_cc[VMIN] = 100;
    //delay, x0.2 sec
    port_settings.c_cc[VTIME] = 2;
    //flush serial port
    int fres = tcflush(hSerial, TCIFLUSH);
    if (fres < 0)
    {
        printf("tcflush() error %d\n", fres);
    }
    //set baud rate
    fres = cfsetispeed(&port_settings, B9600);
    if (fres < 0)
    {
        printf("cfsetispeed() error %d\n", fres);
    }
    fres = cfsetospeed(&port_settings, B9600);
    if (fres < 0)
    {
        printf("cfsetospeed() error %d\n", fres);
    }
    //set character size: 8.
    port_settings.c_cflag &= ~CSIZE;
    port_settings.c_cflag |= CS8;
    //set flow cotrol: none
    port_settings.c_cflag &= ~(CRTSCTS);
    //set parity: none
    port_settings.c_cflag &= ~(PARENB);
    port_settings.c_iflag |= IGNPAR;
    //set stop bits: 1
    port_settings.c_cflag &= ~(CSTOPB);
 
 
    //apply port settings
    fres = tcsetattr(hSerial, TCSANOW, &port_settings);
    if (fres < 0)
    {
        printf("tcsetattr() error %d\n", fres);
    }
    return hSerial;
}
 
int main(int argc, char* argv[])
{
    if(argc < 2) 
    {
        printf("One parameter is omitted\n");
        return -1;
    }
    const char * devName = argv[1]; // "/dev/ttyS0"
    int hSerial = openSerialPort(devName);  
    if(hSerial < 0) 
    {
        return hSerial;
    }        
 
    const char sendVect[5] = {0x01, 0x02, 0x03, 0x04, 0x05};
 
    int byteCount = write(hSerial, sendVect, sizeof(sendVect));
    if (byteCount < 0)
    {
        printf("write error %d\n", byteCount);
    }
 
    char buf[250];
    bzero(buf, sizeof(buf));
 
    byteCount = read(hSerial, buf, sizeof(buf));
    if (byteCount < 0)
    {
        printf("read error %d\n", byteCount);
    }
    else 
    {
        printf("response: \"%s\" \n", buf);
    }     
 
    close(hSerial);
 
    return 0;
}


Как использовать VMIN и VTIME читать здесь:
http://www.unixwiz.net/techtips/termios-vmin-vtime.html

Остальное должно быть ясно из комментариев к коду.

воскресенье, 23 декабря 2012 г.

Вычисление номера элемента в отсорированном массиве в SQL

Столкнулся с такой задачей: нужно для каждой строки результата работы SQL-запроса вывести номер этой строки внутри отсортированного по неким параметрам массива. Чтобы было понятно, о чем речь, приведу пример. Есть таблица с данными:
val
-------
7
3
1
5
9
4

Нужно вывести эти числа, не используя ORDER BY или еще каких SQL-инструкций  и вычилсить их номер в отсортированном массиве. Результат должен быть такой:
val   idx
--------------
7    |   4
3    |   1
1    |   0
5    |   3
9    |   5
4    |   2

Столбец "index" должен быть вычислен. Что есть номер элемента в отсортированном по возрастанию массиве? - это число элементов, которые меньше его. Если таких элементов нет - то значит данный элемент самый маленький и будет стоять первым и иметь индекс 0. Таким образом, получаем первый вариант запроса:

 SELECT
     tbl.value as val
    (SELECT
         count(*)
     FROM
         tbl tbl2
     WHERE
        tbl2.value < val ) as idx
FROM
    tbl

Это будет работать, но возникает проблема, если есть повторяющиеся числа. Тогда некоторые элементы будут иметь одинаковый индекс, а нам это не подходит. Что делать в таком случае? Я использовал значение первичного ключа как дополнительный параметр для сравнения: если два значения равны, то сравниваем их первичные ключи - уж они-то точно будут разными. Улучшенный вариант предыдущего запроса будет выглядеть так:


SELECT
     tbl.id  as myid
     tbl.value as val
    (SELECT
         count(*)
     FROM
         tbl tbl2
     WHERE
        (tbl2.value < val)
        OR
        (tbl2.value = val AND tbl2.id < myid) ) as idx
FROM
    tbl

Последний вариант и был успешно использован.

вторник, 15 мая 2012 г.

Фибоначчиева куча: как это работает

Нечасто встретишь такой качественный разбор работы чего-либо. Вот тут очень хорошо все разжевано:

http://cppalgo.blogspot.com/2011/11/fibonacci-heap.html.

http://neerc.ifmo.ru/wiki/index.php?title=Биномиальная Куча

http://habrahabr.ru/post/135232/

Читать много, но интересно. Для большего понимания тут есть рисунок.

вторник, 17 апреля 2012 г.

Timeout serial on boost

Простой класс для работы с последовательным портом ("COM2" например).


#include <exception>
#include <boost/asio.hpp>
#include <boost/asio/serial_port.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/bind.hpp>
#include <boost/utility.hpp>
#include <boost/algorithm/string.hpp>

namespace SerialPort  
{
    class SerialException : public std::exception {
    public:
        SerialException(const std::string & msg = "SerialException") : std::exception(), m_what(msg)
        {
        }
        ~SerialException() throw() {}
        const char * what() const throw() { return m_what.c_str(); }
    private:
        const std::string m_what;
    };

    class SerialTimeoutException : public SerialException {
    public:
        SerialTimeoutException(const std::string & msg = "SerialTimeoutException") : SerialException(msg)
        {
        }
    }; 
 
    namespace ba=boost::asio;
    class TimeoutSerialPortBoost {
    public:
        TimeoutSerialPortBoost() : m_io(), m_port(m_io), m_timer(m_io)
        {
        }
        void open(
                const std::string & devname,
                const size_t baud_rate,
                const size_t timeout_ms,
                const size_t char_size,
                const ba::serial_port_base::parity parity_opt,
                const ba::serial_port_base::stop_bits stop_bits_opt
                )
        {
            //
            m_port.open(devname);
            m_port.set_option(ba::serial_port_base::baud_rate(baud_rate));
            m_timeout = boost::posix_time::milliseconds(timeout_ms);  
            m_port.set_option(ba::serial_port_base::character_size(char_size)); 

            // none, odd, even
            m_port.set_option(parity_opt);
            //none, software, hardware
            m_port.set_option(ba::serial_port_base::flow_control(ba::serial_port_base::flow_control::none));
            //one, onepointfive, two
            m_port.set_option(stop_bits_opt);
        }
 
        void write(const unsigned char *data, const size_t size)
        {
            ba::write(m_port, ba::buffer(data, size));
        }
 
        void read(unsigned char * data, const size_t size)
        {
            if(m_timeout != boost::posix_time::seconds(0)) //If timeout is set, start timer
            {
                m_timer.expires_from_now(m_timeout);
                m_timer.async_wait(boost::bind(&TimeoutSerialPortBoost::timeout_expired,
                                               this,
                                               ba::placeholders::error
                                               )
                                   );
            }
            ba::async_read(m_port,
                           ba::buffer(data, size),
                           boost::bind(&TimeoutSerialPortBoost::read_completed,
                                       this,
                                       ba::placeholders::error,
                                       ba::placeholders::bytes_transferred
                                       )
                           );
            m_read_result = resultInProgress;
            m_bytes_transferred = 0;
            while(true)
            {
                m_io.run_one();// 
                switch(m_read_result)
                {
                case resultSuccess:
                    m_timer.cancel();
                    return;
                case resultTimeoutExpired:
                    m_port.cancel();
                    throw SerialTimeoutException("Error in TimeutSerialBoost::read, timeout expired");
                case resultError:
                    m_timer.cancel();
                    m_port.cancel();
                    throw SerialException("Error in TimeutSerialBoost::read, read error");
                default: //if resultInProgress remain in the loop
                    break;
                }
            }
        }
    private:
        void read_completed(const boost::system::error_code& error, const size_t transferred)
        {
            if(error)
            {
                if(error != ba::error::operation_aborted)
                    m_read_result = resultError;
            }
            else
            {
                if(m_read_result != resultInProgress)
                    return;
                m_read_result = resultSuccess;
                this->m_bytes_transferred = transferred;
            }
        }
        void timeout_expired(const boost::system::error_code & error)
        {
            if(m_read_result != resultInProgress)
                return;
            if(error != ba::error::operation_aborted)
                m_read_result = resultTimeoutExpired;
        }
    private:
        enum ReadResult
        {
            resultInProgress = 0,
            resultSuccess = 1,
            resultError = 2,
            resultTimeoutExpired = 3
        };
    private:
        ba::io_service m_io;
        ba::serial_port m_port;
        ba::deadline_timer m_timer;
        boost::posix_time::time_duration m_timeout;
        ReadResult m_read_result;
        size_t m_bytes_transferred;
    };
}

Объяснять тут особо нечего, кроме, разве что функции TimeoutSerialPortBoost::read(), которая хорошо демонстрирует сущность отношений между объектами собственно последовательного порта и т.н. сервиса ввода-вывода. Суть в следующем. Если мы хотим что-то прочитать из последовательного порта, то мы делаем это не напрямую (вызывая функции чтения класса порта), а посредством сервиса ввода-вывода. Зачем? Потому что мы не можем напрямую указать последовательному порту, что "надо прочитать некоторые данные, но ждать не более n миллисекунд". Для этого у нас есть сервис. Он "знает" об обоих объектах (таймер и последовательный порт) и мы будет вызывать его функцию run_one() до тех пор, пока не произойдет какое-то событие - чтение данных или истечет таймаут. Вызов run_one(). Фактически, вызов функции run_one() блокирует выполнение программы до тех пор, пока не произойдет какое-нибудь событие. После каждого вызова проверяется значение переменной состояния. Переменная состояния m_read_result может быть изменена обработчикам чтения или обработчиком таймаута (одно из этих событий обязательно произойдет после первого вызова run_one) и мы всегда будем знать, как у нас дела.
Естественно, мы можем напрямую вызывать функции чтения класса serial_port, но это будет во 1-х, идеологически неправильно, а во 2-х, мы не сможем завершать ожидание байт по истечению таймаута.

Почему все так сложно и громоздко? Почему было не сделать так, как это осуществлено, например, в WinAPI, когда при вызове CreateFile() мы сразу указываем все параметры, в том числе и таймаут? Потому, что так это и есть на самом деле. То есть в внутри WinAPI происходит ровно то же самое, что мы сделали тут. То есть устанавливается таймер и система ждет, какое событие произойдет раньше - истечение таймаута или чтение байтов. Такова идеология создателей boost - они не ограждают программиста от труда понимать как все работает на самом деле и потому над некоторыми вещами действительно придется хорошенько подумать. Хорошо бы примеров побольше давали, потому что данный исходник был сделан после изучения исходников другого программиста (TimeoutSerial.h,  TimeoutSerial.cpp). Было бы очень удобно, если бы такие хорошие примеры находились прямо на boost.org.