Five communication modes between processes

Source: https://blog.csdn.net/wh_sjc/article/details/70283843

 

IPC (InterProcess Communication) refers to the communication or exchange of information between different processes.

IPC usually includes pipeline (including nameless pipeline and named pipeline), message queue, semaphore, shared storage, socket, Streams, etc. Socket and Streams support two IPC processes on different hosts.

Take C programming in Linux as an example.

1, Pipeline

Pipe, usually the nameless pipe, is the oldest form of IPC for UNIX systems.

1. Features:

  1. It is half duplex (that is, data can only flow in one direction), with fixed read and write ends.

  2. It can only be used for communication between related processes (also between parent-child processes or sibling processes).

  3. It can be regarded as a special file. For its reading and writing, you can also use ordinary read, write and other functions. But it is not a normal file, does not belong to any other file system, and only exists in memory.

1, Pipeline

Pipe, usually the nameless pipe, is the oldest form of IPC for UNIX systems.

1. Features:

  1. It is half duplex (that is, data can only flow in one direction), with fixed read and write ends.

  2. It can only be used for communication between related processes (also between parent-child processes or sibling processes).

  3. It can be regarded as a special file. For its reading and writing, you can also use ordinary read, write and other functions. But it is not a normal file, does not belong to any other file system, and only exists in memory.

2. Prototype:

1 #include <unistd.h>
2 int pipe(int fd[2]);    // Return value: 0 for success, 1 for failure

When a pipeline is established, it creates two file descriptors: fd[0] is opened for reading and fd[1] is opened for writing. As shown below:

To close the pipeline, just close the two file descriptors.

3. Examples

Pipes in a single process are of little use. Therefore, the process that usually calls pipe then calls fork, which creates the IPC channel between the parent process and the child process. As shown in the figure below:

If you want the data flow from the parent process to the child process, close the read end (fd[0]) and the write end (fd[1]) of the parent process; otherwise, you can make the data flow from the child process to the parent process.

1 #include<stdio.h>
 2 #include<unistd.h>
 3 
 4 int main()
 5 {
 6     int fd[2];  // Two file descriptors
 7     pid_t pid;
 8     char buff[20];
 9 
10     if(pipe(fd) < 0)  // Create pipe
11         printf("Create Pipe Error!\n");
12 
13     if((pid = fork()) < 0)  // Create child process
14         printf("Fork Error!\n");
15     else if(pid > 0)  // Parent process
16     {
17         close(fd[0]); // Close the reader
18         write(fd[1], "hello world\n", 12);
19     }
20     else
21     {
22         close(fd[1]); // Close write end
23         read(fd[0], buff, 20);
24         printf("%s", buff);
25     }
26 
27     return 0;
28 }

2, FIFO

FIFO, also known as named pipe, is a file type.

1. Features

  1. FIFO can exchange data between unrelated processes, unlike anonymous pipes.

  2. FIFO has a path name associated with it, which exists in the file system in the form of a special device file.

2. Archetype

1 #include <sys/stat.h>
2 // Return value: 0 for success and - 1 for error
3 int mkfifo(const char *pathname, mode_t mode);

The mode parameter is the same as that in the open function. Once a FIFO has been created, it can be manipulated with general file I/O functions.

When a FIFO is open ed, the difference between setting the non blocking flag (o ﹣ Nonblock) and not:

  • If O ﹣ Nonblock (default) is not specified, read-only open blocks the FIFO to be opened by some other process for writing. Similarly, write only open is blocked until another process opens it for reading.

  • If O "Nonblock is specified, read-only open returns immediately. Write open only returns - 1 if no process has opened the FIFO for reading, its errno is set to Enio.

3. Examples

FIFO communication is similar to using files to transfer data in the process, but FIFO type files have the characteristics of pipeline at the same time. During data reading, data is cleared in FIFO pipeline at the same time, and "first in, first out". The following example demonstrates the process of using FIFO for IPC:

write_fifo.c

