Process

A process is an instance of an executing program. Many processes may be running the same program.

Program

One program may be used to construct many processes. A program is a file containing a range of information that describes how to con- struct a process at run time. 那么程序包含了以下信息:

  • Binary format identification
  • Machine-language instructions

    (encode the algorithm of the program)

  • Program entry-point address
  • Data
  • Shared-library and dynamic-linking information
  • Other information

Memory Layout of Process

The memory allocated to each process is composed of a number of parts, usually referred to as segments. The segments are list in the following:

The Memory Layout of Process

char globBuf[65536];  //uninitialized data segment
int primes[] = {2, 3, 5, 7}; //intialized data segment

int square(int x){        // Allocated in stack frame for square()
    int result;           
    result = x*x;    
    return result;        // Return value passed via register
}

int main(int argc, char *argc[]){ // stack frame for main() 
    char *p;    // stack frame for main(), wild pointer

    p = malloc(1024);    // Points to memory in heap segment, not wild pointer anymore

    exit(EXIT_SUCCESS);
}

PID and getpid()

Each process has a process ID (PID), a positive integer that uniquely identifies the process on the system. Process IDs are used and returned by a variety of system calls(参见Appendix).

pid_t getpid(void);    //pid_t is a type, can be interpreted to int

This call Always successfully returns process ID of caller

pid_t getppid(void);

Returns process ID of parent of caller

fork()

fork() is used to create new process.

The fork() system call creates a new process, the child, which is an almost exact duplicate of the calling process, the parent.

\*
- In parent: returns process ID of child on success, or –1 on error;
- In successfully created child: always returns 0
*\
pid_t fork(void);

After the fork(), each process can modify the variables in its stack, data, and heap segments without affecting the other process.

int main(int argc, char* argv[]){
    printf("I am : %d \n", (int) getpid());
    pid_t pid = fork();
    printf("fork returned: %d \n", (int) pid);

    if (pid < 0) {
        perror ("Fork Failed");
    }
    if (pid == 0) {
        printf("I am the child with pid %d\n", (int) getpid());
        exit(0)
    }
    else if (pid > 0 ){
        printf("I am the parent : %d \n", (int) getpid() );
    }
    return 0;
}

NOTE: Although stack, data, and heap segments are freshed, the child shares of all of the parent’s file descriptors (in text segment) [Kernel 把这些部分标记成read-only]

Race Condition after fork()

After a fork(), it is indeterminate which process—the parent or the child—next has access to the CPU. (On a multiprocessor system, they may both simultaneously get access to a CPU.)

The example in the code segment mentioned above may have racing. To solve the race condition, we can let the active process can send a signal after completing the action; the other process waits for the signal.

int main(int argc, char* argv[]){
    printf("I am : %d \n", (int) getpid());
    pid_t pid = fork();
    TELL_WAIT();
    printf("fork returned: %d \n", (int) pid);

    if (pid < 0) {
        perror ("Fork Failed");
    }
    if (pid == 0) {
        WAIT_PARENT();  \\parent first
        printf("I am the child with pid %d\n", (int) getpid());
        exit(0)
    }
    else if (pid > 0 ){
        printf("I am the parent : %d \n", (int) getpid() );
        TELL_CHILD(pid); \\ tell child finished
    }
    return 0;
}

wait()

wait(&status) system call has two purposes.

pid_t wait(int *status); // Returns process ID of terminated child, or –1 on error
  • First, if a child of this process has not yet terminated by calling exit(), then wait() suspends execution of the process until one of its children has terminated.

等娃儿结束

  • Second, the termination status of the child is returned in the status argument of wait()

传娃儿结束的状态 (可以查看exit()传的状态)

void doSomeWork(char *name){
    const int NUM_TIMES = 5;
    for (int i  = 0; i < NUM_TIMES; i++) {
        sleep(rand() % 4);
        printf("Done Work in %dth loop for %s \n", i, name);
    }
}

void main(int argc, char* argv[]){
    printf("I am : %d \n", (int) getpid());

    // To check process in terminal -ps -a 
    //sleep(5);
    pid_t pid = fork();

    srand((int) pid); // for rand() 

    if (pid == 0) {
        printf("I am the child with pid %d\n", (int) getpid());
        doSomeWork("Child");
        // wait 233
        exit(233);
    } 

    printf("I am the parent, waiting for child to end\n");
    doSomeWork("Parent");
    // the wait() function returns child pid
    pid_t childpid = wait(NULL);
    printf("Parent Knows the child %d finished\n", (int) childpid);

    int status = 0;
    pid_t childpid = wait(&status);
    printf("Parent Knows the child %d finished with status %d\n", (int) childpid, status);
    // check the value passed by exit() -- 233
    int childReturnValue = WEXITSTATUS(status);
    printf("    Return Value was %d\n", (int) childReturnValue);
}

exec()

exec() is a set of a bunch of functions. They generally loads a new program into a process's memory, The existing program text is discarded, and the stack, data, and heap segments are freshly created for the new program. This enables a the processes generated by fork() to execute different functions.

  • execve(pathname, argv, envp) system call loads a new program (pathname, with argument list argv, and environment list envp) into a process's memory.

NOTE: Although stack, data, and heap segments are freshed, the child shares of all of the parent’s file descriptors (in text segment) [Kernel 把这些部分标记成read-only]

exit()

a process can terminate normally, using the _exit() or more generally exit() system call

void exit(int status); // termination status of the process can pass to wait()

results matching ""

    No results matching ""