Article catalog
- 4 Linux interprocess communication application development
- 4.1 introduction process
- 4.2 why do processes need communication
- 4.3 pipeline communication of process communication
- 4.4 IPC communication of process communication
- 4.5 signal communication of process communication
- 4.6 socket communication of process communication
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
- The program is the basis of the generation process.
- Each execution of a program constitutes a different process.
- 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).
- 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:
- 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.
- 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.
- 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.
- After the child process ends, its parent process must reclaim all its resources, otherwise it will become a zombie process.
- 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
- 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.
- Message queuing enables two-way communication
- 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:
- 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.
- However, in online process B, you will first apply for contribution resource 2, and then apply for shared resource 1.
- 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.
- 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
- 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.
- Signal is a simulation of interrupt mechanism at the software level. It is an asynchronous communication mode.
- The Linux kernel notifies the user process through signals, and different signal types represent different events.
- 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.
- 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.
- 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.