1 #include<stdio.h>
 2 #include<stdlib.h>   // exit
 3 #include<fcntl.h>    // O_WRONLY
 4 #include<sys/stat.h>
 5 #include<time.h>     // time
 6 
 7 int main()
 8 {
 9     int fd;
10     int n, i;
11     char buf[1024];
12     time_t tp;
13 
14     printf("I am %d process.\n", getpid()); // Description process ID
15     
16     if((fd = open("fifo1", O_WRONLY)) < 0) // Open a FIFO with write 
17     {
18         perror("Open FIFO Failed");
19         exit(1);
20     }
21 
22     for(i=0; i<10; ++i)
23     {
24         time(&tp);  // Take the current time of the system
25         n=sprintf(buf,"Process %d's time is %s",getpid(),ctime(&tp));
26         printf("Send message: %s", buf); // Printing
27         if(write(fd, buf, n+1) < 0)  // Write to FIFO
28         {
29             perror("Write FIFO Failed");
30             close(fd);
31             exit(1);
32         }
33         sleep(1);  // Sleep for 1 second
34     }
35 
36     close(fd);  // Close FIFO file
37     return 0;
38 }

read_fifo.c

1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<errno.h>
 4 #include<fcntl.h>
 5 #include<sys/stat.h>
 6 
 7 int main()
 8 {
 9     int fd;
10     int len;
11     char buf[1024];
12 
13     if(mkfifo("fifo1", 0666) < 0 && errno!=EEXIST) // Create FIFO pipe
14         perror("Create FIFO Failed");
15 
16     if((fd = open("fifo1", O_RDONLY)) < 0)  // Open FIFO with read
17     {
18         perror("Open FIFO Failed");
19         exit(1);
20     }
21     
22     while((len = read(fd, buf, 1024)) > 0) // Read FIFO pipe
23         printf("Read message: %s", buf);
24 
25     close(fd);  // Close FIFO file
26     return 0;
27 }

Compile and run the above two files with gcc in the two terminals, and the output results are as follows:

1 [cheesezh@localhost]$ ./write_fifo 
 2 I am 5954 process.
 3 Send message: Process 5954's time is Mon Apr 20 12:37:28 2015
 4 Send message: Process 5954's time is Mon Apr 20 12:37:29 2015
 5 Send message: Process 5954's time is Mon Apr 20 12:37:30 2015
 6 Send message: Process 5954's time is Mon Apr 20 12:37:31 2015
 7 Send message: Process 5954's time is Mon Apr 20 12:37:32 2015
 8 Send message: Process 5954's time is Mon Apr 20 12:37:33 2015
 9 Send message: Process 5954's time is Mon Apr 20 12:37:34 2015
10 Send message: Process 5954's time is Mon Apr 20 12:37:35 2015
11 Send message: Process 5954's time is Mon Apr 20 12:37:36 2015
12 Send message: Process 5954's time is Mon Apr 20 12:37:37 2015

 

1 [cheesezh@localhost]$ ./read_fifo 
 2 Read message: Process 5954's time is Mon Apr 20 12:37:28 2015
 3 Read message: Process 5954's time is Mon Apr 20 12:37:29 2015
 4 Read message: Process 5954's time is Mon Apr 20 12:37:30 2015
 5 Read message: Process 5954's time is Mon Apr 20 12:37:31 2015
 6 Read message: Process 5954's time is Mon Apr 20 12:37:32 2015
 7 Read message: Process 5954's time is Mon Apr 20 12:37:33 2015
 8 Read message: Process 5954's time is Mon Apr 20 12:37:34 2015
 9 Read message: Process 5954's time is Mon Apr 20 12:37:35 2015
10 Read message: Process 5954's time is Mon Apr 20 12:37:36 2015
11 Read message: Process 5954's time is Mon Apr 20 12:37:37 2015

The above example can be extended to client process server process As an example of communication, write FIFO is similar to the client. It can open multiple clients to send request information to a server. Read FIFO is similar to the server. It timely monitors the read end of FIFO. When there is data, it reads out and processes it. However, a key problem is that each client must know the FIFO interface provided by the server in advance, as shown in the figure below This arrangement:

3, Message queuing

