Sử dụng FIFO và xử lý tín hiệu trong lập trình hệ thống

Khác với pipe vô danh chỉ dùng được giữa các tiến trình có quan hệ cha-con, FIFO (First In First Out) hay còn gọi là pipe có tên, cho phép giao tiếp giữa các tiến trình không liên quan thông qua một đường dẫn file trong hệ thống.

Tạo và sử dụng FIFO

FIFO tồn tại dưới dạng một file đặc biệt trong hệ thống tệp, nhưng dữ liệu thực sự được lưu trong bộ nhớ. Kích thước file luôn là 0 byte và không hỗ trợ thao tác định vị như lseek.

Có hai cách tạo FIFO:

  • Dùng lệnh shell: mkfifo ten_pipe
  • Dùng hàm mkfifo(const char *path, mode_t mode) trong code C

Sau khi tạo, FIFO có thể mở bằng open() và thao tác như file bình thường — tuy nhiên chỉ hỗ trợ đọc/ghi tuần tự.

Ví dụ: Gửi dữ liệu qua FIFO

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

int main(int ac, char *av[]) {
    if (ac != 2) {
        fprintf(stderr, "Dùng: %s <tên_pipe>\n", av[0]);
        return 1;
    }

    // Tạo pipe nếu chưa tồn tại
    if (mkfifo(av[1], 0644) == -1 && errno != EEXIST) {
        perror("Lỗi tạo FIFO");
        return 1;
    }

    int pipe_fd = open(av[1], O_WRONLY);
    if (pipe_fd == -1) {
        perror("Không mở được FIFO để ghi");
        return 1;
    }

    char input[512];
    while (fgets(input, sizeof(input), stdin)) {
        write(pipe_fd, input, strlen(input) + 1);

        if (strncmp(input, "exit", 4) == 0) {
            unlink(av[1]); // Xóa FIFO
            close(pipe_fd);
            break;
        }
    }
    return 0;
}

Ví dụ: Nhận dữ liệu từ FIFO

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

int main(int ac, char *av[]) {
    if (ac != 2) {
        fprintf(stderr, "Dùng: %s <tên_pipe>\n", av[0]);
        return 1;
    }

    if (mkfifo(av[1], 0644) == -1 && errno != EEXIST) {
        perror("Lỗi tạo FIFO");
        return 1;
    }

    int pipe_fd = open(av[1], O_RDONLY);
    if (pipe_fd == -1) {
        perror("Không mở được FIFO để đọc");
        return 1;
    }

    char buffer[512];
    while (read(pipe_fd, buffer, sizeof(buffer)) > 0) {
        printf("Nhận: %s", buffer);

        if (strncmp(buffer, "exit", 4) == 0) {
            unlink(av[1]);
            close(pipe_fd);
            break;
        }
    }
    return 0;
}

Xử lý tín hiệu (Signal)

Tín hiệu là cơ chế giao tiếp không đồng bộ giữa tiến trình và nhân hệ điều hành, không truyền dữ liệu mà chỉ gửi mã số nguyên biểu thị sự kiện.

Mỗi tín hiệu có một hành vi mặc định (kết thúc tiến trình, tạm dừng, bỏ qua...). Lập trình viên có thể ghi đè hành vi này bằng cách đăng ký hàm xử lý tùy chỉnh.

Cú pháp đăng ký:

void (*signal(int sig, void (*handler)(int)))(int);

Trong đó:

  • sig: mã tín hiệu (ví dụ: SIGINT, SIGTERM)
  • handler: con trỏ tới hàm xử lý hoặc hằng SIG_IGN (bỏ qua), SIG_DFL (hành vi mặc định)

Ví dụ xử lý Ctrl+C:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void handle_sigint(int sig) {
    printf("\nĐã nhận tín hiệu %d. Không thoát!\n", sig);
}

int main() {
    signal(SIGINT, handle_sigint);

    while (1) {
        printf("Đang chạy... (nhấn Ctrl+C để kiểm tra)\n");
        sleep(2);
    }
    return 0;
}

Thẻ: fifo mkfifo

Đăng vào ngày 21 tháng 6 lúc 21:55