在不支持FIOCANCEL的VxWorks环境中如何安全关闭文件描述符?
Got it, let's work through this problem since FIOCANCEL isn't available in your VxWorks setup. Here are three practical, reliable workarounds to safely close a file descriptor that's tied up in a blocking read from another task:
1. Timeout-Based Polling with an Exit Flag
Instead of letting your read task block indefinitely on read(), use select() with a short timeout to periodically check if it's time to exit. This gives you a clean way to signal the read task to shut down before closing the FD.
Implementation Steps:
- Create a volatile, thread-safe exit flag (use a mutex or spinlock to protect access if multiple tasks might modify it):
volatile int g_shouldExitReadTask = 0; SEM_ID g_exitFlagMutex = semMCreate(SEM_Q_PRIORITY | SEM_INVERSION_SAFE); - Modify the read task to use
select()with a timeout instead of blockingread():fd_set readFds; struct timeval tv; ssize_t bytesRead; char buffer[BUFFER_SIZE]; while (1) { // Setup select set and timeout FD_ZERO(&readFds); FD_SET(targetFd, &readFds); tv.tv_sec = 1; // Adjust timeout to fit your needs tv.tv_usec = 0; int selectRet = select(targetFd + 1, &readFds, NULL, NULL, &tv); if (selectRet > 0 && FD_ISSET(targetFd, &readFds)) { // Data available: perform read bytesRead = read(targetFd, buffer, BUFFER_SIZE); if (bytesRead <= 0) { // Error or EOF: exit loop break; } // Process your data here... } else if (selectRet == 0) { // Timeout: check exit flag semTake(g_exitFlagMutex, WAIT_FOREVER); int exitNow = g_shouldExitReadTask; semGive(g_exitFlagMutex); if (exitNow) { break; } } else { // Select failed: handle error and exit break; } } // Cleanup resources in read task before exiting - In your close task, set the exit flag, wait for the read task to terminate, then close the FD:
// Signal read task to exit semTake(g_exitFlagMutex, WAIT_FOREVER); g_shouldExitReadTask = 1; semGive(g_exitFlagMutex); // Wait for read task to finish (use taskJoin if the task is joinable) taskJoin(readTaskId, WAIT_FOREVER); // Now safely close the FD close(targetFd);
2. Pipe-Based Wakeup Mechanism
This is a more efficient alternative to polling: use a pipe to instantly wake up the read task when you need to close the FD. The read task listens for both the target FD and the pipe's read end via select().
Implementation Steps:
- Create a pipe for inter-task communication:
int wakeupPipe[2]; if (pipe(wakeupPipe) != OK) { // Handle pipe creation error return ERROR; } - Modify the read task to monitor both the target FD and the pipe:
fd_set readFds; int maxFd = max(targetFd, wakeupPipe[0]) + 1; char buffer[BUFFER_SIZE]; char dummy; while (1) { FD_ZERO(&readFds); FD_SET(targetFd, &readFds); FD_SET(wakeupPipe[0], &readFds); int selectRet = select(maxFd, &readFds, NULL, NULL, NULL); if (selectRet > 0) { if (FD_ISSET(targetFd, &readFds)) { // Process data from target FD ssize_t bytesRead = read(targetFd, buffer, BUFFER_SIZE); if (bytesRead <= 0) break; // Handle data... } if (FD_ISSET(wakeupPipe[0], &readFds)) { // Wakeup signal received: exit loop read(wakeupPipe[0], &dummy, 1); // Clear pipe data break; } } else { // Select error: exit break; } } // Cleanup pipe read end in read task close(wakeupPipe[0]); - In the close task, send a wakeup signal via the pipe, wait for the read task to exit, then close the FD:
// Send wakeup signal to read task write(wakeupPipe[1], "x", 1); close(wakeupPipe[1]); // Wait for read task to terminate taskJoin(readTaskId, WAIT_FOREVER); // Safely close the target FD close(targetFd);
This method avoids polling overhead and wakes the read task immediately when needed.
3. Task Signal to Interrupt Blocking read()
VxWorks allows you to send a signal to a task to interrupt blocking system calls like read(). When the read task receives the signal, read() will return -1 with errno set to EINTR, letting you exit the read loop.
Implementation Steps:
- Ensure the read task doesn't block the signal you'll use (e.g.,
SIGUSR1):sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGUSR1); sigprocmask(SIG_UNBLOCK, &mask, NULL); - Modify the read task to handle interrupted reads:
char buffer[BUFFER_SIZE]; while (1) { ssize_t bytesRead = read(targetFd, buffer, BUFFER_SIZE); if (bytesRead < 0) { if (errno == EINTR) { // Read was interrupted by signal: exit loop break; } // Handle other errors break; } else if (bytesRead == 0) { // EOF received: exit break; } // Process data... } - In the close task, send the signal to the read task, wait for it to exit, then close the FD:
// Send signal to interrupt read task taskSignal(readTaskId, SIGUSR1); // Wait for read task to finish taskJoin(readTaskId, WAIT_FOREVER); // Close the FD safely close(targetFd);
Note: Not all device drivers support interrupting read() with signals. Test this with your specific hardware/driver first.
Recommendation
The pipe-based wakeup method is generally the best choice—it's efficient, reliable, and avoids the overhead of polling or potential signal compatibility issues. The timeout method works well if pipes aren't an option, while the signal method is a last resort for drivers that support it.
内容的提问来源于stack exchange,提问作者Mrchacha

