11 Replies - 1703 Views - Last Post: 10 December 2012 - 01:22 PM Rate Topic: -----

#1 Jiro_  Icon User is offline

  • D.I.C Head

Reputation: 3
  • View blog
  • Posts: 66
  • Joined: 27-August 12

start second binary without fork()

Posted 05 December 2012 - 03:05 AM

I am creating a simple linux system and I would need to start a second binary, but due the simplicity of the system at the moment of execution, I can't be using the system() call. also for what I understood, the fork function starts the same binary, but in a different process (and beginning from the fork call etc) but I want to start a second binary, with different code...

sources: forkng

This post has been edited by Jiro_: 05 December 2012 - 03:05 AM


Is This A Good Question/Topic? 0
  • +

Replies To: start second binary without fork()

#2 AKMafia001  Icon User is offline

  • </code.in.dream>

Reputation: 187
  • View blog
  • Posts: 625
  • Joined: 11-June 11

Re: start second binary without fork()

Posted 05 December 2012 - 06:33 AM

fork() spawns a new child process which is similar to the parent process except it has a different process ID. On success it returns 0 to the child process and a process ID of the child to the parent process...

So, we can have something like this:
pid_t pID = fork();
if (pID == 0)                // child
{
      // Code only executed by child process
}



If 0 is returned, it means its child process... So, the block of code will be executed by the child process only...

So, you can have some code to be executed by child process or you can use exec() which replaces the current process image with a new process image...
Was This Post Helpful? 2
  • +
  • -

#3 jjl  Icon User is offline

  • Engineer
  • member icon

Reputation: 1112
  • View blog
  • Posts: 4,619
  • Joined: 09-June 09

Re: start second binary without fork()

Posted 05 December 2012 - 02:20 PM

If you are going to be firing off a child process, you should have the parent wait for the child to finish before killing the parent (you don't want to have any zombie children :))

if((childPID = fork()) < 0)
   fprintf(stderr, "Bad Fork");
else if(childPid == 0) {
   execl("echo", "Hello World", 0);
   fprintf(stderr, "Bad Exec"); //should never get here
}
printf("Child %d is done.\n", wait3(NULL, 0, NULL));


Was This Post Helpful? 1
  • +
  • -

#4 AKMafia001  Icon User is offline

  • </code.in.dream>

Reputation: 187
  • View blog
  • Posts: 625
  • Joined: 11-June 11

Re: start second binary without fork()

Posted 05 December 2012 - 02:39 PM

Yes @jjl, the parent goes to sleep by calling wait() and when the child is done it wakes up the parent by calling exit()...

If the parent is done before the child, then the child is zombie... ;) The init process gets it over then...
Was This Post Helpful? 0
  • +
  • -

#5 vividexstance  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 687
  • View blog
  • Posts: 2,377
  • Joined: 31-December 10

Re: start second binary without fork()

Posted 06 December 2012 - 07:33 AM

You can use one of the exec() functions to run a different program. Have a look at the man pages for the details because there are 6 exec functions. Here's a simple example that runs the 'printenv' program:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
	printf("Initial value of USER: \"%s\"\n", getenv("USER"));
	
	if(putenv("USER=vivid") != 0)
	{
		fprintf("Error: putenv failed\n");
                return EXIT_FAILURE;
	}
	
	execl("/usr/bin/printenv", "printenv", "USER", "SHELL", (char *) NULL);
	
	/* If we get here, something went wrong */
	fprintf(stderr, "Error: execl\n");
        return EXIT_FAILURE;
}


This post has been edited by vividexstance: 06 December 2012 - 07:34 AM

Was This Post Helpful? 1
  • +
  • -

#6 Jiro_  Icon User is offline

  • D.I.C Head

Reputation: 3
  • View blog
  • Posts: 66
  • Joined: 27-August 12

Re: start second binary without fork()

Posted 06 December 2012 - 03:25 PM

Thanks guys im trying this asap!
Was This Post Helpful? 0
  • +
  • -

