Linux application development [Chapter 4] Linux interprocess communication application development

Posted by Mav666 on Wed, 15 Dec 2021 16:26:31 +0100

Article catalog

4 Linux interprocess communication application development

4.1 introduction process

In daily work / study, readers may often hear the following words: "job", "task", "opened several threads", "created several processes", "multi thread", "multi process", etc. If you have studied the course "operating system", I believe you have a good understanding of these concepts. But for many electronics Electrical engineering For students (or other non computer majors), since this course is not a compulsory course, we may not have these concepts in our mind. When we hear these concepts, we will be confused, but it doesn't matter. Let's overcome our fear of these concepts first. For example, when we first started learning mathematics as a child, we first started learning positive integer / natural number, and then Gradually come into contact with fractions, decimals, negative numbers, rational numbers, irrational numbers, real numbers, and then complex numbers. The same is true of the concepts in these operating systems. Let's start from the initial stage and gradually overcome the real meaning behind these new concepts.

This article mainly discusses the inter process communication mode of Linux. This topic is divided into three parts: Linux (operating system), process and inter process communication. Not to mention the Linux operating system, we mainly focus on the latter two parts: process and inter process communication. Before discussing inter process communication, let's focus on a knowledge point concept -- process.

4.1. 1 concept of process

4.1. 1.1 procedure

Before discussing the process, consider a question: what is a program?

The daily work / study content of embedded software engineers is to look at C/C + + source code, analyze C/C + + source code Writing C/C + + source code (some people will say that there should be the most important debugging programs. My daily work is to write programs in three and debug programs in seven. Don't worry about where the debugging programs go. Let's sell them here first). These independent source codes are programs. They have a common feature. They are static at the moment in the process of reading, analyzing and writing, They are stored on our hard disk, on our company's servers.

Program: an ordered collection of instructions and data stored on disk. Here is a program. At the moment, it is lying quietly on the hard disk.

01 #include <stdio.h>
02
03 int main(int argc, char *argv[])
04{
05	printf("hello world!\n");
06	return 0;
07}

4.1. 1.2 process

With the concept of the above program, first give the definition of the process directly.

Process: a dynamic execution process of a program with certain independent functions on a data set. It is dynamic, including creation, scheduling, execution, and extinction (done by the operating system).

We can understand every word in the definition separately, but when combined into a sentence, we don't know what it means. Nicholas Voss, the father of Pascal, the Turing prize winner, proposed a famous formula: program = algorithm + data structure. The so-called algorithm is the method to solve a problem. The program uses the algorithm to process specific data. These data are a broad concept, not just data such as 1, 2, 3. Therefore, in a more straightforward language, the process of analyzing and processing data is a process when the program starts running.

4.1. 1.3 connection between process and procedure

  1. The program is the basis of the generation process.
  2. Each execution of a program constitutes a different process.
  3. Process is the embodiment of program function (remember an important item in the daily work of programmers mentioned earlier - debugging program? The debugging process is actually the execution of the program, which is the embodiment of the program function, so it is a process at this time).
  4. Through multiple execution, a program can correspond to multiple processes; Through the call relationship, a process can contain multiple programs.

4.1. 1.4 difference between process and procedure

program

process

state

Static is a collection of ordered code

Dynamic is the execution process of program functions

Life cycle

Permanently and permanently stored on the storage device

Temporarily, when the execution of a program ends, its corresponding process ends

The following figure reflects the change process from program to process.

We use an example in life to deepen our understanding of processes and procedures:

1.There was a computer scientist whose daughter was going to have a birthday. He was going to make a birthday cake for his daughter, so he went to find a recipe and learned to make a cake.

menu=Program scientist=CPU  Raw materials for making cakes=The process of making cake with data=process

2.When the scientist was making a cake, suddenly his little son ran over and said that his hand was punctured, so the scientist went to find a medical manual to treat the wound for the little son. After treating the wound, he continued to make a birthday cake

Medical manual=The new procedure treats the younger son's wound=New process

Switch from cake making to wound dressing=After the process is switched and the wound is treated, continue to make birthday cake=Process recovery

So far, I hope the readers have established some basic concepts about the process. We won't introduce the in-depth part of the process here for the time being, For example, what are the components of the process (code segment, user data segment, system data segment)? What are the types of the process? What are the status of the process? After we have mastered the basic knowledge of the process, readers can consult relevant books and materials if they are interested.

4.1. 2 process operations (create, end, recycle)

4.1. 2.1 create process

use fork Function to create a process
 Header file: #include <unistd.h>
Function prototype: pid_t fork(void);
Return value: When successful, the parent process returns the process number of the child process(>0 Nonzero integer),0 returned in child process;adopt fork The return value of the function distinguishes between parent and child processes.
Parent process: implement fork Function.
Subprocess: Parent process call fork Function to generate a new process.

Please note that the return value of this function is different from that of most of the functions we contact.

Generally, a function has only one return value, but the function has two return values. In fact, the number of return values of this function can be understood in another way, because after the function is executed, there will be two processes in the system - parent process and child process, which return a value in each process, so it gives the user the feeling that two values are returned.

Characteristics of the process:

  1. In linux, a process must be a child of another process, or a process must have a parent process, but it can have no child process.
  2. The child process inherits the contents of the parent process, including the parent process's code, variables, pcb, and even the current PC value. In the parent process, the PC value points to the address of the next instruction of the current fork function, so the child process also starts to execute from the next instruction of the fork function. The execution order of parent-child processes is uncertain. Either child processes or parent processes execute first, depending on the scheduling of the current system.
  3. The parent-child processes have independent address space and independent code space, which do not affect each other. Even if the parent-child processes have global variables with the same name, they cannot be shared because they are in different address spaces.
  4. After the child process ends, its parent process must reclaim all its resources, otherwise it will become a zombie process.
  5. If the parent process ends first, the child process will become an orphan process, which will be adopted by the INIT process. The INIT process is the first process created after the kernel starts.

Tips:

Under linux, when we are not familiar with a system interface API function (for example, we do not know the header file required to call this function, the meaning of each parameter of this function, etc.), we can use the man command under ubuntu to view the description of this function.

Sample program (Reference: jz2440 \ process \ 1st_create_process \ create_process. C)

01 /**********************************************************************
02  * Function Description: create a child process
03  * Input parameters: None
04  * Output parameters: None
05  * Return value: None
06  * Modification date version number modified by
07  * -----------------------------------------------
08  * 2020/05/16       V1.0      zh(ryan)        establish
09  ***********************************************************************/
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <sys/types.h>
15 
16 int main(int argc, char *argv[])
17 {
18     pid_t pid;
19 
20     pid = fork();  // Create child process
21 
22     if (pid == 0) {  // Subprocess
23         int i = 0;
24         for (i = 0; i < 5; i++) {
25             usleep(100);
26             printf("this is child process i=%d\n", i);
27         }
28     }
29     
30     if (pid > 0) {  // Parent process
31         int i = 0;
32         for (i = 0; i < 5; i++) {
33             usleep(100);
34             printf("this is parent process i=%d\n", i);
35         }
36     }
37 
38     while(1);   //Don't let the process end so that we can see some status information about the process
39     return 0;
40 }

JZ2440 experiment

When experimenting on the jz2440 development board, the reader first needs to create the NFS file system. The jz2440 development board starts from the network file system to run the executable compiled on ubuntu. For how to build the NFS file system, please refer to the video tutorial u-boot kernel root file system (connection between the enhanced version of ARM bare metal phase 1 and the driver phase 2). Readers can also execute on ubuntu by changing the compiler from "ARM linux gcc" to "gcc".

  • Compiler
arm-linux-gcc create_process.c -o create_process
  • Copy the executable file test to the corresponding directory of NFS file system
cp create_process /work/nfs_root/first_fs
  • The executable can be seen under the serial port of jz2440 development board
  • Executable file "&" means to execute in the background, so that we can continue to type in commands under the serial port console, and the console can receive input characters and respond; If "&" is not added, it means that it is executed in the foreground and the console cannot respond to the input characters.
./create_process &
  • top command to view process status
top

It is found that two create processes do exist at this time_ Process, where one process PID is 777 (its parent process PID is 776) and the other process PID is 776 (its parent process PID is 770).

4.1. 2.2 ending the process

use exit Function to end a process
 Header file: #include <stdlib.h>
Function prototype: void exit (int status)
use_exit Function to end a process
 Header file: #include <unistd.h>
Function prototype: void _exit(int status);

The difference between the two functions is: exit will flush the buffer when the process ends_ Exit will not;

What is the difference between the two exit functions and the return function? Exit and_ The exit function is returned to the operating system. The return function is returned by the current function and returned to the function calling it. If it happens to be in the main function, the return function is also returned to the operating system. At this time, return, exit_ Exit plays a similar role.

Program experiment: verify exit and_ The difference between exit

Example 1: exit using exit (Reference: jz2440 \ process \ 2nd_exit_process \ exit_process. C)

01 /**********************************************************************
02  * Function Description: use exit to exit the current process
03  * Input parameters: None
04  * Output parameters: None
05  * Return value: None
06  * Modification date version number modified by
07  * -----------------------------------------------
08  * 2020/05/16       V1.0      zh(ryan)        establish
09  ***********************************************************************/
10 #include <stdio.h>
11 #include <stdlib.h>
12 
13 int main(int argc, char *argv[])
14 {
15     printf("hello world\n");
16     printf("will exit");
17     exit(0);    //Use_ Exit exit
18 }

Example 2: Using_ Exit exit (Reference: jz2440\processth_exit_process\exit_process.c)

01 /**********************************************************************
02  * Function Description: use_ exit exits the current process
03  * Input parameters: None
04  * Output parameters: None
05  * Return value: None
06  * Modification date version number modified by
07  * -----------------------------------------------
08  * 2020/05/16       V1.0      zh(ryan)        establish
09  ***********************************************************************/
10 #include <stdio.h>
11 #include <stdlib.h>
12 
13 int main(int argc, char *argv[])
14 {
15     printf("hello world\n");
16     printf("will exit");
17     _exit(0);    //Use_ Exit exit
18 }

