在半双工串行端口上设计完全异步 IO 时,我需要您的建议。目前我有一个阅读器线程和许多由信号量和互斥锁控制的编写器线程。现在我想通过消除线程来简化同步。主要问题是串口 IO 有一个奇怪的行为。
read
我需要做的就是确保write
系统调用只阻塞调用线程,直到 IO 操作实际完成。我假设read
默认情况下是阻塞系统调用。虽然我得到-1
了回报read
。有一个奇怪的EBUSY
错误,我没有描述。当前代码:
bool SerialManager::initialize(const PortType& portType, const size_t& number)
{
// Open Serial port (/dev/ttyS2 in this case)
fd = open(portName.str().c_str(), O_RDWR ); //O_NOCTTY
if (fd < 0) // if open is not successful
{
cerr << ERROR << "Unable to open `" << portName << "'." << endl;
return false;
}
else
{
cout << INFO << "Port " << portName.str() << " successfully opened."
<< endl;
cout << INFO << "Configuring port..." << endl;
fcntl(fd, F_SETFL,~O_NONBLOCK);
struct termios port_settings; // structure to store the port settings in
cfsetispeed(&port_settings, B38400); // set baud rate
cfsetospeed(&port_settings, B38400); // set baud rate
port_settings.c_cflag |= CLOCAL | CREAD;
port_settings.c_cflag &= ~CRTSCTS; // disable H/W flow control
port_settings.c_lflag &= ~( ISIG | // disable SIGxxxx signals
IEXTEN | // disable extended functions
ECHO | ECHOE); // disable all auto-echo functions
port_settings.c_lflag &= ~ICANON ; // raw mode
port_settings.c_oflag &= ~OPOST; // raw output
port_settings.c_iflag &= ~(IXON | IXOFF | IXANY); // disable S/W flow control;
port_settings.c_cc[VTIME] = 20; // wait 0.1 second to get data
port_settings.c_cc[VMIN] = 0;
port_settings.c_cflag = (port_settings.c_cflag &= ~CSIZE) | CS8; // set data byte size
port_settings.c_cflag &= ~CSTOPB; // set stop bit 1
port_settings.c_cflag &= ~PARENB; // set no parity
port_settings.c_iflag |= IGNPAR; // ignore parity
port_settings.c_iflag &= ~(INPCK | ISTRIP | PARMRK);
// Set
if (tcsetattr(fd, TCSANOW, &port_settings) < 0)
{
cerr << ERROR << "Unable to configure serial port." << endl;
return false;
}
else
{
cout << INFO << "Port `" << portName.str()
<< "' configuration was successful." << endl;
return true;
}
}
}
写入数据:
int SerialManager::asyncWriteData(const byte* data, const size_t& size)
{
int writeSize = write(fd, data, size);
return writeSize;
}
供阅读:
void SerialManager::asyncRead(byte* buffer, const size_t& size, bool& ok)
{
byte temp[256];
ssize_t packetSize = read(fd, temp, 256);
if (packetSize > 0)
{
for (size_t i = 0; i < size; ++i)
buffer[i] = temp[i];
ok = true;
}
cout << errno << endl;
perror("Error occured: "); // <=== Here I'm getting EBUSY (code 16)
ok = false;
}
SerialManager
在外面使用类:
....
word checksum = this->id + 0x2C;
checksum = ~checksum;
// Send read command
byte command[] =
{ 0xff, // heading
0xff, // ~
this->id, // id of actuator
0x04, // length
0x02, // instruction: read
0x24, // start address: present position
0x02, // data length
static_cast<byte>(checksum) //checksum
};
SerialManager::lockPort(); // lock a mutex to avoid collitions
int numbytes = SerialManager::asyncWriteData(command, 8);
if (numbytes < 0)
{
cerr << ERROR << "Could not write to serial port." << endl;
return 0;
}
cout << INFO << numbytes << " bytes has been written." << endl;
for (size_t i = 0; i < 8; ++i)
{
cout << hex << setfill('0') << setw(2) << (int) command[i] << ' ';
}
cout << endl;
byte* data = new byte[8];
bool ok;
// Here I need to make sure data write is completed before start reading
SerialManager::asyncRead(data, 8, ok);
if (ok)
{
word position = data[5] + (static_cast<word>(data[6]) << 8);
return position;
}
else
{
cerr << ERROR << "Unable to read data from serial port..." << endl;
return -1;
}
SerialManager::unlockPort(); // Unlock previously locked mutex
....
更新:
我删除了没有意义的读者线程。因为我们有一条无法控制传输的半双工线路。同步IO有两个问题:
从控制器向执行器发送很长的数据,当数据在端口上时,第一个执行器没有响应:
一个执行器可能会响应,而另一个执行器的数据未完全传输
也EBUSY
可以通过添加fsync
after来解决问题write
。这就是我需要的。(一个阻塞write
):
int SerialManager::asyncWriteData(const byte* data, const size_t& size)
{
ssize_t packetSize = write(fd, data, size);
if (packetSize > 0)
{
fsync(fd);
}
return packetSize;
}
来自人 fsync:
fsync() 将文件描述符 fd 引用的文件的所有修改的内核数据(即修改的缓冲区缓存页面)传输(“刷新”)到该文件所在的磁盘设备(或其他永久存储设备)。呼叫阻塞,直到设备报告传输已完成。它还刷新与文件关联的元数据信息