Message queue, which is the link table of messages, is stored in the kernel. A message queue is identified by an identifier (queue ID).

1. Features

  1. Message queues are record oriented, where messages have a specific format and priority.

  2. Message queuing is independent of the sending and receiving processes. When the process terminates, the message queue and its contents are not deleted.

  3. Message queue can realize random query of messages. Messages can be read not only in first in first out order, but also by message type.

2. Archetype

1 #include <sys/msg.h>
2 // Create or open message queue: queue ID returned successfully, and - 1 returned failed
3 int msgget(key_t key, int flag);
4 // Add message: 0 for success and - 1 for failure
5 int msgsnd(int msqid, const void *ptr, size_t size, int flag);
6 // Read message: the length of message data is returned successfully, and - 1 is returned in case of failure
7 int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);
8 // Control message queue: success returns 0, failure returns - 1
9 int msgctl(int msqid, int cmd, struct msqid_ds *buf);

msgget creates a new message queue in two cases:

  • If there is no message queue corresponding to the key value key, and the flag contains the IPC "creat flag bit.
  • The key parameter is IPC? Private.

When the msgrcv function reads the message queue, the type parameter has the following situations:

  • type == 0, return the first message in the queue;
  • Type > 0, returns the first message of type in the queue;
  • Type < 0, returns the message in the queue whose message type value is less than or equal to the absolute value of type. If there are multiple messages, the message with the lowest type value is taken.

As you can see, when the type value is not 0, it is used to read messages in non FIFO order. You can also think of type as the weight of priority. (please Google other parameters)

3. Examples

The following is a simple example of using message queuing for IPC. The server program has been waiting for a specific type of message. After receiving the message, it sends another specific type of message as the feedback. The client reads the feedback and prints it out.

msg_server.c

1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <sys/msg.h>
 4 
 5 // Used to create a unique key
 6 #define MSG_FILE "/etc/passwd"
 7 
 8 // Message structure
 9 struct msg_form {
10     long mtype;
11     char mtext[256];
12 };
13 
14 int main()
15 {
16     int msqid;
17     key_t key;
18     struct msg_form msg;
19     
20     // Get key value
21     if((key = ftok(MSG_FILE,'z')) < 0)
22     {
23         perror("ftok error");
24         exit(1);
25     }
26 
27     // Print key value
28     printf("Message Queue - Server key is: %d.\n", key);
29 
30     // Create message queue
31     if ((msqid = msgget(key, IPC_CREAT|0777)) == -1)
32     {
33         perror("msgget error");
34         exit(1);
35     }
36 
37     // Print message queue ID and process ID
38     printf("My msqid is: %d.\n", msqid);
39     printf("My pid is: %d.\n", getpid());
40 
41     // Loop read message
42     for(;;) 
43     {
44         msgrcv(msqid, &msg, 256, 888, 0);// Return the first message of type 888
45         printf("Server: receive msg.mtext is: %s.\n", msg.mtext);
46         printf("Server: receive msg.mtype is: %d.\n", msg.mtype);
47 
48         msg.mtype = 999; // Message type received by client
49         sprintf(msg.mtext, "hello, I'm server %d", getpid());
50         msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
51     }
52     return 0;
53 }

msg_client.c

1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <sys/msg.h>
 4 
 5 // Used to create a unique key
 6 #define MSG_FILE "/etc/passwd"
 7 
 8 // Message structure
 9 struct msg_form {
10     long mtype;
11     char mtext[256];
12 };
13 
14 int main()
15 {
16     int msqid;
17     key_t key;
18     struct msg_form msg;
19 
20     // Get key value
21     if ((key = ftok(MSG_FILE, 'z')) < 0) 
22     {
23         perror("ftok error");
24         exit(1);
25     }
26 
27     // Print key value
28     printf("Message Queue - Client key is: %d.\n", key);
29 
30     // Open message queue
31     if ((msqid = msgget(key, IPC_CREAT|0777)) == -1) 
32     {
33         perror("msgget error");
34         exit(1);
35     }
36 
37     // Print message queue ID and process ID
38     printf("My msqid is: %d.\n", msqid);
39     printf("My pid is: %d.\n", getpid());
40 
41     // Add message of type 888
42     msg.mtype = 888;
43     sprintf(msg.mtext, "hello, I'm client %d", getpid());
44     msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
45 
46     // Read messages of type 777
47     msgrcv(msqid, &msg, 256, 999, 0);
48     printf("Client: receive msg.mtext is: %s.\n", msg.mtext);
49     printf("Client: receive msg.mtype is: %d.\n", msg.mtype);
50     return 0;
51 }