In the two sample programs, line 15 has one more "\ n" than the print statement on line 16, which will force the characters to be printed to be flushed to the buffer in order to compare exit and_ The difference between exit is that "\ n" is not added in line 16. According to the difference between the above two exit functions, example 1 should print "hello world" and "will exit" at the same time. Example 2 program will only print "hello world" and not "will exit". So is this the case? Let's verify it under jz2440.

JZ2440 experiment

Example 1

  • compile
arm-linux-gcc exit_process.c -o exit_process
  • Copy to NFS
cp exit_process /work/nfs_root/first_fs
  • function
./exit_process

The running result does print "hello world" and "will exit" at the same time

4.1. 2.3 recycling process

use wait Function to recycle a process
 Header file: #include <sys/types.h>
	   #include <sys/wait.h>
Function prototype: pid_t wait(int *status);
Return value:  The process number of the child process is returned successfully, and the failure is returned-1
use waitpid Function to recycle a process
 Header file: #include <sys/types.h>
	   #include <sys/wait.h>
Function prototype: pid_t waitpid(pid_t pid, int *status, int options);
Return value:  The process number of the child process is returned successfully, and the failure is returned-1

Program example: the child process exits and the parent process reclaims the child process (Reference: jz2440\processth_exit_wait\exit_wait.c)

1 /**********************************************************************
02  * Function Description: use exit to exit the child process, and the parent process uses waitpid to recycle the resources of the child process
03  * Input parameters: None
04  * Output parameters: None
05  * Return value: None
06  * Modification date version number modified by
07  * -----------------------------------------------
08  * 2020/05/16       V1.0      zh(ryan)        establish
09  ***********************************************************************/
10 #include <unistd.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <sys/types.h>
14 #include <sys/wait.h>
15 
16 int main(int argc, char *argv[])
17 {
18 	int status = -1;
19 	pid_t pid;
20 
21 	pid = fork();
22 	if (pid == 0){ //Subprocess
23 		printf("fork\n");
24 		exit(1);
25 	} else if (pid > 0) { //Parent process
26 		pid = waitpid(pid, &status, 0);
27 		printf("status=0x%x\n", status);
28 	} else {
29 		perror("fork\n");
30 	}
31 
32 	return 0;
33 }

JZ2440 experiment

  • compile
arm-linux-gcc exit_wait.c -o exit_wait
  • Copy to NFS
cp exit_wait /work/nfs_root/first_fs
  • function
./exit_wait

Operation results

4.2 why do processes need communication

Let's first look at the following two simple programs. There is a global variable "global" with the same name in both programs. The only difference is that the initial value of this global variable is different. Note: the following two sample programs are to let us understand a feature of the process, so the experimental environment is Ubuntu virtual machine.

Procedure 1:

01 #include <stdio.h>
02 int global = 1;
03
04 void delay(void)
05 {
06 	unsigned int a = 1000000;
07 	while(a--);
08 }
09
10 int main(int argc, char *argv[])
11 {
12 	while (1) {
13 		printf("global=%d\n", global);
14 		delay();
15 	}
16 	return 0;
17 }

Procedure 2:

01 #include <stdio.h>
02 int global = 2;
03
04 void delay(void)
05 {
06 	unsigned int a = 1000000;
07 	while(a--);
08 }
09
10 int main(int argc, char *argv[])
11 {
12 	while (1) {
13 		printf("global=%d\n", global);
14 		delay();
15 	}
16 	return 0;
17 }

The only difference between the two programs is shown in the red box below:

  • Compiler
gcc test1.c -o test1
gcc test2.c -o test2
  • Run program
./test1
./test2

Program 1 running results

Program 2 running results

We found that after the two programs run, the value of the global variable in the current process will not change, it will not be changed to the value in another process, which leads to a characteristic of the process: * * the uniqueness and non sharing of process resources, and it cannot access the data (address space) in other processes, Nor can other processes access their own data (address space)** Each process is a black box for other processes (when readers learn about threads later, they will find that threads are different from processes in this feature).

So why? This is because the operating system is to ensure the security of the system (the collapse of process A will not affect process B, but process B will continue to run). It will allocate A specific address space for each process. Each process can only execute instructions and access data in this specific address space, as shown in the figure below. When A program needs to access A variable, it accesses the variable through the variable address. In different processes, variables with the same name do not correspond to each other With the same address (within the current process address space range), the process cannot access the address space outside the address range assigned to it, and naturally cannot obtain the variable values in other processes.

Why do processes need to communicate? From the above two sample programs, we can know that different processes cannot access each other's address space. However, in our actual project development, in order to achieve a variety of functions, data interaction must be required between different processes, so how should we achieve data interaction between processes? This is the purpose of interprocess communication: to realize the data interaction between different processes.

Under linux, memory space is divided into user space and kernel space. Applications developed by application developers exist in user space, and most processes are in user space; Driver developers develop drivers that exist in kernel space.

In user space, different processes cannot access each other's resources. Therefore, inter process communication cannot be realized in user space. In order to realize inter process communication, the kernel must provide corresponding interfaces in the kernel space. linux system provides the following four process communication modes.

Interprocess communication mode

classification

pipeline communication

Unknown pipeline and famous pipeline

IPC communication

Shared memory, message queue, semaphore

Signal communication

Signal sending, receiving and processing

socket communication

Local socket communication, remote socket communication

linux has a basic idea - "everything is a file". The realization of inter process communication in the kernel is also based on the idea of file reading and writing. Different processes realize inter process communication by operating the same kernel object in the kernel, as shown in the figure below. This kernel object can be pipes, shared memory, message queues, semaphores, signals, and socket s.

4.3 pipeline communication of process communication

The pipeline is divided into nameless pipeline and famous pipeline, and its characteristics are as follows

type

characteristic

Unnamed Pipes

There are no file nodes in the file system, which can only be used for inter process communication with kinship (such as parent-child processes)

Famous pipeline

There are file nodes in the file system, which is suitable for communication between any two processes in the same system

4.3. 1 unknown pipeline

4.3. 1.1 features

The nameless pipeline is actually a one-way queue. Read operations are performed at one end and write operations are performed at the other end. Therefore, two file descriptors are required. The descriptor fd[0] points to the read end and fd[1] points to the write end. It is a special file, so it cannot be created with a simple open function. We need the pipe function to create it. It can only be used for communication between two processes that are related.

4.3. 1.2 creating anonymous pipes

1.Header file#include <unistd.h>

2.Function prototype: int pipe(int fd[2])

3.parameter: The pipeline file descriptor has two file descriptors: fd[0]and fd[1],The pipe has a read end fd[0]And a write side fd[1]

4.Return value: 0 indicates success; 1 means failure

4.3. 1.3 read, write and close the pipeline

1.Read pipeline read,The file descriptor corresponding to the read pipeline is fd[0]

2.Write pipeline write,The file descriptor corresponding to the write pipeline is fd[1]

3.Close the pipe close,Because when creating a pipeline, two pipeline file descriptors will be created at the same time, namely, the read pipeline file descriptor fd[0]And write pipeline file descriptor fd[1],Therefore, you need to close both file descriptors

4.3. 1.4 anonymous pipeline realizes interprocess communication

Program example 1

(Reference: jz2440 \ process_pipe \ 1st_write_pipe \ my_pipe_write. C)

01 /**********************************************************************
02  * Function Description: create a pipeline, write a string to the pipeline, read from the pipeline, and verify
03               Can I read a previously written string
04  * Input parameters: None
05  * Output parameters: None
06  * Return value: None
07  * Modification date version number modified by
08  * -----------------------------------------------
09  * 2020/05/16       V1.0      zh(ryan)        establish
10  ***********************************************************************/
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 
15 int main(int argc, char *argv[])
16 {
17     int fd[2];
18     int ret = 0;
19     char write_buf[] = "Hello linux";
20     char read_buf[128] = {0};
21     
22     ret = pipe(fd);
23     if (ret < 0) {
24         printf("create pipe fail\n");
25         return -1;
26     }
27     printf("create pipe sucess fd[0]=%d fd[1]=%d\n", fd[0], fd[1]);
28     
29     //Write pipeline to file descriptor fd[1]
30     write(fd[1], write_buf, sizeof(write_buf));
31     
32     //Read pipeline from file descriptor fd[0]
33     read(fd[0], read_buf, sizeof(read_buf));
34     printf("read_buf=%s\n", read_buf);
35     
36     close(fd[0]);
37     close(fd[1]);
38     return 0;
39 }

JZ2440 experiment

  • compile
arm-linux-gcc my_pipe_write.c -o my_pipe_write
  • Copy to NFS file system
cp my_pipe_write /work/nfs_root/first_fs
  • function
./my_pipe_write

The running results show that the string "Hello linux" in the pipeline can be read correctly.

Program example 2

Before using the anonymous pipeline to realize inter process communication, let's take a look at the following program: we know that the execution order of parent-child processes is uncertain and is scheduled by the system. We create a child process in the parent process. We want the parent process to control the operation of the child process. The parent process sets "process_inter=1". When "process_inter=1", the child process will execute the print operation, otherwise the child process will not execute the print operation. Do we think the following procedures can achieve our purpose?

(Reference: jz2440 \ process_pipe \ 2nd_comm \ test. C)