#7 Jiro_  Icon User is offline

  • D.I.C Head

Reputation: 3
  • View blog
  • Posts: 66
  • Joined: 27-August 12

Re: start second binary without fork()

Posted 07 December 2012 - 10:45 AM

View Postvividexstance, on 06 December 2012 - 07:33 AM, said:

You can use one of the exec() functions to run a different program. Have a look at the man pages for the details because there are 6 exec functions. Here's a simple example that runs the 'printenv' program:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
	printf("Initial value of USER: \"%s\"\n", getenv("USER"));
	
	if(putenv("USER=vivid") != 0)
	{
		fprintf("Error: putenv failed\n");
                return EXIT_FAILURE;
	}
	
	execl("/usr/bin/printenv", "printenv", "USER", "SHELL", (char *) NULL);
	
	/* If we get here, something went wrong */
	fprintf(stderr, "Error: execl\n");
        return EXIT_FAILURE;
}


I gave this a shot, and it works, but the problem is that this also shuts down the parent program. isn't there a manner to keep the parent in the background waiting for the exit of the child?
something like this:
//execute some code
start child
wait for child to exit
//do some more code


Was This Post Helpful? 0
  • +
  • -

#8 vividexstance  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 687
  • View blog
  • Posts: 2,377
  • Joined: 31-December 10

Re: start second binary without fork()

Posted 07 December 2012 - 11:13 AM

Yes there is, you have to fork(), and in the child you exec*() and in the parent you use one of the wait*() system calls. Here is an example of a simple way to implement the system() syscall from the "The Linux Programming Interface"(the example also does some signal handling:
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int system(const char *command)
{
	int status, savedErrno;
	pid_t childPid;
	sigset_t blockMask, origMask;
	struct sigaction saIgnore, saOrigQuit, saOrigInt, saDefault;
	
	/* (1) Is shell available? */
	if(command == NULL)
	{
		return system(":") == 0;
	}
	
	/* Block SIGCHLD */
	sigemptyset(&blockMask);
	sigaddset(&blockMask, SIGCHLD);
	
	/* (2) Only block SIGCHLD in the parent process */
	sigprocmask(SIG_BLOCK, &blockMask, &origMask);
	
	/* Ignore SIGINT and SIGQUIT */
	saIgnore.sa_handler = SIG_IGN;
	saIgnore.sa_flags = 0;
	sigemptyset(&saIgnore.sa_mask);
	
	/* (3) SIGINT and SIGQUIT need to be ignored in the parent process */
	sigaction(SIGINT, &saIgnore, &saOrigInt);
	sigaction(SIGQUIT, &saIgnore, &saOrigQuit);
	
	switch(childPid = fork())
	{
		/* Error: fork failed */
		case -1:
			status = -1;
			/* Carry on to reset signal attributes */
			break;
		
		/* Child: exec command */
		case 0:
			saDefault.sa_handler = SIG_DFL;
			saDefault.sa_flags = 0;
			sigemptyset(&saDefault.sa_mask);
			
			/* (4) If SIGINT or SIGQUIT are ignored, reset them to SIG_DFL */
			if(saOrigInt.sa_handler != SIG_IGN)
			{
				sigaction(SIGINT, &saDefault, NULL);
			}
			
			if(saOrigQuit.sa_handler != SIG_IGN)
			{
				sigaction(SIGQUIT, &saDefault, NULL);
			}
			
			/* (5) No error checking is performed */
			sigprocmask(SIG_SETMASK, &origMask, NULL);
			
			execl("/bin/sh", "sh", "-c", command, (char *) NULL);
			
			/* (6) We could not exec the shell */
			_exit(127);
		
		/* Parent: wait for our child to terminate */
		default:
			/* (7) Wait specifically for the child we created */
			while(waitpid(childPid, &status, 0) == -1)
			{
				/* If error other than EINTR, then exit loop */
				if(errno != EINTR)
				{
					status = -1;
					break;
				}
			}
			
			break;
	}
	
	/* Unblock SIGCHLD, restore dispositions of SIGINT and SIGQUIT */
	
	/* (8) The following may change 'errno', so save it */
	savedErrno = errno;
	
	/* (9) */
	sigprocmask(SIG_SETMASK, &origMask, NULL);
	sigaction(SIGINT, &saOrigInt, NULL);
	sigaction(SIGQUIT, &saOrigQuit, NULL);
	
	/* (10) Restore errno */
	errno = savedErrno;
	
	return status;
}


This post has been edited by vividexstance: 07 December 2012 - 11:14 AM

Was This Post Helpful? 1
  • +
  • -

#9 Jiro_  Icon User is offline

  • D.I.C Head

Reputation: 3
  • View blog
  • Posts: 66
  • Joined: 27-August 12

Re: start second binary without fork()

Posted 07 December 2012 - 12:13 PM

thanks a lot! I'm using a simplyfied solution, but strongly based on yours (I didn't completely got yours so I decided not to use it)
my function now looks like this:
int shell::system(const char *command, const char* arg)
{
    int PID,exitStatus;
    PID = fork(); //start the forking
    if(PID == 0)
    {
        //child process, here we will execute the new program
        execl(command,arg);
    }
    else
    {
        //parent process, gotta wait for completion here
        pid_t stat = waitpid(PID,&exitStatus,0);
        if(WIFEXITED(stat)) //something went wrong...
        {
            syntax_error(string(command));
            argument_error(string(arg));
        }
    }
    return exitStatus;
}


