MST

星途 面试题库

面试题:C语言Linux异步I/O多文件操作的并发控制

在使用C语言进行Linux异步I/O的多文件并发操作时,如何避免文件描述符资源的泄露?如果多个异步I/O请求同时操作不同文件,怎样实现对这些操作的有效同步和错误处理,以确保数据的一致性和程序的稳定性?
26.7万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

避免文件描述符资源泄露

  1. 及时关闭文件描述符:在使用完文件描述符后,无论是成功完成操作还是在操作过程中出现错误,都要确保调用close()函数关闭文件描述符。例如:
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    int fd = open("test.txt", O_RDONLY);
    if (fd == -1) {
        perror("open");
        return 1;
    }
    // 进行文件操作
    close(fd);
    return 0;
}
  1. 使用atexit函数:可以注册一个函数,在程序正常退出时自动关闭所有打开的文件描述符。例如:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>

int fd1, fd2;

void close_files() {
    close(fd1);
    close(fd2);
}

int main() {
    fd1 = open("file1.txt", O_RDONLY);
    fd2 = open("file2.txt", O_RDONLY);
    if (fd1 == -1 || fd2 == -1) {
        perror("open");
        if (fd1 != -1) close(fd1);
        if (fd2 != -1) close(fd2);
        return 1;
    }
    atexit(close_files);
    // 进行文件操作
    return 0;
}
  1. 利用RAII思想(手动模拟):定义一个结构体来管理文件描述符,在结构体的析构函数(自定义的清理函数)中关闭文件描述符。例如:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>

typedef struct {
    int fd;
} FileDescriptor;

void FileDescriptor_init(FileDescriptor *fd_obj, const char *filename, int flags) {
    fd_obj->fd = open(filename, flags);
    if (fd_obj->fd == -1) {
        perror("open");
        exit(1);
    }
}

void FileDescriptor_destroy(FileDescriptor *fd_obj) {
    close(fd_obj->fd);
}

int main() {
    FileDescriptor fd1, fd2;
    FileDescriptor_init(&fd1, "file1.txt", O_RDONLY);
    FileDescriptor_init(&fd2, "file2.txt", O_RDONLY);
    // 进行文件操作
    FileDescriptor_destroy(&fd1);
    FileDescriptor_destroy(&fd2);
    return 0;
}

多文件异步I/O操作的同步和错误处理

  1. 同步机制
    • 互斥锁(pthread_mutex_t:如果需要对共享资源(如文件元数据)进行保护,可以使用互斥锁。例如,在更新文件的某些共享状态时:
#include <pthread.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

pthread_mutex_t mutex;
int global_file_status;

void *async_io_thread(void *arg) {
    int fd = *(int *)arg;
    pthread_mutex_lock(&mutex);
    // 访问或修改共享资源 global_file_status
    pthread_mutex_unlock(&mutex);
    // 进行异步I/O操作
    return NULL;
}

int main() {
    pthread_mutex_init(&mutex, NULL);
    int fd1 = open("file1.txt", O_RDONLY);
    int fd2 = open("file2.txt", O_RDONLY);
    pthread_t tid1, tid2;
    pthread_create(&tid1, NULL, async_io_thread, &fd1);
    pthread_create(&tid2, NULL, async_io_thread, &fd2);
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_mutex_destroy(&mutex);
    close(fd1);
    close(fd2);
    return 0;
}
- **信号量(`sem_t`)**:可以用于控制同时访问文件的线程或进程数量。例如,限制最多有3个异步I/O操作同时进行:
#include <semaphore.h>
#include <pthread.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

sem_t semaphore;

void *async_io_thread(void *arg) {
    int fd = *(int *)arg;
    sem_wait(&semaphore);
    // 进行异步I/O操作
    sem_post(&semaphore);
    return NULL;
}

int main() {
    sem_init(&semaphore, 0, 3);
    int fd1 = open("file1.txt", O_RDONLY);
    int fd2 = open("file2.txt", O_RDONLY);
    int fd3 = open("file3.txt", O_RDONLY);
    int fd4 = open("file4.txt", O_RDONLY);
    pthread_t tid1, tid2, tid3, tid4;
    pthread_create(&tid1, NULL, async_io_thread, &fd1);
    pthread_create(&tid2, NULL, async_io_thread, &fd2);
    pthread_create(&tid3, NULL, async_io_thread, &fd3);
    pthread_create(&tid4, NULL, async_io_thread, &fd4);
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_join(tid3, NULL);
    pthread_join(tid4, NULL);
    sem_destroy(&semaphore);
    close(fd1);
    close(fd2);
    close(fd3);
    close(fd4);
    return 0;
}
  1. 错误处理
    • 检查系统调用返回值:每次进行异步I/O相关的系统调用(如aio_readaio_write)后,检查其返回值。如果返回值表示错误,使用errno获取具体的错误代码,并进行相应处理。例如:
#include <aio.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
    int fd = open("test.txt", O_RDONLY);
    if (fd == -1) {
        perror("open");
        return 1;
    }
    struct aiocb aiocbp;
    // 初始化 aiocbp
    int res = aio_read(&aiocbp);
    if (res == -1) {
        perror("aio_read");
        close(fd);
        return 1;
    }
    // 等待操作完成并检查结果
    res = aio_suspend(&aiocbp, 1, NULL);
    if (res == -1) {
        perror("aio_suspend");
        close(fd);
        return 1;
    }
    int status = aio_return(&aiocbp);
    if (status == -1) {
        perror("aio_return");
        close(fd);
        return 1;
    }
    close(fd);
    return 0;
}
- **错误日志记录**:使用`syslog`或自定义的日志函数记录错误信息,以便于调试和排查问题。例如:
#include <syslog.h>
#include <aio.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
    openlog("async_io_app", LOG_PID, LOG_USER);
    int fd = open("test.txt", O_RDONLY);
    if (fd == -1) {
        syslog(LOG_ERR, "open failed: %m");
        return 1;
    }
    struct aiocb aiocbp;
    // 初始化 aiocbp
    int res = aio_read(&aiocbp);
    if (res == -1) {
        syslog(LOG_ERR, "aio_read failed: %m");
        close(fd);
        return 1;
    }
    // 等待操作完成并检查结果
    res = aio_suspend(&aiocbp, 1, NULL);
    if (res == -1) {
        syslog(LOG_ERR, "aio_suspend failed: %m");
        close(fd);
        return 1;
    }
    int status = aio_return(&aiocbp);
    if (status == -1) {
        syslog(LOG_ERR, "aio_return failed: %m");
        close(fd);
        return 1;
    }
    close(fd);
    closelog();
    return 0;
}