01 /**********************************************************************
02  * Function Description: 1 Create a child process in the parent process,
03             2.After the parent process is executed, the variable process_ The value of Inter is 1;
04             3.Sub process judgment process_ If Inter is 1, the following print statement will be executed, otherwise it will not be executed.
05  * Input parameters: None
06  * Output parameters: None
07  * Return value: None
08  * Modification date version number modified by
09  * -----------------------------------------------
10  * 2020/05/16       V1.0      zh(ryan)        establish
11  ***********************************************************************/
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <sys/types.h>
16 
17 int main(int argc, char *argv[])
18 {
19     pid_t pid;
20     int process_inter = 0;
21 
22     pid = fork();  // Create child process
23 
24     if (pid == 0) {  // Subprocess
25         int i = 0;
26         while (process_inter == 0); //
27         for (i = 0; i < 5; i++) {
28             usleep(100);
29             printf("this is child process i=%d\n", i);
30         }
31     }
32     
33     if (pid > 0) {  // Parent process
34         int i = 0;
35         for (i = 0; i < 5; i++) {
36             usleep(100);
37             printf("this is parent process i=%d\n", i);
38         }
39         process_inter == 1;
40     }
41 
42     while(1);
43     return 0;
44 }

JZ2440 experiment

  • compile
arm-linux-gcc test.c -o test
  • Copy to NFS file system
cp test /work/nfs_root/first_fs
  • function
./test

As a result, it is found that the print statement on line 29 has not been printed, and there is no process in the subprocess_ Inter is always 0.

Program example 3

(Reference: jz2440\process_pipeth_pipe_comm\comm_fork.c)

01 /**********************************************************************
02  * Function Description: 1 Using anonymous pipeline to realize parent-child process communication
03             2.The parent process writes a value to the pipeline
04             3.The subprocess reads the value from the pipeline. If it is non-zero, the subsequent printing will be executed, otherwise it will not be executed
05  * Input parameters: None
06  * Output parameters: None
07  * Return value: None
08  * Modification date version number modified by
09  * -----------------------------------------------
10  * 2020/05/16       V1.0      zh(ryan)        establish
11  ***********************************************************************/
12 
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <sys/types.h>
17 
18 int main(int argc, char *argv[])
19 {
20     pid_t pid;
21     char process_inter = 0;
22     int fd[2], ret = 0;
23 
24     ret = pipe(fd);   //To create an anonymous pipe, you must create a child process before you create it
25     if (ret < 0) {
26         printf("create pipe fail\n");
27         return -1;
28     }
29     printf("create pipe sucess\n");
30     
31     pid = fork();  //Create child process
32 
33     if (pid == 0) {  // Subprocess
34         int i = 0;
35         read(fd[0], &process_inter, sizeof(process_inter));  // If the pipeline is empty, hibernate and wait
36         while (process_inter == 0);
37         for (i = 0; i < 5; i++) {
38             usleep(100);
39             printf("this is child process i=%d\n", i);
40         }
41     } else if (pid > 0) {  // Parent process
42         int i = 0;
43         for (i = 0; i < 5; i++) {
44             usleep(100);
45             printf("this is parent process i=%d\n", i);
46         }
47         process_inter = 1;
48         sleep(2);
49         write(fd[1], &process_inter, sizeof(process_inter));
50     }
51 
52     while(1);
53     return 0;
54 }

JZ2440 experiment

  • compile
arm-linux-gcc comm_fork.c -o comm_fork
  • Copy to NFS file system
cp comm_fork /work/nfs_root/first_fs
  • function
./comm_fork

As a result, due to the 2s delay in line 38, the printing in the child process is also correctly output about 2s after the parent process finishes printing, as shown below.

4.3. 2 famous pipeline

4.3. 2.1 features

The so-called named pipeline, as the name suggests, is that there is a file name in the kernel, indicating that it is a pipeline file. There are seven file types in Linux, as follows.

file type

Document characteristics

Ordinary file

Identifier '-', created in open mode

Catalog file

Identifier'd ', created in mkdir mode

Link file

Identifier 'l', la -s, can be divided into soft link and hard link

(2) pipeline documents

Identification 'p', created with mkfifo

socket file

Identifier's', created with socket

Character device file

Identifier 'c'

Block device file

Identifier 'b'

Well known pipes can be used for both inter process communication with kinship and inter process communication without kinship. In our actual project, many processes do not have kinship, so the use of well-known pipes will be more common.

4.3. 2.2 creating named pipes

Function prototype: int mkfifo(const char * filename, mode_t mode)

Parameters: pipe file name, permission, created file permission and umask It matters

Return value: 0 for success and 0 for failure-1

Note: mkfifo does not generate a pipeline in the kernel, but a named pipeline file in user space

4.3. 2.3 realize interprocess communication through well-known pipeline

Sample program 1

Create a named pipe file (Reference: jz2440\process_pipeth_create_myfifo\create_myfifo.c)

01 /**********************************************************************
02  * Function Description: 1 Create a named pipe
03  * Input parameters: None
04  * Output parameters: None
05  * Return value: None
06  * Modification date version number modified by
07  * -----------------------------------------------
08  * 2020/05/16       V1.0      zh(ryan)        establish
09  ***********************************************************************/
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <sys/types.h>
15 
16 int main(int argc, char *argv[])
17 {
18     int ret;
19 
20     ret = mkfifo("./myfifo", 0777);   //Create a named pipeline with file permissions of 777
21     if (ret < 0) {
22         printf("create myfifo fail\n");
23         return -1;
24     }
25     printf("create myfifo sucess\n");
26     
27     return 0;
28 }

JZ2440 experiment

  • compile
arm-linux-gcc create_myfifo.c -o create_myfifo
  • Copy to NFS file system
cp create_myfifo /work/nfs_root/first_fs
  • function
./create_myfifo

The running result shows that a named pipeline file myfifo (file type "- p") is generated in the current directory.

Sample program 2

Process 1 source code (Reference: jz2440\process_pipeth_myfifo_commnd_named_pipe.c)

01 /**********************************************************************
02  * Function Description: 1 Create a named pipeline 3rd in process 1_ FIFO, the permission is 0777
03             2.Open the named pipeline file in write mode and write a value to it
04  * Input parameters: None
05  * Output parameters: None
06  * Return value: None
07  * Modification date version number modified by
08  * -----------------------------------------------
09  * 2020/05/16       V1.0      zh(ryan)        establish
10  ***********************************************************************/
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <sys/types.h>
16 #include <fcntl.h>
17 
18 int main(int argc, char *argv[])
19 {
20     int i, ret, fd;
21     char p_flag = 0;
22 
23     /* Create a named pipe */
24     if (access("./3rd_fifo", 0) < 0) {   //First judge whether the famous pipeline file exists. If it does not exist, you need to create it first
25         ret = mkfifo("./3rd_fifo", 0777);
26         if (ret < 0) {
27             printf("create named pipe fail\n");
28             return -1;
29         }
30         printf("create named pipe sucess\n");
31     }
32     
33     /* Open a named pipe and open it in write mode */
34     fd=open("./3rd_fifo", O_WRONLY);
35     if (fd < 0) {
36         printf("open 3rd_fifo fail\n");
37         return -1;
38     }
39     printf("open 3rd_fifo sucess\n");
40 
41     for (i = 0; i < 5; i++) {
42         printf("this is first process i=%d\n", i);
43         usleep(100);
44     }
45     p_flag = 1;
46     sleep(5);
47     write(fd, &p_flag, sizeof(p_flag));
48 
49     while(1);
50     return 0;
51 }

Process 2 source code (Reference: jz2440\process_pipeth_myfifo_commnd_named_pipe_2.c)

01 /**********************************************************************
02  * Function Description: 1 Open the named pipeline file in read-only mode and read the value
03             2.When this value is non-zero, continue to execute the following printout statements
04  * Input parameters: None
05  * Output parameters: None
06  * Return value: None
07  * Modification date version number modified by
08  * -----------------------------------------------
09  * 2020/05/16       V1.0      zh(ryan)        establish
10  ***********************************************************************/
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <sys/types.h>
16 #include <fcntl.h>
17 
18 int main(int argc, char *argv[])
19 {
20     int i;
21     int fd=open("./3rd_fifo", O_RDONLY);
22     char p_flag = 0;
23     
24     if (fd < 0) {
25         printf("open 3rd_fifo fail\n");
26         return -1;
27     }
28     
29     printf("open 3rd_fifo sucess\n");
30     read(fd, &p_flag, sizeof(p_flag));
31     while(!p_flag);
32     for (i = 0; i < 5; i++) {
33         printf("this is second process i=%d\n", i);
34         usleep(100);
35     }
36 
37     while(1);
38     return 0;
39 }

JZ2440 experiment

  • compile
arm-linux-gcc 5nd_named_pipe.c -o 5nd_named_pipe

arm-linux-gcc 5nd_named_pipe_2.c -o 5nd_named_pipe_2
  • Copy to NFS file system
cp 5nd_named_pipe /work/nfs_root/first_fs

cp 5nd_named_pipe_2 /work/nfs_root/first_fs
  • function

Note that we all run executable programs in the background, which is convenient for us to input multiple times under serial port interrupt.

./5nd_named_pipe &

./5nd_named_pipe_2 &

4.4 IPC communication of process communication

IPC communication is divided into shared memory, message queue and semaphore. These IPC objects (shared memory, message queues, semaphores) exist in kernel space.

The general steps of application communication using IPC are as follows:

  • First, a key value is generated. There are two ways to generate keys. One is to use macro IPC_PRIVATE represents a key, which represents a private object and can only be accessed by the current process or processes with kinship. The other is to use the ftok function to generate a key value. The IPC object created in this way can be accessed by different processes.
  • Use the generated key value to create an IPC object (if it is an already created IPC object, open the IPC object). At this time, each IPC object has a unique ID number (IPC_id, which can be shm_id, msg_id, sem_id, and each ID represents an IPC object).
  • Process through IPC_id, call the read-write function to access the IPC channel to operate the IPC object. Call shmctrl, shmat and shmdt to access the shared memory; Call msgctrl, msgsnd and msgrecv to access the message queue; Call semctrl and semop to access the semaphore.

How to understand key and IPC_id(shm_id/msg_id/sem_id)

To answer this question, please first think about how an application accesses an IPC object (shared memory, message queue, semaphore lamp)?

