You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux下C语言开发井字棋:如何让多子进程在独立终端运行

解决方案思路

核心是让新启动的终端重新执行当前程序的特定分支,而非外部命令。通过命令行参数区分进程角色(主进程/玩家1/玩家2/显示窗口),配合共享内存+消息队列实现进程间通信与同步。

关键步骤

  • 程序启动时解析命令行参数,判断自身角色:
    • 无参数:作为主进程,负责创建共享内存、消息队列,fork并启动三个子终端进程。
    • 带player1/player2/display参数:直接执行对应角色的业务逻辑。
  • 子进程通过system()启动终端(如xterm -e ./tic_tac_toe player1),让新终端运行当前程序的玩家/显示分支。
  • 用共享内存存储棋盘状态,消息队列传递落子请求、更新通知,信号量保证共享内存的互斥访问。

代码实现示例

1. 头文件与全局定义

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/msg.h>
#include <sys/sem.h>
#include <string.h>
#include <wait.h>

// 棋盘大小
#define SIZE 3
// 消息类型
#define MSG_PLAYER1 1
#define MSG_PLAYER2 2
#define MSG_DISPLAY 3
#define MSG_END 4

// 共享内存的棋盘结构
typedef struct {
    char grid[SIZE][SIZE];
    int turn; // 0: player1, 1: player2
    int game_over; // 0: 进行中, 1: 结束
} Board;

// 消息队列结构
typedef struct {
    long mtype;
    int row;
    int col;
    char winner; // 'X'/'O'/'D'(平局)
} Msg;

// 信号量操作结构体
struct sembuf sem_op;

2. 主进程逻辑

主进程负责初始化IPC资源,启动三个子终端进程,最后清理资源:

void player1_logic();
void player2_logic();
void display_logic();
int check_winner(Board *board);

int main(int argc, char *argv[]) {
    // 如果是子角色进程,直接进入对应逻辑
    if (argc > 1) {
        if (strcmp(argv[1], "player1") == 0) {
            player1_logic();
        } else if (strcmp(argv[1], "player2") == 0) {
            player2_logic();
        } else if (strcmp(argv[1], "display") == 0) {
            display_logic();
        }
        exit(0);
    }

    // 主进程:创建IPC资源
    key_t shm_key = ftok(".", 'b');
    int shm_id = shmget(shm_key, sizeof(Board), IPC_CREAT | 0666);
    Board *board = (Board*)shmat(shm_id, NULL, 0);

    // 初始化棋盘
    memset(board->grid, ' ', sizeof(board->grid));
    board->turn = 0;
    board->game_over = 0;

    // 创建消息队列
    key_t msg_key = ftok(".", 'm');
    int msg_id = msgget(msg_key, IPC_CREAT | 0666);

    // 创建信号量(用于共享内存互斥)
    key_t sem_key = ftok(".", 's');
    int sem_id = semget(sem_key, 1, IPC_CREAT | 0666);
    semctl(sem_id, 0, SETVAL, 1); // 初始值1,互斥锁

    // 启动三个子终端进程
    if (fork() == 0) {
        system("xterm -e ./tic_tac_toe player1");
        exit(0);
    }
    if (fork() == 0) {
        system("xterm -e ./tic_tac_toe player2");
        exit(0);
    }
    if (fork() == 0) {
        system("xterm -e ./tic_tac_toe display");
        exit(0);
    }

    // 主进程等待所有子进程结束
    wait(NULL);
    wait(NULL);
    wait(NULL);

    // 清理IPC资源
    shmdt(board);
    shmctl(shm_id, IPC_RMID, NULL);
    msgctl(msg_id, IPC_RMID, NULL);
    semctl(sem_id, 0, IPC_RMID);

    return 0;
}

3. 玩家1逻辑示例

void player1_logic() {
    key_t shm_key = ftok(".", 'b');
    int shm_id = shmget(shm_key, sizeof(Board), 0666);
    Board *board = (Board*)shmat(shm_id, NULL, 0);

    key_t msg_key = ftok(".", 'm');
    int msg_id = msgget(msg_key, 0666);

    key_t sem_key = ftok(".", 's');
    int sem_id = semget(sem_key, 1, 0666);

    Msg msg;
    int row, col;

    while (1) {
        // 检查游戏是否结束
        sem_op.sem_num = 0;
        sem_op.sem_op = -1;
        sem_op.sem_flg = 0;
        semop(sem_id, &sem_op, 1);
        if (board->game_over) {
            sem_op.sem_op = 1;
            semop(sem_id, &sem_op, 1);
            break;
        }
        sem_op.sem_op = 1;
        semop(sem_id, &sem_op, 1);

        // 等待自己的回合
        sem_op.sem_op = -1;
        semop(sem_id, &sem_op, 1);
        if (board->turn != 0) {
            sem_op.sem_op = 1;
            semop(sem_id, &sem_op, 1);
            sleep(1);
            continue;
        }
        sem_op.sem_op = 1;
        semop(sem_id, &sem_op, 1);

        // 输入落子位置
        printf("Player 1 (X), enter row (0-2) and column (0-2): ");
        scanf("%d %d", &row, &col);

        // 验证合法性并落子
        sem_op.sem_op = -1;
        semop(sem_id, &sem_op, 1);
        if (row >=0 && row < SIZE && col >=0 && col < SIZE && board->grid[row][col] == ' ') {
            board->grid[row][col] = 'X';
            // 检查胜负
            int result = check_winner(board);
            if (result) {
                board->game_over = 1;
                msg.mtype = MSG_DISPLAY;
                msg.winner = result == 1 ? 'X' : 'D';
                msgsnd(msg_id, &msg, sizeof(msg)-sizeof(long), 0);
                // 通知另一个玩家
                msg.mtype = MSG_PLAYER2;
                msgsnd(msg_id, &msg, sizeof(msg)-sizeof(long), 0);
            } else {
                board->turn = 1; // 切换回合
                msg.mtype = MSG_DISPLAY;
                msg.row = row;
                msg.col = col;
                msgsnd(msg_id, &msg, sizeof(msg)-sizeof(long), 0);
            }
        } else {
            printf("Invalid move! Try again.\n");
        }
        sem_op.sem_op = 1;
        semop(sem_id, &sem_op, 1);
    }

    shmdt(board);
    printf("Game over! Player 1 exiting...\n");
    sleep(2);
}