4, Semaphore

semaphore is a counter, which is different from IPC structure. Semaphores are used to realize mutual exclusion and synchronization between processes, not to store inter process communication data.

1. Features

  1. Semaphores are used for inter process synchronization. To transfer data between processes, shared memory is needed.

  2. Semaphores are based on the PV operation of the operating system, and the operation of the program on semaphores is atomic operation.

  3. Each PV operation on a semaphore is not limited to adding or subtracting 1 to the semaphore value, but also to adding or subtracting any positive integer.

  4. Support semaphore group.

2. Archetype

The simplest semaphore is a variable that can only take 0 and 1, which is also the most common form of semaphore, called Binary Semaphore. A semaphore that can take multiple positive integers is called a general semaphore.

In Linux, semaphore functions operate on a common semaphore array, rather than a single binary semaphore.

1 #include <sys/sem.h>
2 // Create or get a semaphore group: if the semaphore set ID is returned successfully, the failure returns - 1
3 int semget(key_t key, int num_sems, int sem_flags);
4 // Operate on semaphore group, change semaphore value: return 0 for success, return - 1 for failure
5 int semop(int semid, struct sembuf semoparray[], size_t numops);  
6 // Information about control semaphores
7 int semctl(int semid, int sem_num, int cmd, ...);

When semget creates a new semaphore set, it must specify the number of semaphores in the set (i.e. num UU SEMS), which is usually 1; if it refers to an existing set, specify num UU SEMS as 0.

In the semop function, the sembuf structure is defined as follows:

1 struct sembuf 
2 {
3     short sem_num; // The corresponding sequence number in semaphore group, 0-sem ﹣ nums-1
4     short sem_op;  // Change of semaphore value in one operation
5     short sem_flg; // IPC_NOWAIT, SEM_UNDO
6 }

Where SEM? OP is the change amount of semaphore in one operation:

  • If SEM? OP > 0, it means that the process releases the corresponding number of resources and adds the value of SEM? OP to the value of semaphore. If there are processes sleeping waiting for this semaphore, wrap them.

  • If SEM UUP < 0, the resource requesting the absolute value of SEM UUP.

    • If the corresponding number of resources can meet the request, the semaphore value is subtracted from the absolute value of SEM Ou OP, and the function returns successfully.
    • When the corresponding number of resources cannot meet the request, this operation is related to SEM ﹣ Flg.
      • If SEM? Flg specifies IPC? Nowait, the error of semop function returns EAGAIN.
      • If SEM? Flg does not specify IPC? Nowait, the semaphore's semncnt value is increased by 1, and then the process is suspended until:
        1. When the corresponding number of resources can meet the request, the semaphore's semncnt value is reduced by 1, and the semaphore's value is reduced by the absolute value of SEM Ou Op. Successful return;
        2. This semaphore is deleted, and error in function smeop returns to EIDRM;
        3. The process captures the signal and returns it from the signal processing function. In this case, the semant value of this semaphore is reduced by 1, and the error of function semop returns EINTR
  • If SEM Ou OP = = 0, the process is blocked until the corresponding value of semaphore is 0:

    • When the semaphore is already 0, the function returns immediately.
    • If the semaphore value is not 0, the function action is determined according to SEM ﹣ flg:
      • If SEM? Flg specifies IPC? Nowait, an error is returned to EAGAIN.
      • If SEM? Flg does not specify IPC? Nowait, the semaphore's semncnt value is increased by 1, and then the process is suspended until:
        1. When the semaphore value is 0, reduce the semaphore semzcnt value by 1, and the function semop returns successfully;
        2. This semaphore is deleted, and error in function smeop returns to EIDRM;
        3. The process captures the signal and returns it from the signal processing function. In this case, the semant value of this semaphore is reduced by 1, and the error of function semop returns EINTR

