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

Linux下C语言基于文件实现Semaphore同步进程时输出顺序异常问题排查

问题描述

我正在Linux机器上用C语言尝试通过文件实现信号量(Semaphore),需求是同步两个进程:一个读取文件中的元音存入数组,另一个读取辅音存入数组,之后交替读取两个数组的内容,将原文件内容重建并写入新文件。

练习要求必须用第三个文件实现信号量,我将该文件的第一个字节作为信号量,让进程循环等待直到对方完成操作。用长sleep()测试时,若进程确实交替执行,输出文件符合预期,但目前信号量实现无法正常工作。

text文件内容为“hello world”,执行代码后输出文件内容是“hll wrld eoo”,求问信号量实现具体哪里出错了?

附上代码:

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <fcntl.h>
 
int main (){
    int child1=0, child2=0, fd, fd2, fs, i=0, i2=0;
    int count=0, count2=0;
    char buf1[20], buf2[20], a, b, con='1', vow='0', check1, check2;
    fd=open("text", O_RDONLY);
    fd2=open("text2", O_CREAT|O_RDWR|O_TRUNC,0777);
    fs=open("semaphore", O_RDWR, 0777);
 
    if (fork()==0)
        child1=1;
    else {
        if (fork()==0)
            child2=1;
    }
 
    // 元音进程
    if (child1){
        printf("I'm the first child\n");
        while ((read(fd,&a,1))==1){
            if (a=='a' || a=='e' || a=='i' || a=='o' || a=='u')
                buf1[count]=a;
            count++;
            }
        printf("count: %d\n", count);
        // 等待另一个进程完成
        sleep(2);
        for (i; i<=count+1; i++){
            if (buf1[i]!='\0'){
                printf("%c\n", buf1[i]);
                a=buf1[i];
                write(fd2,&a,1);
            }
            lseek(fs,0,SEEK_SET);
            write(fs,&con,1);// 通知辅音进程可以执行(信号量设为1)         
            while(check1=='1'){
                lseek(fs,0,SEEK_SET);
                read(fs,&a,1);
                check1=a;
                sleep(1);
            } // 等待直到信号量改变
        }
    }   
 
    // 辅音进程
    else if (child2){
        sleep(1);
        lseek(fd,0,SEEK_SET);
        printf ("I'm the second child\n");
        while ((read(fd,&a,1))==1){
            if (a=='a' || a=='e' || a=='i' || a=='o' || a=='u')
                ;
            else
                buf2[count2]=a;
            count2++;
        }
        // 同步
        sleep(1);
        printf("count: %d\n", count2);
 
        for (i; i<=count2+1; i++){
            lseek(fs,0,SEEK_SET);
            if (buf2[i]!='\0'){
                printf("%c\n", buf2[i]);
                b=buf2[i];
                write(fd2,&b,1);
            } 
            // 等待元音进程
            while(check2=='0'){
                lseek(fs,0,SEEK_SET);
                read(fs,&b,1);
                check2=b;
                sleep(1);
            }
            lseek(fs,0,SEEK_SET);
            write(fs,&vow,1);// 通知元音进程可以执行(信号量设为0)
        }
    }
 
    else
        printf("I'm the father\n");
    sleep(10);
    exit(0);
}
问题分析与修复

你的代码主要有以下几个关键错误:

1. 信号量文件未初始化

你打开了semaphore文件,但没有设置初始值。文件创建后默认内容是随机的,导致两个进程的等待逻辑从一开始就处于不确定状态,无法按预期交替执行。

修复:在父进程中创建并初始化信号量文件,比如初始设为'0'(让元音进程先执行):

// 父进程中初始化信号量
fs = open("semaphore", O_CREAT | O_RDWR | O_TRUNC, 0777);
char init_sem = '0';
write(fs, &init_sem, 1);
close(fs);
// 之后再重新打开供子进程使用
fs = open("semaphore", O_RDWR, 0777);

2. 等待逻辑的条件与顺序错误

  • 你在写完字符后才设置信号量,然后等待,但正确的逻辑应该是先等待信号量允许自己执行,再写入,最后设置信号量允许对方执行。
  • check1和check2未初始化,初始值为随机垃圾值,导致第一次等待循环可能直接跳过,进程连续写入,破坏交替顺序。

修复:调整信号量操作顺序,并初始化等待变量:
元音进程循环示例:

int idx = 0;
char check1 = '1'; // 初始设为需要等待的状态
while (idx < count) {
    // 等待信号量变为'0'(允许自己执行)
    lseek(fs, 0, SEEK_SET);
    read(fs, &check1, 1);
    while (check1 != '0') {
        sleep(1);
        lseek(fs, 0, SEEK_SET);
        read(fs, &check1, 1);
    }
    // 写入当前元音
    a = buf1[idx];
    write(fd2, &a, 1);
    idx++;
    // 设置信号量为'1',通知辅音进程执行
    lseek(fs, 0, SEEK_SET);
    write(fs, &con, 1);
}

辅音进程循环示例:

int idx = 0;
char check2 = '0'; // 初始设为需要等待的状态
while (idx < count2) {
    // 等待信号量变为'1'(允许自己执行)
    lseek(fs, 0, SEEK_SET);
    read(fs, &check2, 1);
    while (check2 != '1') {
        sleep(1);
        lseek(fs, 0, SEEK_SET);
        read(fs, &check2, 1);
    }
    // 写入当前辅音
    b = buf2[idx];
    write(fd2, &b, 1);
    idx++;
    // 设置信号量为'0',通知元音进程执行
    lseek(fs, 0, SEEK_SET);
    write(fs, &vow, 1);
}

3. 数组存储索引逻辑错误

原代码中,count和count2是每次读取文件字节就自增,不管是否存入数组。比如“hello”中,非元音字节不会写入buf1,但count仍会自增,导致buf1中存在大量未赋值的空/垃圾值,后续循环写入时会跳过有效数据或写入空值,破坏输出顺序。

修复:只有当字节被存入数组时,才自增计数:
元音进程存储逻辑:

while ((read(fd, &a, 1)) == 1) {
    if (a == 'a' || a == 'e' || a == 'i' || a == 'o' || a == 'u') {
        buf1[count] = a;
        count++; // 仅存储元音时自增
    }
}

辅音进程存储逻辑:

while ((read(fd, &a, 1)) == 1) {
    if (!(a == 'a' || a == 'e' || a == 'i' || a == 'o' || a == 'u')) {
        buf2[count2] = a;
        count2++; // 仅存储辅音时自增
    }
}

4. 循环范围错误

原代码中循环条件是i <= count+1和i <= count2+1,这会导致循环次数过多,读取到数组外的垃圾值。正确的循环范围应该是遍历数组中已存储的有效元素,即从0到count-1(或count2-1)。

修复:用索引变量遍历有效元素,如前面示例中的idx < count和idx < count2。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 10:01:57