Obviously, We need an identity ID that uniquely represents the IPC object (IPC_id, which is managed by the operating system), but since this ID can only be obtained in the process that currently creates the IPC object and cannot be obtained in other IPC processes, how to obtain the ID of the IPC object? At this time, the key value is required. It is equivalent to an alias or external name of the IPC_id, so the key value must also be unique, so To get a unique IPC object ID. Different processes access the same IPC object through the same key value to the same IPC object ID. As shown in the figure below

ftok function

Function prototype : char ftok(const char *path, char key)

Parameters: path,File path that exists and can be accessed

       key,One character

Return value: returns a value correctly key Value, error returned-1

Why do I need the ftok function to become a key and then create an IPC object?

This is similar to the difference between anonymous pipeline and famous pipeline. IPC is used_ The shared memory created by the private macro is similar to an anonymous pipe, which can only realize the communication between related processes.

So why use ftok to generate a key value? Can I specify a non-zero value directly? It is not recommended to directly specify a non-zero key value, because the key value specified by the reader is likely to be the same as the existing key value in the system.

After the ftok function creates a key value, it is similar to a famous pipeline. It can realize inter process communication with kinship and non kinship.

4.4. 1 shared memory

4.4. 1.1 features

The so-called shared memory refers to the same address space that multiple processes can access, but we know that in order to ensure the safety of system execution, the Linux operating system divides each process into its own independent address space, and each process cannot access the address space of other processes. What is the principle of shared memory implementation?

The kernel opens up a physical memory area, and the process itself maps this memory space to its own address space for reading and writing.

As can be seen from the figure, the process can directly access this memory, and the data does not need to be copied between the two processes, so the speed is faster. Shared memory does not have any synchronization and mutual exclusion mechanism, so semaphores should be used to synchronize the access to shared memory.

When shared memory needs to be used for communication, the general steps are as follows:

  • First create a piece of shared memory that exists in kernel space.
  • The process finds the unique ID of the shared memory through the key value, and then maps the shared memory to its own address space.
  • Each process accesses the shared memory in the kernel by reading and writing the mapped address.

4.4. 1.2 creating shared memory

Function prototype : int shmget(key_t key, int size, int shmflg)

Header file: #include <sys/shm.h>

Function parameters : key: IPC_PRIVATE or ftok Return value of

		 IPC_PRIVATE Returned key The values are the same,All 0

		 size : Shared memory size

		 shmflg : with open The permission bit of the function can also be expressed in octal

Return value: success, shared memory segment identifier ID; -1 error

Program example 1 (refer to jz2440\process_ipc st_shm st_shm.c)

01 /**********************************************************************
02  * Function Description: 1 Use IPC_PRIVATE create shared memory
03  * Input parameters: None
04  * Output parameters: None
05  * Return value: None
06  * Modification date version number modified by
07  * -----------------------------------------------
08  * 2020/05/16       V1.0      zh(ryan)        establish
09  ***********************************************************************/
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <sys/types.h>
15 #include <sys/shm.h>
16 #include <signal.h>
17 
18 int main(int argc, char *argv[])
19 {
20     int shmid;
21     
22     shmid = shmget(IPC_PRIVATE, 128, 0777);
23     if (shmid < 0) {
24         printf("create shared memory fail\n");
25         return -1;
26     }
27     printf("create shared memory sucess, shmid = %d\n", shmid);
28     system("ipcs -m");
29     return 0;
30 }

JZ2440 experiment

  • compile
arm-linux-gcc 1st_shm.c -o 1st_shm
  • Copy to NFS file system
cp 1st_shm /work/nfs_root/first_fs
  • function

After executing the program in line 18, the following information will be printed on the serial port. The function of this line is the same as that of entering "ipcs -m" directly under the serial port console. We find that the key value of shared memory is 0.

./1st_shm

Program example 2 (refer to jz2440\process_ipc st_shm)nd_shm.c)

Program source code, use fotk function to generate a key value

01 /**********************************************************************
02  * Function Description: 1 Create shared memory using the key generated by the ftok function
03  * Input parameters: None
04  * Output parameters: None
05  * Return value: None
06  * Modification date version number modified by
07  * -----------------------------------------------
08  * 2020/05/16       V1.0      zh(ryan)        establish
09  ***********************************************************************/
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <sys/types.h>
15 #include <sys/shm.h>
16 #include <signal.h>
17 
18 int main(int argc, char *argv[])
19 {
20     int shmid;
21     int key;
22 
23     key = ftok("./a.c", 'a');  //First create a key value
24     if (key < 0) {
25         printf("create key fail\n");
26         return -1;
27     }
28     printf("create key sucess key = 0x%X\n",key);
29     
30     shmid = shmget(key, 128, IPC_CREAT | 0777);
31     if (shmid < 0) {
32         printf("create shared memory fail\n");
33         return -1;
34     }
35     printf("create shared memory sucess, shmid = %d\n", shmid);
36     system("ipcs -m");
37     return 0;
38 }

JZ2440 experiment

  • compile
arm-linux-gcc 2nd_shm.c -o 2nd_shm
  • Copy to NFS file system
cp 2nd_shm /work/nfs_root/first_fs
  • function

We need to be on 2nd_ Create a file a.c (on the jz2440 development board) under the same level directory of SHM

touch a.c

We found that the key value of shared memory is non-zero 0x610d0169

./2nd_shm

4.4. 1.3 how applications access shared memory

We know that the shared memory created is still in the kernel space, and the user program cannot directly access the kernel address space. How can the user program access the shared memory?

shmat function

Map shared memory to user space so that applications can access shared memory directly

Function prototype: void *shmat(int shmid, const void *shmaddr, int shmflg)

Parameters: shmid ID number

	   shmaddr Mapping address, NULL Automatically completed mapping for the system

       shmflg SHM_RDONLY Shared memory read only

       The default is 0, readable and writable

Return value: successfully mapped address; Failed, return NULL

Program example (refer to jz2440\process_ipc st_shmnd_shm.c)

01 /**********************************************************************
02  * Function Description: 1 Create a shared memory and map the shared memory address to the user address space through shmat
03             2.The user enters a line of string into the shared memory through standard input
04             3.Then read the content from the shared memory to verify whether it can be read
05  * Input parameters: None
06  * Output parameters: None
07  * Return value: None
08  * Modification date version number modified by
09  * -----------------------------------------------
10  * 2020/05/16       V1.0      zh(ryan)        establish
11  ***********************************************************************/
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <sys/types.h>
16 #include <sys/shm.h>
17 #include <signal.h>
18 
19 int main(int argc, char *argv[])
20 {
21     int shmid;
22     int key;
23     char *p;
24     
25     key = ftok("./a.c", 'b');
26     if (key < 0) {
27         printf("create key fail\n");
28         return -1;
29     }
30     printf("create key sucess key = 0x%X\n",key);
31     
32     shmid = shmget(key, 128, IPC_CREAT | 0777);
33     if (shmid < 0) {
34         printf("create shared memory fail\n");
35         return -1;
36     }
37     printf("create shared memory sucess, shmid = %d\n", shmid);
38     system("ipcs -m");
39     
40     p = (char *)shmat(shmid, NULL, 0);
41     if (p == NULL) {
42         printf("shmat fail\n");
43         return -1;
44     }
45     printf("shmat sucess\n");
46     
47     //Wait for console input and write data to shared memory
48     fgets(p, 128, stdin);
49     
50     //Read shared memory
51     printf("share memory data:%s\n", p);
52     
53     //Read shared memory again
54     printf("share memory data:%s\n", p);
55     return 0;
56 }

JZ2440 experiment

  • compile
arm-linux-gcc 3nd_shm.c -o 3nd_shm
  • Copy to NFS file system
cp 3nd_shm /work/nfs_root/first_fs
  • function

We need to be in 3nd_ Create a file a.c (on the jz2440 development board) under the same level directory of SHM

touch a.c

The user is prompted for information

./3nd_shm

We enter any character in the console, such as "hello linux", and then press enter. The print is as follows

Problem: Line 51 of the code reads the shared memory once, and then line 54 reads the shared memory again. It is found that the content of the shared memory can be read twice, indicating that the content still exists after the shared memory is read. In the pipeline, if we read the content of the pipeline once and then read it for the second time (on the premise of no new writing), we can't read the content in the pipeline. It means that the content disappears after the pipeline is read once. Readers can verify it by themselves through experiments.

shmdt function

Function prototype: int shmdt(const void *shmaddr)

Parameters; shmat Return value of

Return value : Success 0, error-1

Program example (refer to jz2440\process_ipc st_shmth_shm.c)

01 /**********************************************************************
02  * Function Description: 1 Create a shared memory and map the shared memory address to the user address space through shmat
03             2.The user enters a line of string into the shared memory through standard input
04             3.Then read from the shared memory
05             4.Call shmdt to unmap the address, and an error will occur when the application continues to access
06  * Input parameters: None
07  * Output parameters: None
08  * Return value: None
09  * Modification date version number modified by
10  * -----------------------------------------------
11  * 2020/05/16       V1.0      zh(ryan)        establish
12  ***********************************************************************/
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <sys/types.h>
17 #include <sys/shm.h>
18 #include <signal.h>
19 #include <string.h>
20 
21 int main(int argc, char *argv[])
22 {
23     int shmid;
24     int key;
25     char *p;
26     
27     key = ftok("./a.c", 'b');
28     if (key < 0) {
29         printf("create key fail\n");
30         return -1;
31     }
32     printf("create key sucess key = 0x%X\n",key);
33     
34     shmid = shmget(key, 128, IPC_CREAT | 0777);
35     if (shmid < 0) {
36         printf("create shared memory fail\n");
37         return -1;
38     }
39     printf("create shared memory sucess, shmid = %d\n", shmid);
40     system("ipcs -m");
41     
42     p = (char *)shmat(shmid, NULL, 0);
43     if (p == NULL) {
44         printf("shmat fail\n");
45         return -1;
46     }
47     printf("shmat sucess\n");
48     
49     //write share memory
50     fgets(p, 128, stdin);
51     
52     //start read share memory
53     printf("share memory data:%s\n", p);
54     
55     //start read share memory again
56     printf("share memory data:%s\n", p);
57 
58     //Delete the address of shared memory in user space
59     shmdt(p);
60     
61     memcpy(p, "abcd", 4);  //Executing this statement will result in a segment fault because the shared memory address mapping is unmapped
62     return 0;
63 }