There are many commands in the semctl function. Here are two common ones:

  • SETVAL: used to initialize the semaphore to a known value. The required value is passed as a val member of the union semun. The semaphore needs to be set before it is used for the first time.
  • IPC? Rmid: delete a semaphore set. If you do not delete the semaphore, it will continue to exist in the system, even if the program has exited, it may cause problems the next time you run this program, and the semaphore is a limited resource.

3. Examples

1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<sys/sem.h>
  4 
  5 // Consortium for semctl initialization
  6 union semun
  7 {
  8     int              val; /*for SETVAL*/
  9     struct semid_ds *buf;
 10     unsigned short  *array;
 11 };
 12 
 13 // Initialization semaphore
 14 int init_sem(int sem_id, int value)
 15 {
 16     union semun tmp;
 17     tmp.val = value;
 18     if(semctl(sem_id, 0, SETVAL, tmp) == -1)
 19     {
 20         perror("Init Semaphore Error");
 21         return -1;
 22     }
 23     return 0;
 24 }
 25 
 26 // P operation:
 27 //    If the semaphore value is 1, obtain the resource and set the semaphore value - 1 
 28 //    If the semaphore value is 0, the process hangs waiting
 29 int sem_p(int sem_id)
 30 {
 31     struct sembuf sbuf;
 32     sbuf.sem_num = 0; /*No*/
 33     sbuf.sem_op = -1; /*P operation*/
 34     sbuf.sem_flg = SEM_UNDO;
 35 
 36     if(semop(sem_id, &sbuf, 1) == -1)
 37     {
 38         perror("P operation Error");
 39         return -1;
 40     }
 41     return 0;
 42 }
 43 
 44 // V operation:
 45 //    Release resources and add semaphore value + 1
 46 //    Wake up processes if they are pending
 47 int sem_v(int sem_id)
 48 {
 49     struct sembuf sbuf;
 50     sbuf.sem_num = 0; /*No*/
 51     sbuf.sem_op = 1;  /*V operation*/
 52     sbuf.sem_flg = SEM_UNDO;
 53 
 54     if(semop(sem_id, &sbuf, 1) == -1)
 55     {
 56         perror("V operation Error");
 57         return -1;
 58     }
 59     return 0;
 60 }
 61 
 62 // Delete semaphore set
 63 int del_sem(int sem_id)
 64 {
 65     union semun tmp;
 66     if(semctl(sem_id, 0, IPC_RMID, tmp) == -1)
 67     {
 68         perror("Delete Semaphore Error");
 69         return -1;
 70     }
 71     return 0;
 72 }
 73 
 74 
 75 int main()
 76 {
 77     int sem_id;  // Semaphore set ID
 78     key_t key;  
 79     pid_t pid;
 80 
 81     // Get key value
 82     if((key = ftok(".", 'z')) < 0)
 83     {
 84         perror("ftok error");
 85         exit(1);
 86     }
 87 
 88     // Create a semaphore set with only one semaphore
 89     if((sem_id = semget(key, 1, IPC_CREAT|0666)) == -1)
 90     {
 91         perror("semget error");
 92         exit(1);
 93     }
 94 
 95     // Initialization: the initial value is set to 0, and the resource is occupied
 96     init_sem(sem_id, 0);
 97 
 98     if((pid = fork()) == -1)
 99         perror("Fork Error");
100     else if(pid == 0) /*Subprocess*/ 
101     {
102         sleep(2);
103         printf("Process child: pid=%d\n", getpid());
104         sem_v(sem_id);  /*Release resources*/
105     }
106     else  /*Parent process*/
107     {
108         sem_p(sem_id);   /*Waiting for resources*/
109         printf("Process father: pid=%d\n", getpid());
110         sem_v(sem_id);   /*Release resources*/
111         del_sem(sem_id); /*Delete semaphore set*/
112     }
113     return 0;
114 }

