Linux C Programming Practice (III)-Process Control and Process Communication Programming

Source: Internet
Author: User
Tags random seed signal handler

 

C programming practices in Linux (III)

-Process Control and Process Communication Programming

 

1. Linux Process

A Linux Process contains three parts of data in the memory: code segment, stack segment, and data segment. The code segment stores the code of the program. Code segments can run multiple

Process sharing. The stack segment stores the return address of the subroutine (function), the parameters of the subroutine, and the local variables of the program. The data segment stores the global variables, constants, and dynamic data space allocated by the Program (for example, the memory applied by the malloc function ). Unlike code segments, if multiple identical programs run simultaneously in the system, they cannot use the same stack segment or data segment.

Linux processes are in the following states: User status (the state in which the process runs in the user State) and Kernel Status (the state in which the process runs in the kernel state), memory ready (the process is not executed, but in the ready state, as long as the kernel schedules it, it can be executed), memory sleep (the process is sleeping and in the memory, not switched to the SWAP device), ready, and switched out (the process is in the ready state, but it must be switched into the memory before the kernel can schedule it to run again), sleep and switch out (the process is sleeping and the memory is swapped out), is preemptive (when the process returns the user State from the kernel state, the kernel is preemptible to it, and implements context switching, after another process is scheduled, the process is in the preemptive status), created (the process has just been created and exists, but it is neither ready nor sleep, this state is the initial State of all processes except process 0) and the dead state (the process stops calling exit and the process no longer exists, but it is still recorded in the progress table, this record can be collected by the parent process ).

Next, we will explain the "Life and Death" of the state transition of a Linux Process from creation to extinction ".

(1) The process is created by the parent process through the system call fork and is in the Creation state;

(2) After the fork call configures the kernel data structure and the private data structure of the sub-process for the sub-process, the sub-process enters the ready state (or is ready in the memory, or the SWAP device is ready because the memory is not enough );

(3) if the process is ready in the memory, the process can be scheduled by the kernel scheduler to run on the CPU;

(4) The kernel schedules the process to enter the kernel state, and then returns the kernel state to the user State for execution. After a certain period of time, the process is scheduled by the scheduling program and enters the kernel state, and then enters the ready state. Sometimes, when a process is running in the user State, it also enters the kernel state by using the system call because the kernel service is required. After the service is completed, the kernel state is changed back to the user State. Note that a process may be preemptible when returning from the Kernel Status to the user status. This is because a process with a higher priority needs to use the CPU urgently and cannot wait until the next scheduling time, this leads to preemption;

(5) The process executes the exit Call and enters the frozen state, and ends.

2. Process Control

Process control mainly involves Process Creation, sleep, and exit. in Linux, the fork, exec, and clone process creation methods are provided, sleep process sleep and exit process exit call. In addition, Linux also provides the system call wait for the parent process to wait for the child process to end.

Fork

For those who have never been familiar with the Unix/Linux operating system, fork is one of the most difficult concepts to understand. It returns two values after one execution, which is completely "Incredible ". First look at the following program

:

Int main ()

