我一直在尝试让我的程序工作几个小时,而我无法弄清楚我的代码有什么问题。它是关于使用管道在进程之间传递变量。每个过程增加M次。当我使用共享内存时,该程序工作正常,但当我将其更改为使用管道时,这是一场灾难。创建或使用命名管道似乎根本不起作用,或者我想我只是以错误的方式进行操作。这是源代码:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/mman.h>
#include <unistd.h>
#include <memory.h>
#include <fcntl.h>
#include <sys/stat.h>
#define PIPE_NAME "MY_PIPE"
#define N 5
#define M 10
struct sembuf operations;
int semid;
key_t key;
int marker;
void semWait(int semid, int sempos) {
operations.sem_num = sempos;
operations.sem_op = -1;
operations.sem_flg = 0;
if (semop(semid, &operations, 1) < 0) {
perror("ERROR: semop wait\n");
exit(-1);
}
}
void semPost(int semid, int sempos) {
operations.sem_num = sempos;
operations.sem_op = 1;
operations.sem_flg = IPC_NOWAIT;
if (semop(semid, &operations, 1) < 0) {
perror("ERROR: semop post\n");
exit(-1);
}
}
void worker(int id) {
int j, nmarker;
int fd = open(PIPE_NAME, O_RDWR);
read(fd, &nmarker, sizeof(int));
for (j = 0 ; j < M; j++) {
semWait(semid, id);
nmarker = nmarker + 1 ;
printf("%d ", marker);
semPost(semid, N);
}
write(fd, &nmarker, sizeof(nmarker));
close(fd);
}
main() {
int i, tempPID;
int sarray[N+1] = {0};
key = 23;
marker = 0;
if ((semid = semget(key , N+1, 0666 | IPC_CREAT)) == -1) {
perror("ERROR: semget\n");
exit(-1);
}
if ((semctl(semid, N+1, SETALL, sarray)) < 0) {
perror("ERROR: semctl - val\n");
exit(-1);
}
if(mkfifo(PIPE_NAME, S_IFIFO | 0666) < 0) {
perror("ERROR:pipe\n");
exit(-1);
}
int fd;
if( fd = open(PIPE_NAME, O_WRONLY) < 0 ){
perror("ERROR:open\n");
exit(-1);
}
write(fd, &marker, sizeof(marker));
close(fd);
for(i = 0; i < N; i++) {
tempPID = fork();
if (tempPID < 0) {
perror("ERROR: fork\n");
exit(-1);
}
else if (tempPID == 0) { // if child
worker(i);
exit(0);
}
}
for (i = 0 ; i < (M*N); i++) {
semPost(semid, i%N);
semWait(semid, N);
}
printf("Marker = %d\n", marker);
if (semctl( semid, 1, IPC_RMID ) == -1) {
perror("ERROR: semctl free\n");
exit(-1);
}
unlinc(PIPE_NAME);
}
我创建了N个工作进程,每个进程必须将标记值增加M次。我必须创建一个“休眠”进程池并使用信号量逐个唤醒它们,但这一切都很模糊所以当前的源代码就是我想出来的......:\
这是同一程序的一个版本,但是使用共享内存而不是管道:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/mman.h>
#define N 5
#define M 10
struct sembuf operations;
int semid;
key_t key;
int *sharedmem;
void semWait(int semid, int sempos) {
operations.sem_num = sempos;
operations.sem_op = -1;
operations.sem_flg = 0;
if (semop(semid, &operations, 1) < 0) {
perror("ERROR: semop wait\n");
exit(-1);
}
}
void semPost(int semid, int sempos) {
operations.sem_num = sempos;
operations.sem_op = 1;
operations.sem_flg = IPC_NOWAIT;
if (semop(semid, &operations, 1) < 0) {
perror("ERROR: semop post\n");
exit(-1);
}
}
void worker(int id) {
int j;
for (j = 0 ; j < M; j++) {
semWait(semid, id);
(*sharedmem)++;
semPost(semid, N);
}
}
main() {
int i, tempPID;
int sarray[N+1] = {0};
int protect = PROT_READ | PROT_WRITE;
int flags = MAP_SHARED | MAP_ANONYMOUS;
if ((key = ftok("/dev/null", 4343)) == -1) {
perror("ERROR: ftok\n");
exit(-1);
}
if ((semid = semget(key , N+1, 0666 | IPC_CREAT)) == -1) {
perror("ERROR: semget\n");
exit(-1);
}
if ((semctl(semid, N+1, SETALL, sarray)) < 0) {
perror("ERROR: semctl - val\n");
exit(-1);
}
sharedmem = (int*)mmap(NULL, sizeof(int), protect, flags, 0, 0);
*(sharedmem) = 0;
for(i = 0; i < N; i++) {
tempPID = fork();
if (tempPID < 0) {
perror("ERROR: fork\n");
exit(-1);
}
else if (tempPID == 0) { // if child
worker(i);
exit(0);
}
}
for (i = 0 ; i < (M*N); i++) {
semPost(semid, i%N);
semWait(semid, N);
}
printf("Marker = %d\n", *sharedmem);
if (semctl( semid, 1, IPC_RMID ) == -1) {
perror("ERROR: semctl free\n");
exit(-1);
}
munmap(sharedmem, sizeof(int));
}
答案 0 :(得分:2)
你的一些问题出现在工人代码中 - 这两行:
int fd = open(PIPE_NAME, O_RDWR);
read(fd, &nmarker, sizeof(int));
如果您打开管道进行读写,则会遇到麻烦(IMNSHO)。打开它只读,读它,关闭它。然后打开它只写,写入它,关闭它。现在您必须考虑信号量操作应该发生的位置。在尝试打开管道进行写入之前,您实际上需要唤醒下一个进程,因为写入的打开将阻塞,直到有可用于从中读取的进程。同样,打开读取的进程将阻塞,直到有一个可用于写入的进程。因此,内核将协调进程。
您不检查open()
的返回值,因此您不知道是否有有效的文件描述符。请务必检查open()
的返回状态。
您不会检查read()
的返回值,因此您不知道是否有任何有效的管道。请务必检查read()
的返回状态。
(如果写入失败没有有意义的错误恢复,您可以决定忽略write()
的返回状态,但检查它是否正常工作并不是一个坏主意。您可以决定忽略close()
的返回状态出于类似的原因,但在执行close()
之前,您可能无法了解问题。)
继续使用工人代码:
for (j = 0 ; j < M; j++) {
semWait(semid, id);
nmarker = nmarker + 1 ;
printf("%d ", marker);
semPost(semid, N);
}
令人惊讶的是,您打印marker
而不是nmarker
;当然,基本诊断技术在读取时会打印nmarker
的值。您可能会或可能不会在每次迭代时打印j
和nmarker
。请注意,由于此代码中的任何内容均不会增加marker
,因此打印的值不会更改。
这里的逻辑序列很有意思......它最奇怪地与main()
中的循环相结合。父进程将一个值写入FIFO。只有一个孩子可以阅读该值 - 其余孩子立即获得EOF,或无限期挂起(取决于您是否在孩子中使用O_RDONLY
或O_RDWR
)。每个孩子都会发出信号以增加其值,这样做,然后再回到睡眠状态,直到再次醒来。没有任何东西可以将递增的值发送给下一个孩子。因此,每个孩子都会独立递增所选择的任何值 - 这可能是垃圾。对于共享内存,如果你有一个指向共享值的指针,那么所有进程都会立即看到增量 - 这就是为什么它被称为共享内存。但是这里没有共享内存,因此您必须明确地进行通信以使其工作。 (我想知道你的FIFO和共享内存实现是否有效,因为通信是通过共享内存 - 换句话说,意外?)
因此,如果孩子要增加每次读取的变量,它必须同时读取当前值并在循环周围每次写入新值。当然,这将是错误检查的读数。由于信号量的原因,你可能对O_RDWR
没问题,但我个人对于单独的读写开放感到更高兴 - 如果需要,可以在每次迭代时使用。但是我没有实现这个来检查它确实遇到了问题;在FIFO上使用O_RDWR
只是一种常规做法。
在您的孩子将其值增加N次后,它会将结果写入管道。
write(fd, &nmarker, sizeof(nmarker));
close(fd);
主程序然后:
printf("Marker = %d\n", marker);
if (semctl( semid, 1, IPC_RMID ) == -1) {
perror("ERROR: semctl free\n");
exit(-1);
}
unlinc(PIPE_NAME);
由于它未修改marker
,因此打印的值将为0.您应该让主进程读取每个子进程的回复。
取消链接FIFO的正确功能是unlink()
或remove()
。
正如评论中所指出的,一个问题是打开FIFO是阻塞的 - 没有读者。然而,这远非唯一的问题。
以下代码运行。我还没有确认数字正在增加(但它正在递增)。我没有检查过每个过程是否正在转变。我修改了错误处理(每次调用一行而不是3或4行),并添加了一个包含输出中PID的打印功能。我错误检查了每个系统调用(但没有打印语句)。我修复了问题if (fd = open(...) < 0)
。据我所知,在主进程中关闭FIFO会丢弃写入它的内容 - 因此父进程不再立即关闭FIFO。但主要是我将FIFO的读写写入工作循环 - 在外部打开和关闭。该代码还带有诊断打印功能,因此我可以在出错时看到它出错的地方。我还没有完成标头最小化或任何其他应该发生的清理。但是,除main()
之外的所有内容都是静态的,因此不必预先声明。它编译清洁:
/usr/bin/gcc -O3 -g -std=c99 -Wall -Wextra fifocircle.c -o fifocircle
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/mman.h>
#include <unistd.h>
#include <memory.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
static const char *arg0 = "undefined";
static void err_error(const char *fmt, ...)
{
int errnum = errno;
va_list args;
fflush(0);
fprintf(stderr, "%s: pid %d:", arg0, (int)getpid());
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, "(%d: %s)", errnum, strerror(errnum));
fputc('\n', stderr);
exit(1);
}
static void print(const char *fmt, ...)
{
va_list args;
printf("pid %d: ", (int)getpid());
va_start(args, fmt);
vfprintf(stdout, fmt, args);
va_end(args);
fflush(0);
}
#define PIPE_NAME "MY_PIPE"
#define N 5
#define M 10
static struct sembuf operations;
static int semid;
static key_t key;
static int marker;
static void semWait(int semid, int sempos)
{
operations.sem_num = sempos;
operations.sem_op = -1;
operations.sem_flg = 0;
if (semop(semid, &operations, 1) < 0)
err_error("semop wait");
}
static void semPost(int semid, int sempos)
{
operations.sem_num = sempos;
operations.sem_op = 1;
operations.sem_flg = IPC_NOWAIT;
if (semop(semid, &operations, 1) < 0)
err_error("semop post");
}
static void worker(int id)
{
int j;
int fd = open(PIPE_NAME, O_RDWR);
if (fd < 0)
err_error("failed to open FIFO %s for read & write", PIPE_NAME);
print("Worker %d: fd %d\n", id, fd);
for (j = 0 ; j < M; j++)
{
int nmarker;
print("waiting for %d\n", id);
semWait(semid, id);
if (read(fd, &nmarker, sizeof(int)) != sizeof(int))
err_error("short read from FIFO");
print("Got %d from FIFO\n", nmarker);
nmarker = nmarker + 1 ;
if (write(fd, &nmarker, sizeof(nmarker)) != sizeof(nmarker))
err_error("short write to FIFO");
print("Wrote %d to FIFO\n", nmarker);
print("posting %d\n", id);
semPost(semid, N);
}
if (close(fd) != 0)
err_error("failed to close FIFO");
print("done\n");
}
int main(int argc, char **argv)
{
int i;
int sarray[N+1] = {0};
key = 23;
marker = 0;
arg0 = argv[0];
if (argc != 1)
err_error("Usage: %s\n", arg0);
if ((semid = semget(key , N+1, 0666 | IPC_CREAT)) == -1)
err_error("semget");
if ((semctl(semid, N+1, SETALL, sarray)) < 0)
{
perror("ERROR: semctl - val\n");
exit(-1);
}
if (mkfifo(PIPE_NAME, S_IFIFO | 0666) < 0)
err_error("failed to create FIFO %s\n", PIPE_NAME);
print("FIFO created\n");
int fd;
if ((fd = open(PIPE_NAME, O_RDWR)) < 0 )
err_error("failed to open FIFO %s\n", PIPE_NAME);
print("FIFO opened\n");
if (write(fd, &marker, sizeof(marker)) != sizeof(marker))
err_error("short write to FIFO");
print("FIFO loaded\n");
print("Master: about to fork\n");
for (i = 0; i < N; i++)
{
pid_t pid = fork();
if (pid < 0)
err_error("failed to fork");
else if (pid == 0)
{
worker(i);
exit(0);
}
}
print("Master: about to loop\n");
for (i = 0 ; i < (M*N); i++)
{
print("posting to %d\n", i%N);
semPost(semid, i%N);
print("waiting for %d\n", N);
semWait(semid, N);
}
if (close(fd) != 0)
err_error("failed to close FIFO");
print("Marker = %d\n", marker);
if (semctl( semid, 1, IPC_RMID ) == -1)
err_error("semctl remove");
if (unlink(PIPE_NAME) != 0)
err_error("failed to remove FIFO %s", PIPE_NAME);
return(0);
}