In the above example, if the semaphore is not added, the parent process will finish execution first. The semaphore is added here to let the parent process wait for the child process to finish executing before executing.

5, Shared memory

Shared Memory refers to two or more processes sharing a given storage area.

1. Features

  1. Shared memory is the fastest type of IPC, because processes access memory directly.

  2. Because multiple processes can operate at the same time, synchronization is required.

  3. Semaphores + shared memory are usually used together. Semaphores are used to synchronize access to shared memory.

2. Archetype

1 #include <sys/shm.h>
2 // Create or get a shared memory: return shared memory ID successfully, return - 1 failed
3 int shmget(key_t key, size_t size, int flag);
4 // Connect shared memory to address space of current process: return pointer to shared memory successfully, return - 1 failed
5 void *shmat(int shm_id, const void *addr, int flag);
6 // Disconnect from shared memory: success returns 0, failure returns - 1
7 int shmdt(void *addr); 
8 // Information about controlling shared memory: 0 for success and - 1 for failure
9 int shmctl(int shm_id, int cmd, struct shmid_ds *buf);

When you use the shmget function to create a piece of shared memory, you must specify its size; if you reference an existing shared memory, you specify the size as 0.

When a piece of shared memory is created, it cannot be accessed by any process. The shmat function must be used to connect the shared memory to the address space of the current process. After the connection is successful, the shared memory area object is mapped to the address space of the calling process, and then it can be accessed like the local space.

The shmdt function is used to disconnect the connection established by shmat. Note that this is not to remove the shared memory from the system, only that the current process can no longer access the shared memory.

The shmctl function can perform various operations on the shared memory, and perform corresponding operations according to the parameter cmd. A common one is IPC? Rmid (remove the shared memory from the system).

3. Examples

In the following example, the combination of shared memory + semaphore + message queue is used to realize the communication between the server process and the client process.

  • Shared memory is used to transfer data;
  • Semaphores are used for synchronization;
  • Message queuing is used to notify the server to read after the client modifies the shared memory.

server.c