JZ2440 experiment

  • compile
arm-linux-gcc 4th_shm.c -o 4th_shm
  • Copy to NFS file system
cp 4th_shm /work/nfs_root/first_fs
  • function

We need to be on the 4th_ shm. Create a file a.c (on the jz2440 development board) under the same level directory of C

touch a.c

When running, the user will be prompted to enter information. After entering, the Segmentation fault will appear when executing the statement on line 61, which is expected by the program.

./4th_shm

shmctl function

Function prototype: int shmctl(int shmid, int cmd, struct shmid_ds *buf)

parameter: shmid : Shared memory identifier

	 cmd : IPC_START (Get object properties)   --- The command is implemented ipcs -m

     IPC_SET(Set object properties)

	 IPC_RMID (Delete object properties)    --- The command is implemented ipcrm -m

	 buf : appoint IPC_START/IPC_SET Save when/set a property

Return value : Success 0, error-1

Program example (refer to jz2440\process_ipc st_shmth_shm.c)

01 /**********************************************************************
02  * Function Description: 1 Create a shared memory and map the shared memory address to the user address space through shmat
03               2.The user enters a line of string into the shared memory through standard input
04               3.Then read from the shared memory
05               4.Call shmdt to unmap the address, and an error will occur when the application continues to access
06               5.Finally, call the shmctl function to delete the shared memory in the kernel.
07  * Input parameters: None
08  * Output parameters: None
09  * Return value: None
10  * Modification date version number modified by
11  * -----------------------------------------------
12  * 2020/05/16       V1.0      zh(ryan)        establish
13  ***********************************************************************/
14 
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <unistd.h>
18 #include <sys/types.h>
19 #include <sys/shm.h>
20 #include <signal.h>
21 #include <string.h>
22 
23 int main(int argc, char *argv[])
24 {
25     int shmid;
26     int key;
27     char *p;
28     
29     key = ftok("./a.c", 'b');
30     if (key < 0) {
31         printf("create key fail\n");
32         return -1;
33     }
34     printf("create key sucess key = 0x%X\n",key);
35     
36     shmid = shmget(key, 128, IPC_CREAT | 0777);
37     if (shmid < 0) {
38         printf("create shared memory fail\n");
39         return -1;
40     }
41     printf("create shared memory sucess, shmid = %d\n", shmid);
42     system("ipcs -m");
43     
44     p = (char *)shmat(shmid, NULL, 0);
45     if (p == NULL) {
46         printf("shmat fail\n");
47         return -1;
48     }
49     printf("shmat sucess\n");
50     
51     //write share memory
52     fgets(p, 128, stdin);
53     
54     //start read share memory
55     printf("share memory data:%s\n", p);
56     
57     //start read share memory again
58     printf("share memory data:%s\n", p);
59 
60     //Delete the address of shared memory in user space
61     shmdt(p);
62     
63     //memcpy(p, "abcd", 4);  // When this statement is executed, segment fault will appear
64     
65     shmctl(shmid, IPC_RMID, NULL);
66     system("ipcs -m");
67     return 0;
68 }

JZ2440 experiment

  • compile
arm-linux-gcc 5th_shm.c -o 5th_shm
  • Copy to NFS file system
cp 5th_shm /work/nfs_root/first_fs
  • function
touch a.c

Run. At this time, the user will be prompted to enter information. When executing the statement on line 42 for the first time, the reader can see the shared memory. When executing the statement on line 66 for the second time, the reader can't see the shared memory because the shared memory has been deleted.

./4th_shm

4.4. 1.4 shared memory for interprocess communication

Steps:

1. establish/Open shared memory

2. Mapping shared memory, that is, mapping the specified shared memory to the address space of the process for access

3. Read / write shared memory

4. Unmap shared memory

5. Delete shared memory object

Some considerations or restrictions when using shared memory

1. The amount of shared memory is limited by ipcs -l Command view. Of course, if we have administrator privileges, we can use cat /proc/sys/kernel/shmmax To see

2. The point in time when the shared memory was deleted, shmctl Add delete flag, only when all processes unmap shared memory(That is, all process calls shmdt after),Will delete the shared memory.

Sample source code (refer to jz2440\process_ipc st_shmth_shm.c)

01 /**********************************************************************
02  * Function Description: 1 Create IPC with key value in parent process_ Private creates a shared memory
03             2.Then create a child process in the parent process
04             3.Through standard input, the parent process writes a string to shared memory
05             4.The parent process calls the send signal function to inform the child process that the shared memory can be read
06             5.The child process receives the signal from the parent process and starts reading the shared memory
07             6.After the child process reads the shared memory, it sends a signal to notify the parent process that the reading is complete
08  * Input parameters: None
09  * Output parameters: None
10  * Return value: None
11  * Modification date version number modified by
12  * -----------------------------------------------
13  * 2020/05/16       V1.0      zh(ryan)        establish
14  ***********************************************************************/
15 
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <sys/types.h>
20 #include <sys/shm.h>
21 #include <signal.h>
22 #include <string.h>
23 
24 void myfun(int signum)
25 {
26     return;
27 }
28 
29 int main(int argc, char *argv[])
30 {
31     int shmid;
32     int key;
33     char *p;
34     int pid;
35 
36 
37     shmid = shmget(IPC_PRIVATE, 128, IPC_CREAT | 0777);
38     if (shmid < 0) {
39         printf("create shared memory fail\n");
40         return -1;
41     }
42     printf("create shared memory sucess, shmid = %d\n", shmid);
43 
44     pid = fork();
45     if (pid > 0) {    // Parent process
46         signal(SIGUSR2, myfun);
47         p = (char *)shmat(shmid, NULL, 0);
48         if (p == NULL) {
49             printf("shmat fail\n");
50             return -1;
51         }
52         printf("parent process shmat sucess\n");
53         while (1) {
54             //Gets a string from standard input and writes it to shared memory
55             printf("parent process begin to write memory data:");
56             fgets(p, 128, stdin);
57             kill(pid, SIGUSR1);   // Signal subprocesses to read shared memory
58             pause();              // Wait for the child process to finish reading the signal of shared memory
59         }
60     }
61     if (pid == 0) { // Subprocess
62         signal(SIGUSR1, myfun);
63         p = (char *)shmat(shmid, NULL, 0);
64         if (p == NULL) {
65             printf("shmat fail\n");
66             return -1;
67         }
68         printf("child process shmat sucess\n");
69         while (1) {
70             pause(); // Wait for the parent process to signal to read the shared memory
71             //The child process starts to read the shared memory, and a concurrent signal tells the parent process that the read is complete
72             printf("child process read share memory data:%s\n", p);
73             kill(getppid(), SIGUSR2);
74         }
75     }
76 
77     //Delete the address of shared memory in user space
78     shmdt(p);
79     
80     //memcpy(p, "abcd", 4);  // When this statement is executed, segment fault will appear
81     
82     shmctl(shmid, IPC_RMID, NULL);
83     system("ipcs -m");
84     return 0;
85 }

JZ2440 experiment

  • compile
arm-linux-gcc 6th_shm.c -o 6th_shm
  • Copy to NFS file system
cp 6th_shm /work/nfs_root/first_fs
  • function

The parent process obtains the string entered by the user from standard input, and then the child process prints out the string.

./6th_shm

server process source code (refer to jz2440\process_ipc st_shmth_shm_1.c)

01 /**********************************************************************
02  * Function Description: 1 The server process uses ftok to generate a key value, and uses this key value to create a shared memory
03             2.Writes a string to shared memory through standard input
04             3.server The process calls the send signal function to notify the client process
05  * Input parameters: None
06  * Output parameters: None
07  * Return value: None
08  * Modification date version number modified by
09  * -----------------------------------------------
10  * 2020/05/16       V1.0      zh(ryan)        establish
11  ***********************************************************************/
12 
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <sys/types.h>
17 #include <sys/shm.h>
18 #include <signal.h>
19 #include <string.h>
20 
21 struct mybuf
22 {
23     int pid;
24     char buf[124];
25 };
26 
27 void myfun(int signum)
28 {
29     return;
30 }
31 
32 int main(int argc, char *argv[])
33 {
34     int shmid;
35     int key;
36     struct mybuf *p;
37     int pid;
38 
39     key = ftok("./a.c", 'a');
40     if (key < 0) {
41         printf("create key fail\n");
42         return -1;
43     }
44     printf("create key sucess\n");
45 
46     shmid = shmget(key, 128, IPC_CREAT | 0777);
47     if (shmid < 0) {
48         printf("create shared memory fail\n");
49         return -1;
50     }
51     printf("create shared memory sucess, shmid = %d\n", shmid);
52 
53     signal(SIGUSR2, myfun);
54     p = (struct mybuf *)shmat(shmid, NULL, 0);
55     if (p == NULL) {
56         printf("shmat fail\n");
57         return -1;
58     }
59     printf("parent process shmat sucess\n");
60 
61     p->pid = getpid(); // Write the pid number of the server process to the shared memory
62     pause();           // Wait for the client to read the server pid number
63     pid=p->pid;        // Get the process number of the client
64 
65     while (1) {
66         //write share memory
67         printf("parent process begin to write memory data\n");
68         fgets(p->buf, 124, stdin);
69         kill(pid, SIGUSR1);   // Send a signal to the client to inform the client to read the shared memory data
70         pause();              // Wait for the client to finish reading the shared memory data
71     }
72 
73     //Delete the address of shared memory in user space
74     shmdt(p);
75 
76     shmctl(shmid, IPC_RMID, NULL);
77     system("ipcs -m");
78     return 0;
79 }

