Inter-process communication:
1. Pipe and named pipe ):
A pipeline can be used for communication between kinship processes. In addition to the functions of a pipeline, a famous Pipeline also allows communication between unrelated processes.
2. Signal (signal ):
A signal is a simulation of the interrupt mechanism at the software level. It is a complicated communication method used to notify a process of an event, A process receives a signal and the processor receives an interrupt request.
3. Message Queue ):
A message queue is a chain table of messages. It overcomes the limitations of semaphores in the previous two communication modes. A process with write permission can add new information to the Message Queue according to certain rules; processes with read permission on message queues can read information from message queues.
The message buffer communication technology was first proposed by Hansen. Its basic idea is to use the public message buffer in the memory to implement information exchange between processes based on the "producer-consumer" principle.
Several message buffers are opened in the memory to store messages. each time a process sends a message to another process, it requests a message buffer, sends the prepared message to the buffer, and inserts the message buffer into the message queue of the receiving process, finally, the receiving process is notified. after receiving the notifications sent by the receiving process, the receiving process removes a message buffer from the message queue of the process, retrieves the required information, and occasionally sends the message buffer to the system. the system is responsible for managing the public message buffer and message transmission.
A process can send messages to several processes. Conversely, a process can receive messages from different processes. obviously, operations on message queues in a process are critical. when the sending process is adding a message to the Message Queue of the receiving process, the receiving process cannot simultaneously send a message from the message queue: the opposite is true.
The message buffer communication mechanism includes the following columns:
(1) message buffer, which is a data structure composed of the following items:
1. Message Length
2. Message Body
3. Sender
4. Message Queue pointer
(2) The first pointer of the message queue, M-Q, is generally stored in the PCB.
(1) mutex semaphores M-mutex. The initial value is 1, which is used for mutex access to message queues and is set in PCB.
(2) Synchronous semaphore M-syn. The initial value is 0. It is used for message counting and is set in PCB.
(3) Send message primitive send
(4) Receive message primitive receive ()
4. Shared Memory ):
It can be said that this is the most useful way of inter-process communication. It allows multiple processes to access the same memory space. Different processes can view the updated data in the shared memory in a timely manner. This method relies on some synchronization operations, such as mutex locks and semaphores.
This communication mode needs to solve two problems: the first is how to provide shared memory, and the second is the mutex between public memory, which is the responsibility of program developers.
5. semaphores (semaphore ):
It is mainly used for synchronization and mutex between processes and different threads of the same process.
6. Socket );
This is a more general mechanism for inter-process communication. It can be used for inter-process communication between different machines in the network and is widely used.
Http://blog.csdn.net/eroswang/archive/2007/09/04/1772350.aspx
Inter-process communication in Linux
It is absolutely impossible to describe inter-process communication in detail here, and it is difficult for the author to confidently say what kind of knowledge he has achieved on this part of content, therefore, at the beginning of this section, we will first recommend the famous works of Richard Stevens: Advanced Programming in the Unix environment. its Chinese translation "Advanced Programming in UNIX environment" has been published by the Mechanical Industry Publishing House. The original Article is brilliant. The translation is also authentic. If you are really interested in programming in Linux, so hurry up and place the book next to your desk or computer. It is really hard to suppress the admiration in our hearts. Let's get down to the truth. In this section, we will introduce some of the most preliminary and simplest knowledge and concepts of inter-process communication.
First, inter-process communication can be achieved at least by sending open files. Different processes transmit information through one or more files. In fact, in many application systems, this method is used. However, generally, inter-process communication (IPC: Interprocess Communication) does not include this seemingly low-level communication method. There are many methods to implement inter-process communication in Unix systems, and unfortunately, very few methods can be transplanted in all UNIX systems (the only one is a half-duplex pipeline, this is also the most primitive communication method ). Linux, as a new operating system, supports almost all common inter-process communication methods in UNIX: pipelines, message queues, shared memory, semaphores, and interfaces. Next we will introduce them one by one.
2.3.1 MPs queue
A pipe is the oldest way of inter-process communication. It includes an unknown pipe and a famous pipe. The former is used for communication between parent and child processes, the latter is used for communication between any two processes running on the same machine.
The unknown pipeline is created by the pipe () function:
# Include <unistd. h>
Int pipe (INT filedis [2]);
The filedis parameter returns two file descriptors: filedes [0] is read and filedes [1] is write. Output of filedes [1] is the input of filedes [0. The following example demonstrates how to implement communication between the parent process and the child process.
# Define input 0
# Define output 1
Void main (){
Int file_descriptors [2];
/* Define the sub-process Number */
Pid_t PID;
Char Buf [256];
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 Linux, famous pipelines can be created in two ways: Command Line mknod System Call and function mkfifo. The following two channels generate a famous pipe named myfifo under the current directory:
Method 1: mkfifo ("myfifo", "RW ");
Method 2: mknod myfifo P
After a famous pipeline is generated, you can use common file I/O functions such as open, close, read, and write to operate it. The following is a simple example. Suppose we have created a famous pipe named myfifo.
/* Process 1: Read a famous Pipeline */
# Include <stdio. h>
# Include <unistd. h>
Void main (){
File * in_file;
Int COUNT = 1;
Char Buf [80];
In_file = fopen ("mypipe", "R ");
If (in_file = NULL ){
Printf ("error in fdopen. \ n ");
Exit (1 );
}
While (COUNT = fread (BUF, 1, 80, in_file)> 0)
Printf ("received from pipe: % s \ n", Buf );
Fclose (in_file );
}
/* Process 2: Write a famous Pipeline */
# Include <stdio. h>
# Include <unistd. h>
Void main (){
File * out_file;
Int COUNT = 1;
Char Buf [80];
Out_file = fopen ("mypipe", "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, 80, out_file );
Fclose (out_file );
}
2.3.2 Message Queue
Message Queue is used for communication between processes running on the same machine. It is similar to the pipeline and is a queue used to store messages in the system kernel, it appears in the system kernel in the form of a message linked list. The node Structure in the message linked list is declared with MSG.
In fact, it is a communication method that is gradually being eliminated. We can replace it with a Stream pipeline or an interface. Therefore, we will not explain this method, we recommend that you ignore this method.
2.3.3 shared memory
Shared memory is the fastest way to communicate between processes running on the same machine, because data does not need to be copied between different processes. A shared memory area is usually created by a process, and other processes read and write this memory area. There are two ways to get the shared memory: ing/dev/MEM device and memory image file. The previous method 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. in Linux, this can only be done by limiting the memory used to access the Linux system, which is of course not practical. The common method is to use shared memory for storage through the shmxxx function family.
The first function to use is shmget, which obtains a shared storage identifier.
# Include <sys/types. h>
# Include <sys/IPC. h>
# Include <sys/SHM. h>
Int shmget (key_t key, int size, int flag );
This function is similar to the familiar malloc function. The system allocates the size of memory as the shared memory according to the request. In the Linux kernel, each IPC structure has a non-negative integer identifier, So that you only need to reference the identifier when sending a message to a message queue. This identifier is obtained by the key word of the IPC structure in the kernel. This keyword is the key of the first function above. The data type key_t is defined in the header file SYS/types. H. It is a long integer data. In our subsequent chapters, we will also encounter this keyword.
After the shared memory is created, other processes can call shmat () to connect it to their own address space.
Void * shmat (INT shmid, void * ADDR, int flag );
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 actual address connected to the Data Segment of the process, A process can perform read/write operations on the process.
When using shared storage to implement inter-process communication, note the synchronization of data access. Make sure that the desired data has been written when a process reads data. Generally, semaphores are used to synchronize data access to shared storage. In addition, you can use the shmctl function to set some flags of shared storage memory, such as shm_lock and shm_unlock.
2.3.4 semaphores
Semaphores, also known as semaphores, are used to coordinate data objects between different processes. The most important application is the shared memory mode of inter-process communication in the previous section. 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) Test the semaphore that controls the resource.
(2) If the semaphore value is positive, the resource can be used. The process reduces the semaphore 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. The process is awakened and transferred to step (1 ).
(4) When a process no longer uses a semaphore-controlled resource, the signal value is increased by 1. If a process is sleeping and waiting for this semaphore at this time, it will be awakened.
The Linux Kernel Operating system rather than the user process is used to maintain the semaphore state. We can see the definition of each structure used by the kernel to maintain the semaphore state from the/usr/src/Linux/include/Linux/SEM. h file. Semaphores are a set of data. You can use each element of this set separately. The first function to be called is semget, which is used to obtain a semaphore ID.
Struct SEM {
Short sempid;/* PID of last operaton */
Ushort semval;/* Current Value */
Ushort semncnt;/* num procs awaiting increase in semval */
Ushort semzcnt;/* num procs awaiting semval = 0 */
}
# Include <sys/types. h>
# Include <sys/IPC. h>
# Include <sys/SEM. h>
Int semget (key_t key, int nsems, int flag );
The key is the keyword of the IPC structure discussed earlier. The flag determines whether to create a new semaphore set or reference an existing semaphore set in the future. Nsems is the number of semaphores in the set. If you create a new set (generally on the server), you must specify nsems. If you reference an existing set of semaphores (usually on the client), you can specify nsems as 0.
The semctl function is used to operate on semaphores.
Int semctl (INT Semid, int semnum, int cmd, Union semun Arg );
Different operations are implemented through the CMD parameter. Seven different operations are defined in the header file Sem. H. You can refer to the actual programming.
The semop function automatically executes the operation array on the semaphore set.
Int semop (INT Semid, struct sembuf semoparray [], size_t NOPs );
Semoparray is a pointer that points to an array of semaphore operations. NOPs specifies the number of operations in the array.
Next, let's look at a specific example. It creates a keyword for a specific IPC structure and a semaphore, creates an index for this semaphore, and modifies the semaphore value pointed to by the index, finally, we clear the semaphore. In the following code, the ftok function generates the unique IPC keyword we mentioned above.
# 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 );
}
Semget ()
You can use the system to call semget () to create a new semaphore set or access an existing semaphore set:
System Call: semget ();
Prototype: intsemget (key_t key, int nsems, int semflg );
Return Value: If successful, the IPC identifier of the semaphore set is returned. -1: errno = eaccess (no permission) is returned if the request fails)
Eexist (the semaphore set already exists and cannot be created)
Eidrm (semaphore set deleted)
Enoent (the semaphore set does not exist and ipc_creat is not used)
Enomem (not enough memory to create a new semaphore set)
The first parameter of the enospc system calling semget () is the keyword value (generally returned by the system calling ftok ). The system kernel compares this value with the keyword value of other semaphore sets in the system. The open and access operations are related to the content in the semflg parameter. Ipc_creat: If the semaphore set does not exist in the system kernel, a semaphore set is created. When ipc_excl is used together with ipc_creat, if the semaphore set already exists, the call fails. If ipc_creat is used separately, semget () either returns the identifier of the newly created semaphore set or the semaphore identifier that already exists with the same keyword value in the system. If ipc_excl and ipc_creat are used together, either the identifier of the newly created semaphore set or-1 is returned. It is meaningless to use ipc_excl independently. The nsems parameter indicates the number of semaphores that should be created in a new semaphores set. The maximum number of semaphores in the semaphores set is defined in Linux/SEM. h: # definesemmsl32/* <= 512maxnumofsemaphoresperid */
The following is a program for opening and creating a semaphore set:
Export pen_semaphore_set (key_t keyval, int numsems)
{
Intsid;
If (! Numsems)
Return (-1 );
If (SID = semget (mykey, numsems, ipc_creat | 0660) =-1)
{
Return (-1 );
}
Return (SID );
}
}; ========================================================== ======================================
Semop ()
System Call: semop ();
Call prototype: int semop (INT Semid, struct sembuf * SOPs, unsign ednsops );
Return Value: 0. If yes. -1. If it fails: errno = e2big (nsops is greater than the maximum number of OPS)
Eaccess (insufficient permissions)
Eagain (ipc_nowait is used, but the operation cannot continue)
Efault (the address to which SOPs points is invalid)
Eidrm (semaphore set deleted)
Eintr (receives other signals during sleep)
Einval (the semaphore set does not exist or the Semid is invalid)
Enomem (sem_undo is used, but there is not enough memory to create the required data structure)
Erange (signal value out of range)
The first parameter is the keyword value. The second parameter is the pointer to the array to be operated. The third parameter is the number of operations in the array. The SOPs parameter points to an array composed of sembuf. This array is defined in Linux/SEM. h:/* semop systemcall takes an array of these */
Structsembuf {
Ushortsem_num;/* semaphore index in array */
Eclipsem_op;/* semaphore operation */
Eclipsem_flg;/* operation flags */
The number of semaphores to be processed by sem_num.
The operation to be performed by sem_op.
Sem_flg operation flag. If sem_op is a negative number, the semaphore will subtract its value. This is related to the resources controlled by semaphores. If ipc_nowait is not used, the calling process goes to sleep until the resources controlled by the semaphore can be used. If sem_op is a positive number, the semaphore is added with its value. This means that the process releases resources controlled by semaphores. Finally, if sem_op is 0, the calling process calls sleep () until the semaphore value is 0. This is used when a process is waiting for completely idle resources. ========================================================== ======================================
Semctl ()
System Call: semctl ();
Prototype: int semctl (INT Semid, int semnum, int cmd, Union semunarg );
Return Value: If successful, it is a positive number.
-1: errno = eaccess (insufficient permissions)
Efault (the address to which Arg points is invalid)
Eidrm (semaphore set deleted)
Einval (the semaphore set does not exist or the Semid is invalid)
Eperm (EUID does not have the CMD permission)
Erange (signal value out of range)
The system calls semctl to perform control operations on the semaphore set. This is very similar to the system call of msgctl in the message queue. However, the two System Call parameters are slightly different. Because semaphores are generally used as a semaphores set, rather than a separate semaphores. Therefore, you must know not only the IPC keyword value, but also the specific semaphores in the semaphores set. Both system calls use the CMD parameter, which indicates the specific command to be operated. The last parameter in the two system calls is different. In msgctl, the last parameter is a pointer to the data structure used in the kernel. We use this data structure to obtain information about message queues and set or change the access permissions and users of queues. However, the semaphore supports additional optional commands, which requires a more complex data structure.
The first parameter that the system calls semctl () is the keyword value. The second parameter is the number of semaphores. The command that can be used in the CMD parameter is as follows:
· IPC _ stat reads the data structure semid_ds of a semaphore set and stores it in the Buf parameter of semun.
· Ipc_perm, an element in semid_ds, sets the data structure of the semaphore set. Its value is taken from the Buf parameter in semun.
· IPC _ rmid: removes the semaphore set from the memory.
· Getall is used to read the values of all semaphores in the semaphores set.
· Getncnt returns the number of processes waiting for resources.
· Getpid returns the PID of the last process that executes the semop operation.
· Getval returns the value of a single semaphore In the semaphore set.
· Getzcnt returns the number of processes that are waiting for completely idle resources.
· Setall: Set the values of all semaphores in the semaphores set.
· Setval: set the value of a separate semaphore In the semaphore set. The ARG parameter represents a semun instance. Semun is defined in Linux/SEM. h:
/* Arg for semctl systemcils .*/
Unionsemun {
Intval;/* value for setval */
Structsemid_ds * Buf;/* buffer for ipc_stat & ipc_set */
Ushort * array;/* array for getall & setall */
Structseminfo * _ Buf;/* buffer for ipc_info */
Void * _ pad; Val is used when the setval command is executed. Buf is used in the ipc_stat/ipc_set command. Represents the data structure of semaphores used in the kernel. The pointer used by array when the getall/setall command is used.
The following program returns the semaphore value. When the getval command is used, the last parameter in the call is ignored: intget_sem_val (intsid, intsemnum)
{
Return (semctl (SID, semnum, getval, 0 ));
} The following is an example of a practical application: # definemax_printers5
Printer_usage ()
{
Int X;
For (x = 0; x <max_printers; X ++)
Printf ("Printer % d: % d \ n \ r", X, get_sem_val (SID, x ));
} The following program can be used to initialize a new signal value: void init_semaphore (INT Sid, int semnum, int initval)
{
Union semunsemopts;
Semopts. Val = initval;
Semctl (SID, semnum, setval, semopts );
} Note that the last parameter in the System Call semctl is a replica of the Union type, rather than a pointer to the Union type.
2.3.5 sets of interfaces
Socket programming is one of the main ways to implement inter-process communication between Linux and most other operating systems. The well-known WWW Service, FTP service, and Telnet service are implemented based on a set of interface programming. In addition to remote computer processes, the set of interfaces also apply to inter-process communication within the same local computer. The classic textbook on the set of interfaces is also "UNIX Network Programming: networked APIs and sockets" compiled by Richard Stevens. Tsinghua University Press published the photocopy of this book. It is also one of the essential books for Linux programmers.
For details about this part, refer to the author's article "design your own network ant", which describes and sample programs using several commonly used interface functions. This part may be the most important and attractive part of Linux inter-process communication programming. After all, the Internet is developing at an incredible speed, if a programmer does not consider the network or the Internet when designing and writing his next program, it can be said that his design is difficult to succeed.
3 Comparison between Linux processes and Win32 processes/threads
Anyone familiar with Win32 programming must know that the Win32 process management method is very different from that on Linux. in UNIX, there is only a process concept, however, there is a "Thread" concept in Win32. What is the difference between Linux and Win32?
The process/thread in Win32 inherits from OS/2. In Win32, "process" refers to a program, and "Thread" refers to an execution "clue" in "process ". At the core, the multi-process of Win32 is not much different from that of Linux. The thread in Win32 is equivalent to a Linux Process and is actually executing code. However, threads in the same thread in Win32 share data segments. This is the biggest difference with Linux processes.
The following section shows how the next Win32 process starts a thread.
Int g;
DWORD winapi childprocess (lpvoid lpparameter ){
Int I;
For (I = 1; I <1000; I ++ ){
G ++;
Printf ("this is child thread: % d \ n", G );
}
Exitthread (0 );
};
Void main ()
{
Int threadid;
Int I;
G = 0;
Createthread (null, 0, childprocess, null, 0, & threadid );
For (I = 1; I <1000; I ++ ){
G ++;
Printf ("this is parent thread: % d \ n", G );
}
}
In Win32, The createthread function is used to create a thread. Unlike the creation process in Linux, the Win32 thread does not start to run from the creation process, but is specified by createthread, the thread starts to run from that function. This program is the same as the previous UNIX program, with 1000 pieces of information printed by each of the two threads. Threadid is the thread number of the subthread. In addition, the global variable G is shared between the subthread and the parent thread. This is the biggest difference with Linux. As you can see, Win32 processes/threads are more complex than Linux. in Linux, it is not difficult to implement threads similar to Win32. As long as the fork is passed, the sub-process can call the threadproc function, in addition, you can set up a shared data zone for global variables. However, fork-like functions cannot be implemented in Win32. Therefore, although the library functions provided by the C language compiler under Win32 are compatible with most Linux/Unix library functions, fork cannot be implemented yet.
For multi-task systems, sharing the data zone is necessary, but it is also a problem that is easy to cause confusion. in Win32, a programmer can easily forget that the data between threads is shared, after a thread modifies a variable, the other thread modifies it again, causing a program issue. However, in Linux, because variables are not shared, programmers explicitly specify the data to be shared, making the program clearer and safer.
As for the Win32 "process" concept, it means "application", which is equivalent to exec in UNIX.
Linux also has its own multi-threaded function pthread, which is different from Linux processes and Win32 processes, the introduction to pthread and How to Write multi-threaded programs in Linux will be described in another article titled multi-threaded programming in Linux.
Inter-process communication