1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<sys/shm.h>  // shared memory
  4 #include<sys/sem.h>  // semaphore
  5 #include<sys/msg.h>  // message queue
  6 #include<string.h>   // memcpy
  7 
  8 // Message queue structure
  9 struct msg_form {
 10     long mtype;
 11     char mtext;
 12 };
 13 
 14 // Consortium for semctl initialization
 15 union semun
 16 {
 17     int              val; /*for SETVAL*/
 18     struct semid_ds *buf;
 19     unsigned short  *array;
 20 };
 21 
 22 // Initialization semaphore
 23 int init_sem(int sem_id, int value)
 24 {
 25     union semun tmp;
 26     tmp.val = value;
 27     if(semctl(sem_id, 0, SETVAL, tmp) == -1)
 28     {
 29         perror("Init Semaphore Error");
 30         return -1;
 31     }
 32     return 0;
 33 }
 34 
 35 // P operation:
 36 //  If the semaphore value is 1, obtain the resource and set the semaphore value - 1 
 37 //  If the semaphore value is 0, the process hangs waiting
 38 int sem_p(int sem_id)
 39 {
 40     struct sembuf sbuf;
 41     sbuf.sem_num = 0; /*No*/
 42     sbuf.sem_op = -1; /*P operation*/
 43     sbuf.sem_flg = SEM_UNDO;
 44 
 45     if(semop(sem_id, &sbuf, 1) == -1)
 46     {
 47         perror("P operation Error");
 48         return -1;
 49     }
 50     return 0;
 51 }
 52 
 53 // V operation:
 54 //  Release resources and add semaphore value + 1
 55 //  Wake up processes if they are pending
 56 int sem_v(int sem_id)
 57 {
 58     struct sembuf sbuf;
 59     sbuf.sem_num = 0; /*No*/
 60     sbuf.sem_op = 1;  /*V operation*/
 61     sbuf.sem_flg = SEM_UNDO;
 62 
 63     if(semop(sem_id, &sbuf, 1) == -1)
 64     {
 65         perror("V operation Error");
 66         return -1;
 67     }
 68     return 0;
 69 }
 70 
 71 // Delete semaphore set
 72 int del_sem(int sem_id)
 73 {
 74     union semun tmp;
 75     if(semctl(sem_id, 0, IPC_RMID, tmp) == -1)
 76     {
 77         perror("Delete Semaphore Error");
 78         return -1;
 79     }
 80     return 0;
 81 }
 82 
 83 // Create a semaphore set
 84 int creat_sem(key_t key)
 85 {
 86     int sem_id;
 87     if((sem_id = semget(key, 1, IPC_CREAT|0666)) == -1)
 88     {
 89         perror("semget error");
 90         exit(-1);
 91     }
 92     init_sem(sem_id, 1);  /*The initial value is set to 1. The resource is not occupied*/
 93     return sem_id;
 94 }
 95 
 96 
 97 int main()
 98 {
 99     key_t key;
100     int shmid, semid, msqid;
101     char *shm;
102     char data[] = "this is server";
103     struct shmid_ds buf1;  /*Used to delete shared memory*/
104     struct msqid_ds buf2;  /*For deleting message queues*/
105     struct msg_form msg;  /*Message queuing is used to inform the other party that the shared memory has been updated*/
106 
107     // Get key value
108     if((key = ftok(".", 'z')) < 0)
109     {
110         perror("ftok error");
111         exit(1);
112     }
113 
114     // Create shared memory
115     if((shmid = shmget(key, 1024, IPC_CREAT|0666)) == -1)
116     {
117         perror("Create Shared Memory Error");
118         exit(1);
119     }
120 
121     // Connect shared memory
122     shm = (char*)shmat(shmid, 0, 0);
123     if((int)shm == -1)
124     {
125         perror("Attach Shared Memory Error");
126         exit(1);
127     }
128 
129 
130     // Create message queue
131     if ((msqid = msgget(key, IPC_CREAT|0777)) == -1)
132     {
133         perror("msgget error");
134         exit(1);
135     }
136 
137     // Create semaphore
138     semid = creat_sem(key);
139     
140     // Read data
141     while(1)
142     {
143         msgrcv(msqid, &msg, 1, 888, 0); /*Read messages of type 888*/
144         if(msg.mtext == 'q')  /*quit - Jump out of the loop*/ 
145             break;
146         if(msg.mtext == 'r')  /*read - Read shared memory*/
147         {
148             sem_p(semid);
149             printf("%s\n",shm);
150             sem_v(semid);
151         }
152     }
153 
154     // Disconnect
155     shmdt(shm);
156 
157     /*Delete shared memory, message queues, semaphores*/
158     shmctl(shmid, IPC_RMID, &buf1);
159     msgctl(msqid, IPC_RMID, &buf2);
160     del_sem(semid);
161     return 0;
162 }

client.c