{

Int I;

If (fork () = 0)

{

For (I = 1; I <3; I ++)

Printf ("This is child process" n ");

}

Else

{

For (I = 1; I <3; I ++)

Printf ("This is parent process" n ");

}

}

The execution result is:

This is child process

This is child process

This is parent process

This is parent process

In English, fork means "fork". This name is very vivid. A process is running. If fork is used, another process is generated, and the process is "Forked ".

. The current process is a parent process, and a child process is generated through fork. For the parent process, the fork function returns the process Number of the subroutine, and for the subroutine, the fork function returns zero, which is the essence of a function that returns twice. It can be said that the fork function is one of the most outstanding achievements of Unix systems. It is a long-term hard exploration of Unix developers in the early 1970s s in theory and practice.

Achievements made by Suo.

If we put the loop in the above program a little bigger:

Int main ()

{

Int I;

If (fork () = 0)

{

For (I = 1; I <10000; I ++)

Printf ("This is child process" n ");

}

Else

{

For (I = 1; I <10000; I ++)

Printf ("This is parent process" n ");

}

};

 

Then we can see the concurrent execution of the parent and child processes, and output "This is child process" and "This is parent process" in turn ".

At this moment, we have not fully understood the fork () function. Let's look at the following program to see how many processes are generated and what program output is?

Int main ()

{

Int I;

For (I = 0; I <2; I ++)

{

If (fork () = 0)

{

Printf ("This is child process" n ");

}

Else

{

Printf ("This is parent process" n ");

}

}

};

 

Exec

In Linux, you can use the exec function family, which contains multiple functions (execl, execlp, execle, execv, execve, and execvp) to start a process with a specified path and file name.

The features of the exec function family are as follows: Once a process calls the exec function, the program being executed is killed. The system replaces the code segment with a new program (executed by the exec function) and the original data segment and stack segment are also discarded. The new data segment and stack segment are allocated, but the process number is retained. That is to say, the exec execution result is: the system considers that the execution is still the original process, but the program corresponding to the process is replaced.

The fork function can create a sub-process and the current process does not die. If we call the exec function family in the fork sub-process, we can execute the code of the parent process and start a new one.

It is really amazing to specify a process. The combination of fork and exec cleverly solves the problem that the program starts the execution of another program but continues to run. See the following example:

Char command [MAX_CMD_LEN];

Void main ()

{

Int rtn;/* the return value of the sub-process */

While (1)

{

/* Read the command to be executed from the terminal */

Printf ("> ");

Fgets (command, MAX_CMD_LEN, stdin );

Command [strlen (command)-1] = 0;

If (fork () = 0)

{

/* The sub-process executes this command */

Execlp (command, command );

/* If the exec function returns, the command is not executed normally and the error message is printed */

Perror (command );

Exit (errorno );

}

Else

{

/* Parent process. Wait until the child process ends and print the return value of the child process */

Wait (& rtn );

Printf ("child process return % d" n ", rtn );

}

}

};

 

This function basically implements a shell function. It reads the process name and Parameters entered by the user and starts the corresponding process.

Clone

Clone is a new function provided after Linux2.0. It is more powerful than fork (you can think that fork is a part of clone implementation), so that the created sub-process can share the resources of the parent process and

To use this function, you must set the clone_actually_works_ OK option when compiling the kernel.

The clone function is prototype:

Int clone (int (* fn) (void *), void * child_stack, int flags, void * arg );

This function returns the PID of the created process. The flags flag in the function is used to set related options when creating a sub-process. The meanings are as follows:

Flag

Description

 

CLONE_PARENT

The parent process of the created child process is the caller's parent process. The new process and the process that created the child process become "brother" rather than "Parent and Child"

 

CLONE_FS

The sub-process shares the same file system with the parent process, including root, current directory, and umask.

 

CLONE_FILES

The sub-process shares the same file descriptor with the parent process.

 

CLONE_NEWNS

In the new namespace promoter process, namespace describes the process File hierarchy

 

CLONE_SIGHAND

The sub-process shares the same signal processing (signal handler) table with the parent process.

 

CLONE_PTRACE

If the parent process is traced, the child process is also traced.

 

CLONE_VFORK

The parent process is suspended until the child process releases virtual memory resources.

 

CLONE_VM

The sub-process and parent process run in the same memory space

 

CLONE_PID

The PID is consistent with the parent process when the child process is created.

 

CLONE_THREAD

Added to support POSIX thread standards in Linux 2.4. The sub-process shares the same thread group with the parent process.

 

Let's look at the following example:

Int variable, fd;

Int do_something (){

Variable = 42;

Close (fd );

_ Exit (0 );

}

 

Int main (int argc, char * argv []) {

Void ** child_stack;

Char tempch;

Variable = 9;

Fd = open ("test. file", O_RDONLY );

Child_stack = (void **) malloc (16384 );

Printf ("The variable was % d" n ", variable );

Clone (do_something, child_stack, CLONE_VM | CLONE_FILES, NULL );

Sleep (1);/* delay so that the sub-process can close the file and modify the variable */

Printf ("The variable is now % d" n ", variable );

If (read (fd, & tempch, 1) <1 ){

Perror ("File Read Error ");

Exit (1 );

}

Printf ("We cocould read from the file" n ");

Return 0;

}

Running output:

The variable is now 42

File Read Error

The output result of the program tells us that the sub-process closes the file and modifies the variable (the CLONE_VM and CLONE_FILES flags used for calling clone will make the variables and file descriptor tables

), The parent process will immediately feel it, which is the characteristic of clone.

Sleep

The function call sleep can be used to suspend a process for a specified number of seconds. The prototype of this function is:

Unsigned int sleep (unsigned int seconds );

This function call causes the process to suspend for a specified time. If the specified suspension time is reached, the call returns 0. If the function call is interrupted by the signal, the remaining suspension time is returned.

(The specified time minus the suspended time ).

Exit

The system calls the exit function to terminate the process. Its function prototype is:

Void _ exit (int status );

_ Exit will immediately terminate the calling process, and all file descriptors belonging to the process will be disabled. The status parameter is returned to the parent process as the exit status value.

Wait can obtain this value.

Wait

Wait system calls include:

Pid_t wait (int * status );

Pid_t waitpid (pid_t pid, int * status, int options );

The purpose of wait is to ensure that any sub-process that sends a call will sleep until one of them stops. The waitpid waits for the sub-process specified by the pid parameter to exit.

3. inter-process communication

In Linux, inter-process Communication (IPC) includes pipelines, message queues, shared memory, semaphores, and APIs.

Pipelines can be divided into famous and unknown pipelines. Nameless pipelines can only be used for communication between relatives' processes, while famous pipelines can be used between non-kinship processes.

# Define INPUT 0

# Define OUTPUT 1

Void main ()

{

Int file_descriptors [2];

/* Define the sub-process Number */

Pid_t pid;

Char buf [BUFFER_LEN];

Int returned_count;

/* Create an unknown MPs queue */

Pipe (file_descriptors );

/* Create a sub-process */

If (pid = fork () =-1)

{

Printf ("Error in fork" n ");

Exit (1 );

}

/* Execute the sub-process */

If (pid = 0)

{

Printf ("in the spawned (child) process..." n ");

/* The sub-process writes data to the parent process and closes the read end of the MPs queue */

Close (file_descriptors [INPUT]);

Write (file_descriptors [OUTPUT], "test data", strlen ("test data "));

Exit (0 );

}

Else

{

/* Execute the parent process */

Printf ("in the spawning (parent) process..." n ");

/* The parent process reads data written by the sub-process from the MPs queue and closes the data written by the MPs queue */

Close (file_descriptors [OUTPUT]);

Returned_count = read (file_descriptors [INPUT], buf, sizeof (buf ));

Printf ("% d bytes of data converted ed from spawned process: % s" n ",

Returned_count, buf );

}

}

In the above procedures

Int pipe (int filedis [2]);

Method definition. The filedis parameter returns two file descriptors, filedes [0], to open for read, and filedes [1] to open for write, output of filedes [1] is the input of filedes [0;

In Linux, a famous pipeline can be created in either of the following ways ):

(1) mkfifo ("kerberoexample", "rw ");

(2) mknod implements oexample p

Mkfifo is a function, and mknod is a system call, that is, we can output the above command in shell.

After a famous pipeline is created, we can read and write it like a read/write file:

/* Process 1: Read a famous Pipeline */

Void main ()

{

FILE * in_file;

Int count = 1;

Char buf [BUFFER_LEN];

In_file = fopen ("pipeexample", "r ");

If (in_file = NULL)

{

Printf ("Error in fdopen." n ");

Exit (1 );

}

While (count = fread (buf, 1, BUFFER_LEN, in_file)> 0)

Printf ("received from pipe: % s" n ", buf );

Fclose (in_file );

}

 

/* Process 2: Write a famous Pipeline */

Void main ()

{

FILE * out_file;

Int count = 1;

Char buf [BUFFER_LEN];

Out_file = fopen ("pipeexample", "w ");

If (out_file = NULL)

{

Printf ("Error opening pipe .");

Exit (1 );

}

Sprintf (buf, "this is test data for the named pipe example" n ");

Fwrite (buf, 1, BUFFER_LEN, out_file );

Fclose (out_file );

}

Message Queues are used for communication between processes running on the same machine, similar to pipelines;

The shared memory is usually created by a process, and other processes read and write the memory area. There are two ways to get the shared memory: ing/dev/mem device and memory image file. Previous Method

It does not bring additional overhead to the system, but is not commonly used in reality, because it controls the access to the actual physical memory. The common method is to achieve shared memory through the shmXXX function family:

Int shmget (key_t key, int size, int flag);/* Get a shared storage identifier */

This function enables the system to allocate the size of memory for shared memory;

Void * shmat (int shmid, void * addr, int flag);/* connect the shared memory to its own address space */

Shmid is the shared storage identifier returned by the shmget function. The addr and flag parameters determine how to determine the connection address. the return value of the function is the instance connected to the Data Segment of the process.

Address. Then, the process can read and write the address to access the shared memory.

Essentially, semaphores are a counter used to record access to a resource (such as shared memory. Generally, to obtain shared resources, the process must perform the following operations:

(1) testing the semaphore that controls the resource;

(2) If the semaphore value is positive, the resource can be used, and the process will reduce the number of incoming signs by 1;

(3) If the semaphore is 0, the resource is currently unavailable, and the process enters the sleep state until the signal value is greater than 0, and the process is awakened. Transfer to step (1 );

(4) When a process no longer uses a semaphore-controlled resource, the signal value is incremented by 1. If a process is sleeping and waiting for this semaphore, It is awakened.

The following is an example of using semaphores. This program creates a keyword for a specific IPC structure and a semaphores, creates an index for this semaphores, modifies the semaphore value pointed to by the index, and

Then clear the semaphore:

# Include <stdio. h>

# Include <sys/types. h>

# Include <sys/sem. h>

# Include <sys/ipc. h>

Void main ()

{

Key_t unique_key;/* defines an IPC keyword */

Int id;

Struct sembuf lock_it;

Union semun options;

Int I;

 

Unique_key = ftok (".", 'A');/* generate a keyword. The character 'a' is a random seed */

/* Create a new semaphore Set */

Id = semget (unique_key, 1, IPC_CREAT | IPC_EXCL | 0666 );

Printf ("semaphore id = % d" n ", id );

Options. val = 1;/* set the variable value */

Semctl (id, 0, SETVAL, options);/* set the semaphore of index 0 */

 

/* Print the semaphore value */

I = semctl (id, 0, GETVAL, 0 );

Printf ("value of semaphore at index 0 is % d" n ", I );

 

/* Reset the semaphore below */

Lock_it.sem_num = 0;/* specifies the semaphore */

Lock_it.sem_op =-1;/* define operation */

Lock_it.sem_flg = IPC_NOWAIT;/* Operation Method */

If (semop (id, & lock_it, 1) =-1)

{

Printf ("can not lock semaphore." n ");

Exit (1 );

}

 

I = semctl (id, 0, GETVAL, 0 );

Printf ("value of semaphore at index 0 is % d" n ", I );

 

/* Clear semaphores */

Semctl (id, 0, IPC_RMID, 0 );

}

Socket communication is not proprietary to Linux. Almost all operating systems that provide TCP/IP protocol stacks provide sockets. In all such operating systems, socket programming methods

Almost the same.

4. Section

This chapter describes the concept of Linux processes and explains Process Control and inter-process communication methods with multiple instances. Understanding this chapter is the key to understanding the Linux operating system.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.