Thread: System programming - Absolute beginner confused

  1. #1
    SAMARAS std10093's Avatar
    Join Date
    Jan 2011
    Location
    Nice, France
    Posts
    2,694

    System programming - Absolute beginner confused

    So we had our first touch with system(), fork(), execvp(), and wait() .

    I can not distinguish the difference between system() and execvp() :/

    So let give two pieces of code that use them and produce similar output

    execvp
    Code:
    #include <stdio.h>
     #include <unistd.h>
     #include <string.h>
     int main() {
     char program[80],*args[3];
     int i;
     printf("Ready to exec()...\n");
     strcpy(program,"date");
     args[0]="date";
     args[1]="-u";
     args[2]=NULL;
     i=execvp(program,args);
     printf("i=%d did not work!\n",i);
     return 0;
     }
    outputs
    Code:
    Macintosh-c8bcc88e5669-9:~ usi$ ./bla
    Ready to exec()...
    Thu Nov 22 11:51:31 UTC 2012
    Macintosh-c8bcc88e5669-9:~ usi$
    system
    Code:
    #include <stdio.h>
    
     #include <stdlib.h>
     #include <unistd.h>
     int main() {
     char text[80];
     printf("Ready to system()...\n");
     sprintf(text,"date -u");
     system(text);
     printf("Line after system call.\n");
     return 0;
     }
    output
    Code:
    Macintosh-c8bcc88e5669-9:~ usi$ ./px
    Ready to system()...
    Thu Nov 22 11:52:58 UTC 2012
    Line after system call.
    Macintosh-c8bcc88e5669-9:~ usi$
    The difference I spotted is that with execvp , it will not go to the next line of code..So what happens?
    I read the documentation and said that it will open a new image (something like that) ..
    But it is not at clear to me.What is the effect of the function?system will output the same result of date but then it will go to the next line of code....Why?
    Maybe something with wait function happens internally of system....?


    I can say that system actually may call internally fork, wait and execvp because our homework is to write a function that behaves equivalently to system and the skeleton is this
    Code:
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/wait.h>
    #include <string.h>
    #include <assert.h>
    #include <stdlib.h>
    
    void fork_snippet() {
    	int i;
    	if ((i=fork()) == 0) { // child code
    	} else { // parent code
    		assert(i!=-1);
    	}
    }
    
    void wait_snippet() {
    	int cpid=wait(NULL);
    	assert(cpip!=-1);
    }
    
    void exec_snippet() {
    	char *args[] = {"date","-u",NULL};
    	assert(execvp(args[0],args)!=-1);
    }
    
    void strsep_snippet() {
    	char *token, *string, *tofree;
    	tofree = string = strdup("abc,def,ghi");
    	assert(string != NULL);
    	while ((token = strsep(&string, ",")) != NULL)
    		printf("%s\n", token);
    	free(tofree);
    }

    Every input is precious . I have 0 experience with system calls so if you want to write a big explanation do not hesitate to do so

    Tip : Remember, if someone has good knowledge of something, he can explain it in a simple way..

  2. #2
    Registered User
    Join Date
    Sep 2012
    Posts
    357
    You might want to google "fork exec".

    What happens with exec is that the currently executing program is replaced by another one.
    What happens with system is that a new process is started where the new program runs. The old process does not terminate.

    Using fork allows your own program to divide in two. You can keep one of the processes doing it's thing and replace the other one with a different process.

  3. #3
    SAMARAS std10093's Avatar
    Join Date
    Jan 2011
    Location
    Nice, France
    Posts
    2,694
    Thanks for the reply.

    Well i know google too :P What links i tried where not enough clear to me, otherwise why i should post?

    -->What happens with exec is that the currently executing program is replaced by another one.
    What do you mean "by another one"? Which one?

    -->What happens with system is that a new process is started where the new program runs. The old process does not terminate.
    The new program???

    Please try to be more exact and clear.Excuse me but i am really confused.

  4. #4
    Registered User
    Join Date
    Sep 2012
    Posts
    357
    I just pointed a new keyword relevant to your problems: "fork exec". You haven't used fork in your code, but fork and exec often go together. I thought maybe reading a few pages and fork and exec work together would help you.

    Ok, then ...

    Basically when you execute a program you start one process. So when you start your first program, the Operating System loads the binary code from disk into a memory region, does the things it needs to, and transfers control to your main() function: your process is now running.

    Inside your process there is a statement to load a different program from disk (namely date) and execute that new binary code. What the Operating System does is replace the binary code of your program with the binary code of date and transfer control to the start function of date. Your process no longer exists in memory.

    With system() what the Operating System does is starting a new process (first the shell, then, by replacement, date) that occupies a different memory region. Both processes are present in memory at the same time (your program is waiting for the new process though).

  5. #5
    SAMARAS std10093's Avatar
    Join Date
    Jan 2011
    Location
    Nice, France
    Posts
    2,694
    I think you are right about the google help. I am going to read from there and post back what i understood.
    Thank you gny!

  6. #6
    SAMARAS std10093's Avatar
    Join Date
    Jan 2011
    Location
    Nice, France
    Posts
    2,694
    So let's see what i learned
    I tried to implement the homework ( build a function that works as function system)
    Code:
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/wait.h>
    #include <string.h>
    #include <assert.h>
    #include <stdlib.h>
    
    #define MAXARGS 100
    
    void my_strsep(char**tokenized , char* cmd) {
    
            int i = 0;
            int NoOfArgs = 0;
    
            while(cmd[i] != '\0')
            {
                    if(cmd[i++] == ' ')
                       NoOfArgs++;
            }
            if(++NoOfArgs > MAXARGS)
            {
                printf("Too many arguments.Max arguments number is %d\n",MAXARGS);
                exit(0);
            }
    
    printf("test %d\n",NoOfArgs);
    
            char* string;
            string = strdup(cmd);
            for( i = 0 ; i < NoOfArgs ; i++ )
                tokenized[i] = strsep(&string, " ");
            tokenized[i] = NULL;
    
            free(string);
    }
    
    void my_fork(char**tokenized)
    {
            int i;
            if ((i=fork()) == 0) {// child code
                    execvp(tokenized[0] , tokenized);
            } else {// parent code
                    assert(i!=-1);
                    wait(NULL);
            }
    }
    
    void my_system(char* cmd)
    {
         char* tokenized[MAXARGS];
    
         my_strsep(tokenized , cmd);
    
         my_fork(tokenized);
    
         printf("End of my_system\n");
    }
    
    
    int main(void)
    {
          my_system("uname -a");
    
          return 0;
    }
    But when i run it it will only output the printf's i have inside the code..I won't see what I see when I type in the terminal
    Code:
    uname -a
    See by yourselves
    Code:
    Macintosh-c8bcc88e5669-9:hw8 usi$ ./my_system
    test1
    End of my_system
    End of my_system
    Macintosh-c8bcc88e5669-9:hw8 usi$ uname -a
    Darwin Macintosh-c8bcc88e5669-9.local 11.4.2 Darwin Kernel Version 11.4.2: Thu Aug 23 16:25:48 PDT 2012; root:xnu-1699.32.7~1/RELEASE_X86_64 x86_64
    Macintosh-c8bcc88e5669-9:hw8 usi$
    What I am missing here?

    I replaced execv with execvp and I got the output...What happened is that execvp search in the environment to find it..

    I edited the code above!

    The memory management is wrong.Will fix it and edit again.

    Last EDIT : Will post instead of edit.
    Last edited by std10093; 11-23-2012 at 08:28 AM.

  7. #7
    SAMARAS std10093's Avatar
    Join Date
    Jan 2011
    Location
    Nice, France
    Posts
    2,694
    So, I think it is ok now.But I have a doubt for the memory management.Is it OK?

    Code:
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/wait.h>
    #include <string.h>
    #include <assert.h>
    #include <stdlib.h>
    
    #define MAXARGS 100
    
    /* It will copy the contents of the string in the array. */
    /* Returns a pointer that must be free'ed after usage of */
    /* array is done.                                        */
    char* my_strsep(char**tokenized , char* cmd) {
    
            int i = 0;
            int NoOfArgs = 0;
    
            while(cmd[i] != '\0')
            {
                    if(cmd[i++] == ' ')
                       NoOfArgs++;
            }
            if(++NoOfArgs > MAXARGS)
            {
                printf("Too many arguments.Max arguments number is %d\n",MAXARGS);
                exit(0);
            }
    
            printf("test %d\n",NoOfArgs);
    
            char* string;
            char* toFree;
            string = strdup(cmd);
            toFree = string;
            for( i = 0 ; i < NoOfArgs ; i++ )
                tokenized[i] = strsep(&string, " ");
            tokenized[i] = NULL;
    
            return toFree;
    }
    
    void my_fork(char**tokenized)
    {
            int i , error;
            if ((i=fork()) == 0) {// child code
                    error = execvp(tokenized[0] , tokenized);
                    if(error == -1)
                    {
                            printf("execvp continued!!!\n");
                            exit(0);
                    }
            } else {// parent code
                    if(i == -1)
                    {
                            printf("ERROR in fork\n");
                            exit(0);
                    }
                    wait(NULL);
            }
    }
    
    void my_system(char* cmd)
    {
         char* tokenized[MAXARGS];
         char* toFree;
    
         toFree = my_strsep(tokenized , cmd);
    
         my_fork(tokenized);
    
         free(toFree);
    
         printf("End of my_system\n");
    }
    
    int main(void)
    {
            my_system("unadhfdsgfgme -a");
    
            printf("\n\n");
    
            my_system("uname -a");
    
            return 0;
    }

  8. #8
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    std10093, there is no need to tokenise the command string, if you want to write your own replacement for system().

    The system(cmd) function forks a child process, then executes
    Code:
        char *args[4] = { "sh", "-c", cmd, NULL };
        execv("/bin/sh", args);
    in the child process. It's described in the man 3 system man page, too.

    Tokenising is a bit complicated to do right -- if you do it, you should probably support quotes and basic backslash escapes -- but in typical cases it is better to let the shell worry about those.

  9. #9
    SAMARAS std10093's Avatar
    Join Date
    Jan 2011
    Location
    Nice, France
    Posts
    2,694
    I have to do it with tokenizing because it is the requirement of the homework.I also need to consider only spaces and not quotes etc.

    Where you probably are right i have to keep this approach.

    So the question remains.Do i handle my memory well?

  10. #10
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by std10093 View Post
    I have to do it with tokenizing
    Fair enough.

    Quote Originally Posted by std10093 View Post
    So the question remains.Do i handle my memory well?
    I don't see anything wrong with your memory management; I don't think you leak any memory.

    You do occasionally overcount the number of arguments in the string (since you don't skip multiple successive spaces in your token counting loop), causing it to fail when the string contains too many spaces (because it thinks there are too many tokens in the string, instead of realizing there are just extra consecutive spaces the strsep() will skip). Everything else is, I think, matters of style.

    However, it is Friday, and I'm not really in the mood for bug-hunting, so I may have missed something.

    The rest of this message is an advanced topic, not related to your current exercise:



    Since you are using dynamic memory management, you could modify your tokenizer to return a dynamically allocated array of tokens:
    Code:
    char **tokenize(const char *const command, const char *const separators)
    {
        size_t  tokens = 0;
        size_t  length;
        char   *string;
        char **token;
        size_t  i, n;
    
        const char *cmd, *p;
    
        /* NULL pointers? Empty strings? */
        if (!command || !separators || !*command || !*separators)
            return NULL;
    
        /* Skip leading separators. */
        cmd = command + strspn(command, separators);
    
        /* Save the length of the command;
         * that's how much we need for the char data (+1 for '\0') */
        length = strlen(cmd);
    
        /* No command at all? */
        if (length < 1)
            return NULL;
    
        /* Count the number of tokens */
        p = cmd;
        while (*p) {
            tokens++;
    
            /* Skip the token, */
            p += strcspn(p, separators);
    
            /* and all separators that follow it. */
            p += strspn(p, separators);
        }
    
        /* Allocate memory for the pointers, NULL, the data, and a trailing '\0'. */
        string = malloc((tokens + 1) * sizeof (char *) + length + 1);
    
        /* The pointer we return is the pointer that has to be free()d. */
        token = (char **)string;
    
        /* The character data follows the pointers. */
        string += (tokens + 1) * sizeof (char *);
    
        /* Loop until all of the command has been processed. */
        for (i = 0; i < tokens; i++) {
    
            /* The token starts here. */
            token[i] = string;
    
            /* Length of token to copy. */
            n = strcspn(cmd, separators);
    
            /* Copy the data, if any, */
            if (n > 0)
                memcpy(string, cmd, n);
    
            /* add the trailing '\0' for this token, */
            string[n] = '\0';
            string += n + 1;
    
            /* and skip the token and trailing separators in the command. */
            cmd += n;
            cmd += strspn(cmd, separators);
        }
    
        /* End the array of tokens with a NULL. */
        token[tokens] = NULL;
    
        /* Return the entire array. */
        return token;
    }
    This avoids any limit to the number of tokens or their length, except for available memory.

    The difference to your my_strsep() function is that this one allocates a memory area dynamically, and puts both the pointer array and the character data into it. The pointer the function returns is both the one you need to free() afterwards, and the NULL-terminated array of tokens.

    Many programmers dislike this approach, because they don't trust the pointer arithmetic (done to use a single allocated memory block for both the pointers and the data they point to). This particular case is quite safe, because the pointer array is always properly aligned (because malloc() is guaranteed to return a properly aligned pointer for any type), and strings have no alignment restrictions.

    For more complex cases of packing different types of data into a single memory block, you must be extra careful, and add padding between data types to make sure each part is properly aligned. For example, on some 32-bit architectures double requires 8-byte alignment, whereas pointers just 4-bytes.

    That may sound scary, but it is not. This macro
    Code:
    #define ALIGNOF(type) ((sizeof (type) < 1) ? 1 : offsetof(struct { char dummy; type target; }, target))
    gives you the alignment required (in chars) for any type (for function pointer types, use typedef'd function pointer type name). When allocating, you simply allocate extra ALIGNOF(type) bytes for each section, and adjust the start of the section pointer using start = align(start, ALIGNOF(type));
    Code:
    void *align(void *const ptr, const unsigned int alignment)
    {
        if ((unsigned int)p % alignment))
            return (void *)((char *)p + alignment - ((unsigned int)p % alignment));
        else
            return ptr;
    }
    Again, this is just intended as an informative/instructional extension to your function. Or, more honestly, because I'm bored on a Friday.

    My coding style differs quite a bit, but you should see how the two functions are really quite closely related, aside from the dynamic memory management (and the fact that I chose to take the list of acceptable separators as a string).


  11. #11
    Registered User
    Join Date
    Nov 2012
    Posts
    17
    If execve() does not fail the man tells you that the data, bss and stack are freed as well as any memory allocation based on mmap(). Malloc() aggregates small memory requests into larger brk() calls. For very large requests, malloc() uses the mmap() system call to find addressable memory space.
    Even if the man does not tell it, you can look in /proc/yourprocesspid/maps to see the sizes of heap. If you put your program in pause before execve() you can see before, and the executed program can also start by pause() to check the new configuration.
    You cannot use valgrind because it restarts after execve().

    However, I would try to free the memory if execve() fails.

    You are freeing in the parent, but not in the child which might want to continue if execve () fails . Each page allocated for the parent process ( the heap as well) is copied (i.e. set to copy-on-write) for the forked process to be able to access it. When the parent frees it, the child does not.



  12. #12
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by Andreea View Post
    However, I would try to free the memory if execve() fails.

    You are freeing in the parent, but not in the child which might want to continue if execve () fails.
    No, exiting the child process as shown is the robust approach in this particular case.

    I'd only use a specific exit status, perhaps 127, which is not normally returned by any process (the 0 used by std10093 means successful exit in POSIX systems), so that the parent process can use the child process exit status to detect that the child process failed to execute the command.

    I don't see any use case in a system()-like function, where it would make sense for the child process to continue existing. Either exec() or exit(), I say.

  13. #13
    Registered User
    Join Date
    Nov 2012
    Posts
    17
    Quote Originally Posted by Nominal Animal View Post
    No, exiting the child process as shown is the robust approach in this particular case.

    I'd only use a specific exit status, perhaps 127, which is not normally returned by any process (the 0 used by std10093 means successful exit in POSIX systems), so that the parent process can use the child process exit status to detect that the child process failed to execute the command.

    I don't see any use case in a system()-like function, where it would make sense for the child process to continue existing. Either exec() or exit(), I say.
    Because no system wrapper was provided, once a system call fails, the proper approach is to analyze errno (the global variable) in which the error is saved. You do not want to quit before. Why? Because errno is a global variable containing the code of the last system called that failed. Any other system call failing before you get the chance to analyze it , it ruins your chances of an analysis.

    With respect to execve () the man gives you a lot of possible errno codes. Among them a shell usually tells you (EACCES) when you try to execute a non-executable file, (ELOOP) too many symbolic links, (EMFILE) the process attained its maximum number of open files, (ENOENT) the file does not exist ...

    An exit status chosen by the user cannot convey such information.

  14. #14
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Since the bulk of discussion in this thread has been highly specific to unix (specifically linux) operating systems I suggest that this thread be moved out of the C programming forum.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  15. #15
    SAMARAS std10093's Avatar
    Join Date
    Jan 2011
    Location
    Nice, France
    Posts
    2,694
    Thanks all for the replies.I wasn't able to study them, but i will for sure
    Quote Originally Posted by grumpy View Post
    Since the bulk of discussion in this thread has been highly specific to unix (specifically linux) operating systems I suggest that this thread be moved out of the C programming forum.
    This is ok for me.I thought it was this the appropriate thread to post because I had code in C.But if you judge so, you may know something more

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 14
    Last Post: 12-06-2006, 04:58 PM
  2. Replies: 14
    Last Post: 12-04-2006, 05:16 PM
  3. Absolute beginner
    By cstalnak in forum Tech Board
    Replies: 19
    Last Post: 01-22-2006, 02:44 AM
  4. Windows programming for beginner (Absolute beginner)
    By WDT in forum Windows Programming
    Replies: 4
    Last Post: 01-06-2004, 11:21 AM
  5. Absolute Beginner's Guide to Programming
    By o0obruceleeo0o in forum C++ Programming
    Replies: 9
    Last Post: 04-01-2003, 03:20 PM