1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<sys/shm.h>  // shared memory
  4 #include<sys/sem.h>  // semaphore
  5 #include<sys/msg.h>  // message queue
  6 #include<string.h>   // memcpy
  7 
  8 // Message queue structure
  9 struct msg_form {
 10     long mtype;
 11     char mtext;
 12 };
 13 
 14 // Consortium for semctl initialization
 15 union semun
 16 {
 17     int              val; /*for SETVAL*/
 18     struct semid_ds *buf;
 19     unsigned short  *array;
 20 };
 21 
 22 // P operation:
 23 //  If the semaphore value is 1, obtain the resource and set the semaphore value - 1 
 24 //  If the semaphore value is 0, the process hangs waiting
 25 int sem_p(int sem_id)
 26 {
 27     struct sembuf sbuf;
 28     sbuf.sem_num = 0; /*No*/
 29     sbuf.sem_op = -1; /*P operation*/
 30     sbuf.sem_flg = SEM_UNDO;
 31 
 32     if(semop(sem_id, &sbuf, 1) == -1)
 33     {
 34         perror("P operation Error");
 35         return -1;
 36     }
 37     return 0;
 38 }
 39 
 40 // V operation:
 41 //  Release resources and add semaphore value + 1
 42 //  Wake up processes if they are pending
 43 int sem_v(int sem_id)
 44 {
 45     struct sembuf sbuf;
 46     sbuf.sem_num = 0; /*No*/
 47     sbuf.sem_op = 1;  /*V operation*/
 48     sbuf.sem_flg = SEM_UNDO;
 49 
 50     if(semop(sem_id, &sbuf, 1) == -1)
 51     {
 52         perror("V operation Error");
 53         return -1;
 54     }
 55     return 0;
 56 }
 57 
 58 
 59 int main()
 60 {
 61     key_t key;
 62     int shmid, semid, msqid;
 63     char *shm;
 64     struct msg_form msg;
 65     int flag = 1; /*while Cyclic condition*/
 66 
 67     // Get key value
 68     if((key = ftok(".", 'z')) < 0)
 69     {
 70         perror("ftok error");
 71         exit(1);
 72     }
 73 
 74     // Get shared memory
 75     if((shmid = shmget(key, 1024, 0)) == -1)
 76     {
 77         perror("shmget error");
 78         exit(1);
 79     }
 80 
 81     // Connect shared memory
 82     shm = (char*)shmat(shmid, 0, 0);
 83     if((int)shm == -1)
 84     {
 85         perror("Attach Shared Memory Error");
 86         exit(1);
 87     }
 88 
 89     // Create message queue
 90     if ((msqid = msgget(key, 0)) == -1)
 91     {
 92         perror("msgget error");
 93         exit(1);
 94     }
 95 
 96     // Acquire semaphore
 97     if((semid = semget(key, 0, 0)) == -1)
 98     {
 99         perror("semget error");
100         exit(1);
101     }
102     
103     // Write data
104     printf("***************************************\n");
105     printf("*                 IPC                 *\n");
106     printf("*    Input r to send data to server.  *\n");
107     printf("*    Input q to quit.                 *\n");
108     printf("***************************************\n");
109     
110     while(flag)
111     {
112         char c;
113         printf("Please input command: ");
114         scanf("%c", &c);
115         switch(c)
116         {
117             case 'r':
118                 printf("Data to send: ");
119                 sem_p(semid);  /*Access resources*/
120                 scanf("%s", shm);
121                 sem_v(semid);  /*Release resources*/
122                 /*Clear standard input buffer*/
123                 while((c=getchar())!='\n' && c!=EOF);
124                 msg.mtype = 888;  
125                 msg.mtext = 'r';  /*Send message to inform server to read data*/
126                 msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
127                 break;
128             case 'q':
129                 msg.mtype = 888;
130                 msg.mtext = 'q';
131                 msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
132                 flag = 0;
133                 break;
134             default:
135                 printf("Wrong input!\n");
136                 /*Clear standard input buffer*/
137                 while((c=getchar())!='\n' && c!=EOF);
138         }
139     }
140 
141     // Disconnect
142     shmdt(shm);
143 
144     return 0;
145 }

Note: when scanf() enters characters or strings, the buffer is left \ n, so you need to clear the buffer of standard input after each input operation. However, since the gcc compiler does not support fflush(stdin) (it is only an extension of standard C), we use an alternative:

1 while((c=getchar())!='\n' && c!=EOF);

 

 

Summary of five communication modes

 

 

1. Pipeline: slow speed, limited capacity, only parent-child process can communicate

2.FIFO : any process can communicate with each other, but it is slow

3. Message queuing: the capacity is limited by the system, and it should be noted that when reading for the first time, the problem of not reading the data last time should be considered

4. Semaphore: unable to deliver complex messages, only used for synchronization

5. Shared memory area: it is easy to control the capacity and speed, but it needs to be synchronized. For example, when one process is writing, another process should pay attention to the problem of reading and writing, which is equivalent to the thread safety in the thread. Of course, shared memory area can also be used for inter thread communication, but it is unnecessary. The threads have already shared a piece of memory in the same process

 

 

Keywords: socket Linux Unix Programming

Added by ValdouaD on Mon, 18 May 2020 09:00:24 +0300