client process source code (refer to jz2440\process_ipc st_shmth_shm_2.c)

01 /**********************************************************************
02  * Function Description: 1 The client process uses ftok to generate a key value, which is used to open a shared memory
03             2.client After receiving the signal sent by the server process, the process starts to read the shared memory
04             3.After the child process reads the shared memory, it sends a signal to notify the parent process that the reading is complete
05  * Input parameters: None
06  * Output parameters: None
07  * Return value: None
08  * Modification date version number modified by
09  * -----------------------------------------------
10  * 2020/05/16       V1.0      zh(ryan)        establish
11  ***********************************************************************/
12 
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <sys/types.h>
17 #include <sys/shm.h>
18 #include <signal.h>
19 #include <string.h>
20 
21 struct mybuf
22 {
23     int pid;
24     char buf[124];
25 };
26 
27 void myfun(int signum)
28 {
29     return;
30 }
31 
32 int main(int argc, char *argv[])
33 {
34     int shmid;
35     int key;
36     struct mybuf *p;
37     int pid;
38     
39     key = ftok("./a.c", 'a');
40     if (key < 0) {
41         printf("create key fail\n");
42         return -1;
43     }
44     printf("create key sucess\n");
45 
46     shmid = shmget(key, 128, IPC_CREAT | 0777);
47     if (shmid < 0) {
48         printf("create shared memory fail\n");
49         return -1;
50     }
51     printf("create shared memory sucess, shmid = %d\n", shmid);
52 
53     signal(SIGUSR1, myfun);
54     p = (struct mybuf *)shmat(shmid, NULL, 0);
55     if (p == NULL) {
56         printf("shmat fail\n");
57         return -1;
58     }
59     printf("client process shmat sucess\n");
60     
61     // get server pid
62     //read share memory
63     pid = p->pid;
64     // write client pid to share memory
65     p->pid = getpid();
66     kill(pid, SIGUSR2);   // tell server process to read data
67     
68     //client start to read share memory
69 
70     while (1) {
71         pause();              // wait server process write share memory
72         printf("client process read data:%s\n", p->buf); // read data
73         kill(pid, SIGUSR2);   // server can  write share memory
74     }
75 
76     //Delete the address of shared memory in user space
77     shmdt(p);
78     
79     shmctl(shmid, IPC_RMID, NULL);
80     system("ipcs -m");
81     return 0;
82 }

The source code is left for readers to experiment by themselves. It should be noted that at this time, two consoles need to be run, namely, the server process and the client process. A serial port console and a telnet console can be used. We can also start two terminal verifications under ubuntu.

4.4. 2 message queue

4.4. 2.1 what is a message queue

Message queue is a linked list of messages. It is a linked queue. Similar to the pipeline, each message has a maximum length limit, which can be viewed in cat/proc/sys/kernel/msgmax.

The kernel maintains a data structure msgqid for each message queue object_ DS is used to identify the message queue so that the process can know which message queue is currently operating. Each msqid_ds represents a message queue and passes msqid_ds.msg_first,msg_last maintains a first in first out MSG linked list queue. When a message is sent to the message queue, the sent message is constructed into an MSG structure object and added to msqid_ds.msg_first,msg_ List queue maintained by last. The in the kernel is represented as follows:

4.4. 2.2 features

  1. The life cycle follows the kernel, and the message queue always exists. The user needs to display the calling interface to delete or use the command to delete.
  2. Message queuing enables two-way communication
  3. It overcomes the disadvantage that the pipeline can only carry unformatted byte stream

4.4. 2.3 message queue function

msgget function

Function to create or open a message queue
 Header file:#include <sys/types.h>
        #include <sys/ipc.h>
       #include <sys/msg.h>
Prototype: int msgget(key_t key, int flag)
Parameters: key Associated with message queuing key value
       flag Access rights for message queuing
 Return value: success, message queue ID,error -1

msgctl function

Message queue control function
 Prototype: int msgctl(int msgqid, int cmd, struct msqid_ds *buf)
Parameters: msgqid Message queue ID
           cmd IPC_STAT Read the properties of the message queue and save it in the buf In the buffer pointed to
               IPC_SET  Set the properties of message queue. This value is taken from buf parameter
               IPC_RMID Remove message queuing from the system
           buf Message buffer
 Return value: success 0, error -1

msgsnd function

Add a message to the message queue
 Header file#include <sys/types.h>
      #include <sys/ipc.h>
      #include <sys/msg.h>
Prototype: int msgsnd(int msgqid, const void *msgp, size_t size, int flag)
Parameters: msgqid Message queue ID
           msgp Pointer to message, commonly used message structure msgbuf as follows
           struct msgbuf {
               long mtype;        //Message type
               char mtext[N];     //Message body
           };
           size Bytes of message body
           flag IPC_NOWAIT If the message is not sent, it will be returned immediately
                0:  The function does not return until sending is complete
    Return value: success 0, error -1

msgrcv function

Accept messages from a message queue
 Prototype: int msgrcv(int msgqid, void *msgp, size_t size, long msgtype, int flag)
Parameters: msgqid Message queue ID
       msgp Buffer for receiving messages
       size The number of bytes to receive the message
       msgtype  0 Receive the first message in the message queue
                The first type in the receive message queue greater than 0 is msgtype News of
                Less than 0. The type value in the receive message queue is not greater than msgtype Message with the absolute value of and the smallest type value
           flag IPC_NOWAIT If there is no message, it will return immediately
                0:  If there is no message, it will be blocked all the time
                
Return value: length of successfully received message, error -1

4.4. 2.4 message queue to realize interprocess communication

server source code (refer to jz2440\process_ipc)nd_shm\write_msg.c)

01 /**********************************************************************
02  * Function Description: 1 The server process writes data to the message queue
03  * Input parameters: None
04  * Output parameters: None
05  * Return value: None
06  * Modification date version number modified by
07  * -----------------------------------------------
08  * 2020/05/16       V1.0      zh(ryan)        establish
09  ***********************************************************************/
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <sys/types.h>
15 #include <sys/msg.h>
16 #include <signal.h>
17 #include <string.h>
18 
19 struct msgbuf {
20     long type;        //Message type
21     char voltage[124];     //Message body
22     char ID[4];
23 };
24 
25 int main(int argc, char *argv[])
26 {
27     int msgid, readret, key;
28     struct msgbuf sendbuf;
29 
30     key = ftok("./a.c", 'a');
31     if (key < 0){
32         printf("create key fail\n");
33         return -1;
34     }
35     msgid = msgget(key, IPC_CREAT|0777);
36     if (msgid < 0) {
37         printf("create msg queue fail\n");
38         return -1;
39     }
40     printf("create msg queue sucess, msgid = %d\n", msgid);
41     system("ipcs -q");
42 
43     // write message queue
44     sendbuf.type = 100;
45     while(1) {
46         memset(sendbuf.voltage, 0, 124);  //clear send buffer
47         printf("please input message:");
48         fgets(sendbuf.voltage, 124, stdin);
49         //start write msg to msg queue
50         msgsnd(msgid, (void *)&sendbuf, strlen(sendbuf.voltage), 0);
51     }
52 
53     return 0;
54 }

client source code (refer to jz2440\process_ipc)nd_shm\read_msg.c)

01 /**********************************************************************
02  * Function Description: 1 The client process reads data from the message queue
03  * Input parameters: None
04  * Output parameters: None
05  * Return value: None
06  * Modification date version number modified by
07  * -----------------------------------------------
08  * 2020/05/16       V1.0      zh(ryan)        establish
09  ***********************************************************************/
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <sys/types.h>
15 #include <sys/msg.h>
16 #include <signal.h>
17 #include <string.h>
18 
19 struct msgbuf {
20     long type;        //Message type
21     char voltage[124];     //Message body
22     char ID[4];
23 };
24 
25 int main(int argc, char *argv[])
26 {
27     int msgid, key;
28     struct msgbuf readbuf;
29 
30     key = ftok("./a.c", 'a');
31     if (key < 0){
32         printf("create key fail\n");
33         return -1;
34     }
35     msgid = msgget(key, IPC_CREAT|0777);
36     if (msgid < 0) {
37         printf("create msg queue fail\n");
38         return -1;
39     }
40     printf("create msg queue sucess, msgid = %d\n", msgid);
41     system("ipcs -q");
42 
43     // read message queue
44     while(1) {
45         memset(readbuf.voltage, 0, 124);  //clear recv buffer
46         //start read msg to msg queue
47         msgrcv(msgid, (void *)&readbuf, 124, 100, 0);
48         printf("recv data from message queue:%s", readbuf.voltage);
49     }
50 
51     return 0;
52 }

JZ2440 experiment

  • compile
arm-linux-gcc write_msg.c -o write_msg
arm-linux-gcc read_msg.c -o read_msg
  • Copy to NFS file system
cp write_msg /work/nfs_root/first_fs
cp read_msg /work/nfs_root/first_fs
  • function

Execute read in the background first_ MSG, and then run write in the foreground_ MSG. At this time, enter the string under the console, and you can see that the client process can read the string in the message queue.

./read_msg &
./ write_msg

4.4. 3 semaphore lamp

4.4. 3.1 what are P and V operations

When different processes need to access the same resource, because the execution order of different processes is unknown, it is possible that one process is writing the resource and another process is reading the resource, which will cause uncertainty in process execution. Such a same resource is called shared resource. Shared resource can only be accessed by one process at a time. Therefore, processes need to add synchronization and mutual exclusion operations when accessing shared resources.

Generally, P operation means to apply for the shared resource, and V operation means to release the shared resource.

4.4. 3.2 what is a semaphore lamp