thanks a lot for sharing this with me
Was This Post Helpful? 0
  • +
  • -

#10 vividexstance  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 687
  • View blog
  • Posts: 2,377
  • Joined: 31-December 10

Re: start second binary without fork()

Posted 07 December 2012 - 02:07 PM

I still recommend having a look at least the man pages for the functions you are using. Do you know about man pages?

What about the other code didn't you understand, the signals part?
Was This Post Helpful? 0
  • +
  • -

#11 Jiro_  Icon User is offline

  • D.I.C Head

Reputation: 3
  • View blog
  • Posts: 66
  • Joined: 27-August 12

Re: start second binary without fork()

Posted 10 December 2012 - 12:43 PM

basicly everything that isn't in the code I am using at the moment...
Was This Post Helpful? 0
  • +
  • -

#12 vividexstance  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 687
  • View blog
  • Posts: 2,377
  • Joined: 31-December 10

Re: start second binary without fork()

Posted 10 December 2012 - 01:22 PM

Most of the code I posted deals with signal handling because the system() syscall is supposed to do some signal handling. One thing you really should do, is to check the return values from system calls like fork(). Most likely they won't fail, but there is a chance they will. In your code, if fork() fails, execution goes into the else part of your if-statement and you "wait" for a process that doesn't exist because if fork() fails, it returns -1. So PID == -1, calling the waitpid() function with these arguments: waitpid(-1, &exitStatus, 0) is equivalent to calling the regular wait(&exitStatus) system call. Not always, but the typical way to deal with forking a process is to put it in a switch-statement:
pid_t pid;

switch(pid = fork())
{
    /* Deal with error */
    case -1:
        perror("fork");
        exit(EXIT_FAILURE);
    
    /* Deal with child */
    case 0:
        /* Do something in child */
        _exit(EXIT_SUCCESS);
    
    /* Parent */
    default:
        /* You can put code here or just call break to fall through */
        break;
}

/* The parent's code could go here */


Note the use of _exit() in the child. The C library function exit() actually calls _exit() to exit after it does some clean-up work and calling any functions that were registered to run at exit (exit handlers). By calling _exit(), that extra work isn't done in the child, only in the parent process. Note that if you just use a return 0; statement in main(), that is equivalent to calling exit(0).

This post has been edited by vividexstance: 10 December 2012 - 01:26 PM

Was This Post Helpful? 1
  • +
  • -

Page 1 of 1