4. 显示进程逻辑示例

void display_logic() {
    key_t shm_key = ftok(".", 'b');
    int shm_id = shmget(shm_key, sizeof(Board), 0666);
    Board *board = (Board*)shmat(shm_id, NULL, 0);

    key_t msg_key = ftok(".", 'm');
    int msg_id = msgget(msg_key, 0666);

    key_t sem_key = ftok(".", 'b');
    int sem_id = semget(sem_key, 1, 0666);

    Msg msg;

    // 初始显示空棋盘
    system("clear");
    printf("Tic Tac Toe\n");
    for (int i=0; i<SIZE; i++) {
        for (int j=0; j<SIZE; j++) {
            printf(" %c ", board->grid[i][j]);
            if (j < SIZE-1) printf("|");
        }
        printf("\n");
        if (i < SIZE-1) printf("---+---+---\n");
    }

    while (1) {
        msgrcv(msg_id, &msg, sizeof(msg)-sizeof(long), MSG_DISPLAY, 0);

        // 检查游戏是否结束
        sem_op.sem_num = 0;
        sem_op.sem_op = -1;
        sem_op.sem_flg = 0;
        semop(sem_id, &sem_op, 1);
        if (board->game_over) {
            system("clear");
            printf("Game Over!\n");
            if (msg.winner == 'D') {
                printf("It's a draw!\n");
            } else {
                printf("Player %c wins!\n", msg.winner);
            }
            sem_op.sem_op = 1;
            semop(sem_id, &sem_op, 1);
            break;
        }

        // 更新显示棋盘
        system("clear");
        printf("Tic Tac Toe\n");
        for (int i=0; i<SIZE; i++) {
            for (int j=0; j<SIZE; j++) {
                printf(" %c ", board->grid[i][j]);
                if (j < SIZE-1) printf("|");
            }
            printf("\n");
            if (i < SIZE-1) printf("---+---+---\n");
        }
        sem_op.sem_op = 1;
        semop(sem_id, &sem_op, 1);
    }

    shmdt(board);
    sleep(3);
}

5. 胜负判断辅助函数

int check_winner(Board *board) {
    // 检查行
    for (int i=0; i<SIZE; i++) {
        if (board->grid[i][0] != ' ' && board->grid[i][0] == board->grid[i][1] && board->grid[i][1] == board->grid[i][2]) {
            return board->grid[i][0] == 'X' ? 1 : 2;
        }
    }
    // 检查列
    for (int j=0; j<SIZE; j++) {
        if (board->grid[0][j] != ' ' && board->grid[0][j] == board->grid[1][j] && board->grid[1][j] == board->grid[2][j]) {
            return board->grid[0][j] == 'X' ? 1 : 2;
        }
    }
    // 检查对角线
    if (board->grid[0][0] != ' ' && board->grid[0][0] == board->grid[1][1] && board->grid[1][1] == board->grid[2][2]) {
        return board->grid[0][0] == 'X' ? 1 : 2;
    }
    if (board->grid[0][2] != ' ' && board->grid[0][2] == board->grid[1][1] && board->grid[1][1] == board->grid[2][0]) {
        return board->grid[0][2] == 'X' ? 1 : 2;
    }
    // 检查平局
    int full = 1;
    for (int i=0; i<SIZE; i++) {
        for (int j=0; j<SIZE; j++) {
            if (board->grid[i][j] == ' ') {
                full = 0;
                break;
            }
        }
        if (!full) break;
    }
    if (full) return 3; // 平局

    return 0; // 未分胜负
}

注意事项

  • 终端命令:如果xterm不可用,可替换为gnome-terminal -- ./tic_tac_toe player1或konsole -e ./tic_tac_toe player1,根据桌面环境调整。
  • IPC资源清理:若程序异常退出,可通过ipcs查看残留的共享内存/消息队列/信号量,用ipcrm手动删除。
  • 玩家2逻辑可参考玩家1实现,仅需将角色标识改为O,回合判断改为board->turn == 1。

内容的提问来源于stack exchange,提问作者Belal Qatoum

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 18:03:17