It is a collection of semaphores, including multiple semaphores. It can perform P/V operation on multiple semaphores at the same time. It is mainly used to realize synchronization / mutual exclusion between processes and threads. The kernel maintains a data structure semid for each semaphore lamp_ DS is used to identify the semaphore lamp so that the process can know which semaphore lamp is currently operating. The representation in the kernel is as follows.

How is it different from the semaphore in POSIX specification? Semaphores in POSIX specification only act on one semaphore, while semaphore lights in IPC objects act on a group of semaphores.

function

Semaphore (POSIX)

Semaphore lamp (IPC object)

Define signal variables

sem_t sem1

semget

Initialization semaphore

sem_init

semctl

P operation

sem_wait

semop

V operation

sem_post

semop

Why do I need semaphore lights in IPC objects? Is there not enough semaphore in POSIX specification?

Consider the following scenarios:

  1. Both thread A and thread B need to access shared resource 1 and shared resource 2. In thread A, you need to apply for shared resource 1 first, and then apply for shared resource 2.
  2. However, in online process B, you will first apply for contribution resource 2, and then apply for shared resource 1.
  3. When thread A starts to apply for shared resource 1, it will then apply for shared resource 2; At this time, when thread B starts to apply for shared resource 2, it will immediately apply for shared resource 1.
  4. Thread B is occupying shared resource 2 and thread A is occupying shared resource 1. As A result, thread B cannot apply for shared resource 1, and it will not release shared resource 2; If thread A cannot apply for shared resource 2, it will not release shared resource 1; This creates A deadlock.

4.4. 3.3 semaphore lamp function

semget function

Create or open functions
 Header file#includde <sys/types.h>
#includde <sys/ipc.h>
#includde <sys/sem.h>
prototype: int semget(key_t key, int nsems, int semflag)
Parameters: key Associated with the semaphore set key value
      nsems Number of semaphores contained in the semaphore set
      semflag Access to the semaphore set
 Return value: success, signal light ID,error -1

semctl function

Semaphore lamp control function
 Header file#includde <sys/types.h>
#includde <sys/ipc.h>
#includde <sys/sem.h>
prototype: int semctl(int semid, int semnum, int cmd, ...union semun arg)
          Note that the last parameter is not an address. It can have or not
 Parameters: semid Signal lamp set id
      semnum The number of the semaphore set to modify,When deleting, this value can be set to any value
      cmd GETVAL Gets the value of the semaphore
           SETVAL Set the value of the signal lamp
           IPC_RMID Delete semaphore
      union semun arg: union semun {
                     int              val;    /* Value for SETVAL */
                     struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
                     unsigned short  *array;  /* Array for GETALL, SETALL */
                     struct seminfo  *__buf;  /* Buffer for IPC_INFO (Linux-specific) */
                 };
Return value: success, message queue ID,error -1

semop function

p/v Operation function
 Header file#includde <sys/types.h>
#includde <sys/ipc.h>
#includde <sys/sem.h>
prototype: int semop(int semid, struct sembuf *opsptr, size_t nops)
Parameters: semid Signal lamp set id
      opsptr struct sembuf{
                  short sem_num;   //The number of the signal lamp to be operated
                  short sem_op;    //0: wait until the value of the semaphore changes to 0, 1: resource release, V operation, - 1: resource allocation, P operation
                  short sem_flg;   //0: IPC_NOWAIT, SEM_UNDO
              }
       nops Number of signal lamps to be operated
 Return value: success, message queue ID,error -1

4.4. 3.4 semaphore lamp realizes inter process synchronization / mutual exclusion

Program source code (refer to jz2440\process_ipcrd_shm\share_sysv.c)

01 /**********************************************************************
02  * Function Description: 1 The parent process inputs a string from the keyboard to shared memory
03             2.The subprocess deletes spaces in the string and prints them
04             3.After the parent process enters quit, the shared memory and semaphore set are deleted, and the program ends
05  * Input parameters: None
06  * Output parameters: None
07  * Return value: None
08  * Modification date version number modified by
09  * -----------------------------------------------
10  * 2020/05/16       V1.0      zh(ryan)        establish
11  ***********************************************************************/
12 
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/ipc.h>
17 #include <sys/sem.h>
18 #include <sys/types.h>
19 #include <sys/shm.h>
20 #include <signal.h>
21 #include <unistd.h>
22 
23 #define N 64
24 #define READ 0
25 #define WRITE 1
26 
27 union semun {
28 	int val;
29         struct semid_ds *buf;
30         unsigned short *array;
31         struct seminfo *__buf;
32 };
33 
34 void init_sem(int semid, int s[], int n)
35 {
36 	int i;
37 	union semun myun;
38 
39 	for (i = 0; i < n; i++){
40 		myun.val = s[i];
41 		semctl(semid, i, SETVAL, myun);
42 	}
43 }
44 
45 void pv(int semid, int num, int op)
46 {
47 	struct sembuf buf;
48 
49 	buf.sem_num = num;
50 	buf.sem_op = op;
51 	buf.sem_flg = 0;
52 	semop(semid, &buf, 1);
53 }
54 
55 int main(int argc, char *argv[])
56 {
57 	int shmid, semid, s[] = {0, 1};
58 	pid_t pid;
59 	key_t key;
60 	char *shmaddr;
61 
62 	key = ftok(".", 's');
63 	if (key == -1){
64 		perror("ftok");
65 		exit(-1);
66 	}
67 
68 	shmid = shmget(key, N, IPC_CREAT|0666);
69 	if (shmid < 0) {
70 		perror("shmid");
71 		exit(-1);
72 	}
73 
74 	semid = semget(key, 2, IPC_CREAT|0666);
75 	if (semid < 0) {
76 		perror("semget");
77 		goto __ERROR1;
78 	}
79 	init_sem(semid, s, 2);
80 
81 	shmaddr = shmat(shmid, NULL, 0);
82 	if (shmaddr == NULL) {
83 		perror("shmaddr");
84 		goto __ERROR2;
85 	}
86 
87 	pid = fork();
88 	if(pid < 0) {
89 		perror("fork");
90 		goto __ERROR2;
91 	} else if (pid == 0) {
92 		char *p, *q;
93 		while(1) {
94 			pv(semid, READ, -1);
95 			p = q = shmaddr;
96 			while (*q) {
97 				if (*q != ' ') {
98 					*p++ = *q;
99 				}
100 				q++;
101 			}
102 			*p = '\0';
103 			printf("%s", shmaddr);
104 			pv(semid, WRITE, 1);
105 		}
106 	} else {
107 		while (1) {
108 			pv(semid, WRITE, -1);
109 			printf("input > ");
110 			fgets(shmaddr, N, stdin);
111 			if (strcmp(shmaddr, "quit\n") == 0) break;
112 				pv(semid, READ, 1);
113 			}
114 			kill(pid, SIGUSR1);
115 		}
116 
117 	__ERROR2:
118 		semctl(semid, 0, IPC_RMID);
119 	__ERROR1:
120 		shmctl(shmid, IPC_RMID, NULL);
121 		return 0;
122 }

JZ2440 experiment

  • compile
arm-linux-gcc share_sysv.c -o share_sysv
  • Copy to NFS file system
cp share_sysv /work/nfs_root/first_fs
  • function

Enter the string under the console of the parent process, and the child process will read the string.

./share_sysv

4.5 signal communication of process communication

4.5. 1 signaling mechanism

  1. Generally, interrupts mainly refer to hardware interrupts, such as GPIO interrupt and timer interrupt. When these hardware interrupts are sent to the CPU when the peripheral module is working, they are also an asynchronous mode.
  2. Signal is a simulation of interrupt mechanism at the software level. It is an asynchronous communication mode.
  3. The Linux kernel notifies the user process through signals, and different signal types represent different events.
  4. Linux extends the early unix signaling mechanism.

4.5. 2 common signal types

4.5. 3 signal transmission function

kill function

Header file  #include <unistd.h>

       #include <signal.h>

Function prototype int kill(pid_t pid, int sig);

parameter  pid :  Specifies the process number of the receiving process

            0 On behalf of the same group process;-1 Represents all except INIT Process and processes other than the current process

     sig :  Signal type

The return value is 0 for success and 0 for failure EOF

raise function

Header file  #include <unistd.h>

       #include <signal.h>

Function prototype int raise(int sig);

parameter  sig :  Signal type

The return value is 0 for success and 0 for failure EOF

alarm function

Header file  #include <unistd.h>

       #include <signal.h>

Function prototype int alarm(unsigned int seconds);

parameter  seconds Timer time

The return value returns the remaining time of the previous timer successfully, and the return value of failure EOF

pause function

After calling this function, the process will block until it is interrupted by the signal, function and sleep similar.

Header file  #include <unistd.h>

       #include <signal.h>

Function prototype int pause(void);

The return value is 0 for success and 0 for failure EOF

signal function

To set the signal response mode, please pay attention to this function and kill,killall We Chinese users will understand the difference as signaling. In fact, it is not signaling.

Header file  #include <unistd.h>

       #include <signal.h>

Function prototype void (*signal(int signo, void(*handler)(int)))(int)

parameter  signo Signal type to set

     handler Specified signal processing function;

The return value is 0 for success and 0 for failure EOF

4.5. 4 process capture signal

Program source code (refer to jz2440\process_single\send_single.c)

01 /**********************************************************************
02  * Function Description: 1 Capture the signal sent by the terminal
03  * Input parameters: None
04  * Output parameters: None
05  * Return value: None
06  * Modification date version number modified by
07  * -----------------------------------------------
08  * 2020/05/16       V1.0      zh(ryan)        establish
09  ***********************************************************************/
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <sys/types.h>
15 #include <signal.h>
16 
17 void handler(int signo)
18 {
19  	switch (signo) {
20 		case SIGINT:
21 			printf("I have got SIGINT\n");
22 			break;
23 			
24 		case SIGQUIT:
25 			printf("I have got SIGQUIT\n");
26 			break;
27 			
28 		default:
29 			printf("don't respond to this signal[%d]\n", signo);
30 			exit(0);
31 	}
32 }
33 
34 int main(int argc, char *argv[])
35 {
36 	signal(SIGINT, handler);
37 	signal(SIGQUIT, handler);
38 	while (1)
39 		pause();
40 	return 0;
41 }

