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

