pipe/fork/exec

Tråden skapades och har fått 6 svar. Det senaste inlägget skrevs .
1

Jag har suttit och trasslat med processprogrammering ett tag nu. (Inte kurs, den gjorde jag för länge sedan, utan en del av ett seriöst projekt.) Jag tycker jag försökt allting, men jag kan inte lura ut hur jag sätter upp två pipes och startar ett annat program, och sedan får bra kommunikation mellan dem.

Det som är så fånigt är att jag alltid får kanalen tillbaka buffrad av någon anledning. Så fort jag ska skicka data ett par gånger så blir det stopp i återvägen tills buffern är full.

Jag gör allt efter "kokboken", två pipe(), en fork, barnprocessen stänger stdin och stdout och mappar om pipearna på dem, och gör sedan exec. Jag har googlat mig blå efter en lösning, men det finns inget alls, ingen annan verkar ha samma problem.

Föräldern pollar data med ioctl(FIONREAD), läser och skriver med read och write.

Jag har försökt använda setvbuf för att sätta pipearna i obuffrat läge, men den vill ha en FILE* och inte en file descriptor. Jag har försökt gå från fd till FILE* med fdopen men det verkar bara göra saken värre (hela kommunikationen slås ut, det kommer ingenting i stället för allt på slutet). Och jag har labbat lite med fflush.

Vad mer kan man göra?

  • Medlem
  • Stockholm
  • 2007-04-01 12:33

I/O mot kärnan använder sig av fd och är (generellt) obuffrad.

Ovanpå detta finns #include <stdio.h> som ger dig buffrad I/O, och där filerna accessas genom en FILE*.

Har du tittat på om popen() kan göra det du vill?

Om du vill göra det själv med 2 pipe() så observera att på de 2 fd en pipe() lämnar så är fd[0] för läsning och fd[1] för skrivning. Mappa rätt fd till stdin/out i barn-processen!
Om du använder pipe() så skulle jag inte blanda in stdio.h alls.

Kan du beskriva närmare hur problemet ter sig? Hur ser du att kommunikationen "buffras"? Fungerar child-programmet att köras med pipad in/out från en shell?
Har du en kodsnutt som demonstrerar vad du gör?

Jag tror jag knäckt det, i alla fall delvis. Med setvbuf direkt på stdin och stdout efter ommappningen i barn-grenen så verkar det funka bättre. Och jajamen, jag har just två pipes, en förälder-till-barn och en barn-till-förälder, och det är en massa stängande och dupande innan allt stämmer. Det är lätt att gå bort sig redan där.

Kommunikation med read/write mot pipen var inga problem, det var mappningen på stdio som ställde till det. Som du säger ger det ett lager buffrar till. Att blanda in stdio är nödvändigt, för poängen är att fånga upp utdata från program som jag anropar med exec. Om man behöver lite interaktion mot ett CLI-program så är stdin/stdout metoden, men absolut inte buffrat.

popen() har jag inte rotat i direkt. Jag har ju sett det förstås men jag tror det var något som gjorde det olämpligt.

Tack för svaret! Många bra synpunkter.

Helt framme är man inte...

Här är mitt testprogram:

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include </usr/include/sys/filio.h>

int slaveToMasterPipe[2];
int masterToSlavePipe[2];

void ReadMessage(char * str)
{
	int err;
	long availableData;
	
	availableData = 0;
	err = ioctl(slaveToMasterPipe[0], FIONREAD, &availableData);
	if (err != 0)  printf("ioctl error\n");
	else printf("Waiting: %d\n", availableData);

	if (availableData > 0)
	{
		read(slaveToMasterPipe[0], &str[0], availableData);
		str[availableData] = 0;
	}
	else
		str[0] = 0;
}

void WriteMessage(char * s)
{
	int theLength = strlen(s);
	write(masterToSlavePipe[1], &s[0], theLength);
}

int main()
{
	char str[1024], *cp;
	pid_t pid;
	int status, died;

	FILE *mf, *sf;
	
	pipe (masterToSlavePipe);
	pipe (slaveToMasterPipe);

	switch (pid=fork())
	{
	case -1: printf("can't fork\n");
		exit(-1);
	
	case 0 : // this is the code the child runs 
		close(0); // close stdin
		dup (masterToSlavePipe[0]); // set stdin to read from pipe
		close (masterToSlavePipe[1]);

		close(1);	 // close stdout
		dup (slaveToMasterPipe[1]); // points pipefd at file descriptor
		close (slaveToMasterPipe[0]);

// Set buffering
		setvbuf(stdout, 0, _IOLBF, 0); // stdout must not have full buffering.
	
// Hello.c:
// Works in same program
		sleep(1);
		fgets(str,100,stdin);
		printf("Hello 1, I got: %s\n", str);
		sleep(2);
		fgets(str,100,stdin);
		printf("Hello 2, I got: %s\n", str);
// Fails after exec:
//			execl ("hello","hello",0);
		break;

	default: // this is the code the parent runs
		close (slaveToMasterPipe[1]); // We don't write to incoming channel
		close (masterToSlavePipe[0]); // We don't read from outgoing channel

		// Now read from the pipe
		printf("Now sending Pling!\n");
		WriteMessage("Pling!\n");
		sleep(2);
		ReadMessage(str);
		printf("Reply: %s\n", str);

		printf("Now sending Plong!\n");
		WriteMessage("Plong!\n");
		sleep(2);
		ReadMessage(str);
		printf("Reply: %s\n", str);

		died= wait(&status);
	}
}

som jobbar mot den här:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>


int main(int argc, char *argv[])
{
	char str[1024], *cp;
	
	sleep(1);
	fgets(str,100,stdin);
	printf("Hello 1, I got: %s\n", str);
	sleep(2);
	fgets(str,100,stdin);
	printf("Hello 2, I got: %s\n", str);
}

Det översta programmet fungerar perfekt som det står, för det anropar aldrig det nerdre utan kör samma kod inom sig själv, men kommenterar man bort hello-koden och avkommenterar exec så blir det fel, då kommer allt i ett schok på slutet, dv buffringen går fel igen. Någonting är kvar, men vad?

  • Medlem
  • Stockholm
  • 2007-04-02 14:14

Din child skriver buffrat (printf("Hello 1...). Detta buffras nog tills nästföljande fgets.

Prova med flush innan sleep.

Ja, den skriver buffrat, men det gör ju minsta lilla Hello World, det skall funka ändå hade jag tänkt mig. Felet är tydligen att setvbuf-inställningen inte överlever en exec. Usch, att en så enkel sak skall vara så komplicerad.

Jag hackade vidare, och kom in på pty, pseudo-terminaler, som verkade vara en lösning. Och det är det - nästan!

Nu kan jag anropa ett annat program och både skicka och ta emot information utan att buffringen kommer i vägen. Frid och fröjd - om inte det anropade programmet är ett GUI-program! Då pajar det, då får jag "RegisterProcess failed (error = -50)". Känns det igen? Detta händer alltså inte förrän efter att det andra programmet startat, när man initierar GUIt, dvs efter exec. Jag har sett varningar om att fork utan exec kan gå snett i OSX, men det är inte det problemet.

1
Bevaka tråden