JZ2440 experiment

  • compile
arm-linux-gcc send_single.c -o send_single
  • Copy to NFS file system
cp send_single /work/nfs_root/first_fs
  • function
./send_single

In fact, when using shared memory to realize inter process communication, we have used signal communication. After the parent process writes the shared memory, it sends a signal to inform the child process. The child process starts reading the shared memory after receiving the signal. Here is no example of using signal communication between two processes. Please refer to the code for realizing communication between two processes in shared memory.

4.6 socket communication of process communication

4.6. 1 what is socket

First consider a question: how do processes in the network environment realize communication? For example, when we use QQ to chat with friends, how does the QQ process communicate with the server and your friends' QQ process? These are realized by socket.

Socket originated from UNIX. One of the basic philosophies of Unix/Linux is "everything is a file", which can be operated in the "open – > read / write/read – > close" mode. In the famous pipeline section, we know that socket is also a file type, but socket is a pseudo file, which exists in the kernel buffer and has a constant size of 0.

Socket files must appear in pairs. There is a socket file on the server side and a socket file on the client side. Each process needs to be bound to the corresponding socket file. Each process can be implemented by the kernel by reading and writing its socket file, as shown below.

Generally, socket is used to realize process communication on different hosts in the network environment, but it can also be used to communicate between different processes on the same host. This section mainly discusses the communication between different processes on the same host.

4.6. 2 correlation function

socket function

establish socket File descriptor function

Header file#include <sys/types.h>

     #include <sys/socket.h>

  prototype: int socket(int domain, int type, int protocol)

  Return value: success, message queue ID,error -1

bind function

take socket File descriptor and a socket File binding

Header file#include <sys/types.h>

     #include <sys/socket.h>

prototype: int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

Parameters: sockfd: Using system call socket()Created socket descriptor

      addr: Represents the local address information to be bound
     
      addrlen: Length of local address information

Return value: success, message queue ID,error -1

listen function

Set listening to a socket File descriptor, which sets the number of clients that can connect to the server at the same time. Generally, only server Will call

Header file#include <sys/types.h>

     #include <sys/socket.h>

prototype: int listen(int sockfd, int backlog);

Parameters: sockfd: Using system call socket()Created socket descriptor

      backlog: server Maximum number of connections that can be accepted client quantity

Return value: success, message queue ID,error -1

accept function

wait for client The function to establish the connection is generally only server Will call

Header file#include <sys/types.h>

     #include <sys/socket.h>

prototype: int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);

Parameters: sockfd: Using system call socket()Created socket descriptor

      addr: Point to the opposite end of the established connection client Pointer to address information

      addrlen: Opposite end client Address information length

Return value: success, message queue ID,error -1

Connect function

client Active connection server Function, generally only client Will be called

Header file#include <sys/types.h>

     #include <sys/socket.h>

prototype: int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

Parameters: sockfd: Using system call socket()Created socket descriptor

      addr: Point to the opposite end of the established connection server Pointer to address information

      addrlen: Opposite end server Address information length

Return value: success, message queue ID,error -1

send function

Send data function

Header file#include <sys/types.h>

     #include <sys/socket.h>

prototype: ssize_t send(int sockfd, const void *buf, size_t len, int flags);

Parameters: sockfd: Point to the to send data socket File descriptor, file descriptor for which a connection has been established

      buf: Buffer for data to be sent

      len: Actual number of bytes of data to be sent

      flags: Generally 0 or the following macros

			MSG_DONTROUTE  Bypass routing table lookup

			MSG_DONTWAIT   Only this operation is non blocking

			MSG_OOB      Send or receive out of band data

			MSG_PEEK      Peeping at foreign news

			MSG_WAITALL    Wait for all data

Return value: success, message queue ID,error -1

recv function

Receive data function

Header file#include <sys/types.h>

     #include <sys/socket.h>

prototype: ssize_t recv(int sockfd, void *buf, size_t len, int flags);

Parameters: sockfd: File descriptor for which a connection has been established

	  buf: Buffer for receiving data

	  len: Actual number of bytes of data to be received

	  flags:Generally 0 or the following macros

          MSG_DONTROUTE  Bypass routing table lookup

          MSG_DONTWAIT   Only this operation is non blocking

          MSG_OOB      Send or receive out of band data

          MSG_PEEK      Peeping at foreign news

          MSG_WAITALL    Wait for all data

Return value: success, message queue ID,error -1

4.6.3 socket to realize interprocess communication

General steps of program implementation

Server end

1.establish socket

2.binding socket

3.Set listening

4.Waiting for client connection

5.send out/receive data 

Client side

1.establish socket

2.binding socket

3.connect

4.send out/receive data 

Server source code (refer to jz2440\process_socket\server.c)

01 /**********************************************************************
02  * Function Description: 1 The server prints the string sent by the client and sends the string back to the client
03  * Input parameters: None
04  * Output parameters: None
05  * Return value: None
06  * Modification date version number modified by
07  * -----------------------------------------------
08  * 2020/05/16       V1.0      zh(ryan)        establish
09  ***********************************************************************/
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <string.h>
17 #include <arpa/inet.h>
18 #include <sys/un.h>
19 
20 int main(int argc, char *argv[])
21 {
22     int lfd ,ret, cfd;
23     struct sockaddr_un serv, client;
24     socklen_t len = sizeof(client);
25     char buf[1024] = {0};
26     int recvlen;
27 
28     //Create socket
29     lfd = socket(AF_LOCAL, SOCK_STREAM, 0);
30     if (lfd == -1) {
31         perror("socket error");
32         return -1;
33     }
34 
35     //If the socket file exists, delete the socket file
36     unlink("server.sock");
37 
38     //Initialize server information
39     serv.sun_family = AF_LOCAL;
40     strcpy(serv.sun_path, "server.sock");
41 
42     //binding
43     ret = bind(lfd, (struct sockaddr *)&serv, sizeof(serv));
44     if (ret == -1) {
45         perror("bind error");
46         return -1;
47     }
48 
49     //Set listening, and set the number of clients that can connect to the server at the same time
50     ret = listen(lfd, 36);
51     if (ret == -1) {
52         perror("listen error");
53         return -1;
54     }
55 
56     //Waiting for client connection
57     cfd = accept(lfd, (struct sockaddr *)&client, &len);
58     if (cfd == -1) {
59         perror("accept error");
60         return -1;
61     }
62     printf("=====client bind file:%s\n", client.sun_path);
63     
64     while (1) {
65         recvlen = recv(cfd, buf, sizeof(buf), 0);
66         if (recvlen == -1) {
67             perror("recv error");
68             return -1;
69         } else if (recvlen == 0) {
70             printf("client disconnet...\n");
71             close(cfd);
72             break;
73         } else {
74             printf("server recv buf: %s\n", buf);
75             send(cfd, buf, recvlen, 0);
76         }
77     }
78     
79     close(cfd);
80     close(lfd);
81     return 0;
82 }

Client source code (refer to jz2440\process_socket\client.c)

01 /**********************************************************************
02  * Function Description: 1 The client obtains a string from the standard input, and then sends the string to the server
03  * Input parameters: None
04  * Output parameters: None
05  * Return value: None
06  * Modification date version number modified by
07  * -----------------------------------------------
08  * 2020/05/16       V1.0      zh(ryan)        establish
09  ***********************************************************************/
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <string.h>
17 #include <arpa/inet.h>
18 #include <sys/un.h>
19 
20 int main(int argc, char *argv[])
21 {
22     int lfd ,ret;
23     struct sockaddr_un serv, client;
24     socklen_t len = sizeof(client);
25     char buf[1024] = {0};
26     int recvlen;
27 
28     //Create socket
29     lfd = socket(AF_LOCAL, SOCK_STREAM, 0);
30     if (lfd == -1) {
31         perror("socket error");
32         return -1;
33     }
34 
35     //If the socket file exists, delete the socket file
36     unlink("client.sock");
37 
38     //Bind a socket file to the client
39     client.sun_family = AF_LOCAL;
40     strcpy(client.sun_path, "client.sock");
41     ret = bind(lfd, (struct sockaddr *)&client, sizeof(client));
42     if (ret == -1) {
43         perror("bind error");
44         return -1;
45     }
46 
47     //Initialize server information
48     serv.sun_family = AF_LOCAL;
49     strcpy(serv.sun_path, "server.sock");
50     //connect
51     connect(lfd, (struct sockaddr *)&serv, sizeof(serv));
52 
53     while (1) {
54         fgets(buf, sizeof(buf), stdin);
55         send(lfd, buf, strlen(buf)+1, 0);
56 
57         recv(lfd, buf, sizeof(buf), 0);
58         printf("client recv buf: %s\n", buf);
59     }
60 
61     close(lfd);
62     return 0;
63 }

JZ2440 experiment

  • compile
arm-linux-gcc server.c -o server

arm-linux-gcc client.c -o client
  • Copy to NFS file system
cp server /work/nfs_root/first_fs

cp client /work/nfs_root/first_fs
  • function

In order to see the running results of the program conveniently, the server executes in the background; The client runs in the foreground and can receive input from the terminal.

./server &

./client

4.6. 4 communication between a server and multiple client s

In actual project development, a more common scenario is that a server needs to communicate with multiple client s. This part of the implementation method is left to the readers to implement by themselves. There are many implementation methods, such as the following two methods, and of course other methods.

  1. Multi process implementation: a main process is used to detect the connection of the client. Each time a client connection is detected, a special process is created for the client to realize the communication between the two.
  2. Multithreading can also be used. A main thread is used to detect the connection of the client. Each time a client connection is detected, a special thread is created for the client to realize the communication between the two.