system V IPC inter-process communication mechanism is exhausted

content

Essential IPCS command parsing

ipcs  

ipcrm   

Linux IPC message queue

msgget

msgsnd

msgrcv

msgctl

Linux IPC semaphores

understand semaphores

semget

semop

semctl

Linux IPC Shared Memory

shmget

shmat

shmdt 

shmctl


This article is purely Xiaojie's review of the core key knowledge in Linux after he finished learning the Linux operating system. 
Xiaojie will try to sort it out as clearly as possible, everyone learns and progresses together, knowledge is not high or low, computer Xiaojie's study is also a    
quantitative change ---> the process of qualitative change
is a reward for diligence, water drops through the stone, and at different stages do what I can do at the current stage, at least I don't spend time paralyzed on the bed in the bedroom---- ---- I hope everyone can learn something and gain something

Essential IPCS command parsing

  • ipcs  

Function: View information about the system V IPC inter-process communication facility

Common options

-a : View all communication facility information, default -a without option

-m : specify to view shared memory

-q : specify to view the message queue

-s : specify to view the semaphore 

  • ipcrm   

Function: Delete System V Interprocess Communication (IPC) objects and associated data structures

Usage: ipcrm option id number

Common options

-m : delete shared memory

-q : delete the message queue

-s : delete semaphore

eg : Specifies to delete IPC objects with shmid = 18 and msqid = 8.

Linux IPC message queue

Message queues provide a way for a process to transfer a block of data to another process

Message Queuing Advantages: Compared to Pipeline Communication

  1.  Avoid the synchronous blocking problem of pipeline communication eg: The reading process reads data from the pipeline, but there is no readable data in the pipeline. At this moment, the reading process needs to synchronously block and wait for the writing process to write data.
  2. The message queue does not have to receive data in a first-in, first-out manner like a pipeline, and the message queue can selectively receive data according to the type (type value).
  • msgget

 

Function: Create a global message queue IPC object. (kernel data structure)

Parameter analysis: key uniquely identifies, uniquely identifies a global message queue. msgflg specifies permissions

Return value: Returns a positive integer on success, representing the handle of the message queue, returns -1 on failure, and sets errno

Insert a permission introduction: r : read permission w : writable permission X : executable permission. The so-called 9-bit permission flag is aimed at: the file owner, the file owner group, and other personnel's access permission restrictions

  • msgsnd

Function: send a message to the message queue

Parametric Analysis: 

  1. The message queue handle returned by msgget before msqid
  2. msgp pointer, pointing to a message structure to be sent, the structure is as follows
  3. msgsz The storage space of the message text, which can store up to msgsz bytes of text
  4. msgflg controls the form of sending messages eg: The commonly used IPC_NOWAIT means non-blocking sending, which means that normally if non-blocking is not set, if the message queue is full, it will block and wait, but if IPC_NOWAIT is set, it will return immediately, and set errno = EAGAIN
struct msgbuf {
    long mtype;       /* message type, must be > 0 */
    char mtext[1];    /* message data */
};
//我们必须按照这个形式设置msgbuff

Return value: return 0 on success, -1 on failure and set errno

  • msgrcv

Function: Receive a message from the message queue

Parametric analysis:

  1. The message queue handle returned by msgget before msqid
  2. msgp pointer, pointing to a message structure to be sent, the structure is as follows
  3. msgsz The storage space of the message text, which can store up to msgsz bytes of text
  4. msgflg controls the form of received messages eg: The commonly used IPC_NOWAIT means non-blocking reception, which means that if non-blocking is not set normally, if there is no message in the message queue, it will block and wait, but if IPC_NOWAIT is set, it will return immediately, and set errno = ENOMSG

msgtyp specifies what type of message is accepted:

  1. Equal to 0, specify to receive the first message of the message queue
  2. Greater than 0, specify to receive the first message in the message queue whose type is equal to msgtyp
  3. msgtyp is less than 0, read the first message in the message queue whose type value is smaller than the absolute value of msgtyp

Return value: return the length of the received message body on success, -1 on failure and set errno

  • msgctl

Function: Control message queue, set up message queue IPC, operate

Parametric analysis:

  1. The message queue handle returned by msgget before msqid
  2. cmd For the operation of the message queue, the most commonly used is IPC_RMID, delete the message queue identifier

Return value: return 0 on success, -1 on failure and set errno

Practical case, communication between parent and child processes, the parent process writes messages to the message queue, and the child process receives messages according to the type of the message

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <sys/wait.h>



#define ERR_EXIT(m) \
    do { perror(m); exit(EXIT_FAILURE); } while (0);

#define TEXTSIZE 1024


//消息结构体
typedef struct msgbuff {
	long int type;
	char  text[TEXTSIZE];
} msgbuff;



int main(int argc, char* argv[]) {
	if (2 != argc) {
		fprintf(stderr, "usage: %s <filepath>", argv[0]);
		exit(EXIT_FAILURE);
	}

	key_t key = ftok(argv[1], 'q');
	pid_t pid = fork();
	int msgid;


	if (pid == -1) {
		ERR_EXIT("fork");
	}

	if (pid == 0) {
		//son process
		//获取消息队列
		msgid = msgget(key, 0666 | IPC_CREAT);

		msgbuff msg;

		//然后接收类型为1的消息并且打印
		int read_size = msgrcv(msgid, &msg, TEXTSIZE, 1, 0);
		if (-1 == read_size) {
			ERR_EXIT("msgrcv");
		}

		printf("type: %d, text: %s\n", msg.type, msg.text);

		memset(&msg, 0, sizeof(msg));
		//再接收一下类型为2的消息并且打印
		read_size = msgrcv(msgid, &msg, TEXTSIZE, 2, 0);
		if (-1 == read_size) {
			ERR_EXIT("msgrcv");
		}

		printf("type: %d, text: %s\n", msg.type, msg.text);

	} else {
		//fa process
		//创建消息队列
		msgid = msgget(key, 0666 | IPC_CREAT);
		//创建消息结构体
		msgbuff msg1, msg2;
		memset(&msg1, 0, sizeof(msg1));
		memset(&msg1, 0, sizeof(msg2));

		msg1.type = 1;
		memcpy(msg1.text, "hello msg queue I am type1", strlen("hello msg queue I am type1"));
		msgsnd(msgid, &msg1, TEXTSIZE, 0);

		msg2.type = 2;
		memcpy(msg2.text, "hello msg queue, I am type2", strlen("hello msg queue, I am type2"));
		msgsnd(msgid, &msg2, TEXTSIZE, 0);

		waitpid(pid, NULL, 0);

    	if (-1 == msgctl(msgid, IPC_RMID, NULL)) {
        	ERR_EXIT("msgctl");
        	exit(1);
    	}
	}

	return 0;
}

Linux IPC semaphores

  • understand semaphores

A semaphore is a resource, a critical resource, that exists for synchronization and mutual exclusion between multiple processes or multiple threads.

The semaphore itself is also an IPC object, which is a critical shared resource, which enables communication between multiple processes or multiple threads.

The semaphore itself is an atomic counter, which means that the operation of the semaphore is inherently atomic, so there is no need to worry about the problem of multi-process or multi-threaded mutually exclusive write operations.

What is synchronization problem: Synchronization, the nature of synchronization between two events, means that there is a conditional constraint between the two events, and there is a waiting relationship. For example, A must occur before B occurs, otherwise B can only Suspend waiting, then A and B are said to be synchronous

The most common synchronization problem in Linux: Condition variables, a condition is met before an operation can be performed. Suspend and wait until the condition is met, and the satisfaction of the condition actually depends on the occurrence of another event.

When communicating, both parties must also maintain synchronization. For example, shared memory needs to use a synchronization mechanism such as a semaphore to maintain synchronization between the processes of both parties.

What is the mutual exclusion problem: When multiple processes or threads write to the same resource, the write operations must be mutually exclusive, that is, only one process or thread is allowed to perform one write operation on this resource at the same time.

  • semget

Function: Create or obtain a semaphore set IPC object

Parametric analysis:

  1. key : identifies the global semaphore set
  2. nsems: Indicates the number of semaphores in the semaphore set to be created/acquired. If the semaphore is to be created, this value must be specified. If you are getting an existing semaphore, you can set it to 0.
  3. semflg: Specify permissions

Insert a permission introduction: r : read permission w : writable permission X : executable permission. The so-called 9-bit permission flag is aimed at: the file owner, the file owner group, and other personnel's access permission restrictions

Return value: return the handle of the signal set on success, -1 on failure and set errno

  • semop

Function: perform PV operation on semaphore resources and change the value of semaphore

Parametric analysis:

  1. The handle of the semaphore collection returned by semget before semid
  2. nsops: The nsops parameter specifies the number of operations to be performed, that is, the number of elements in the sops array. semop performs operations on each element in the array sops in order of the array, and this process is an atomic operation.
  3. sops structure pointer, pointing to the first address of the struct sembuf array The structure is defined as follows
    struct sembuf
    {
      unsigned short int sem_num;/* semaphore number */
      short int sem_op; /* semaphore operation */
      short int sem_flg;/* operation flag */
    };
    if (sem_op is positive integer)  
        add the val to semval
    if (sem_op is zero) 
        wait-for-zero" operation
    if (sem_op is less than zero) 
        add the val to semval (加负数, 相当于减)
  4. The optional values ​​of sem_flg are IPC_NOWAIT, SEM_UNDO. IPC_NOWAIT means that the semop call returns immediately regardless of whether the semaphore set operation is successful or not. And set errno = EAGAIN, SEM_UNDO means that when the process exits, cancel the ongoing semop operation      

Return value: return 0 on success, -1 on failure and set errno

  • semctl

Function: operate on the specified semaphore IPC

Parametric analysis:

  1. The handle of the semaphore collection returned by semget before semid
  2. semnum specifies the number of the operation semaphore in the semaphore set, (The semaphores in a set are numbered starting at 0. That is to say, the semaphore number in the set starts from 0. 
  3. The operation performed by cmd on the specified semaphore
  4. Some commands need to pass in the fourth parameter. This parameter type is defined by the user, but the kernel gives it a fixed form
 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) */
 };

Commonly used cmd lists:

  1. SETVAL sets the semval value of the semaphore to semun.val

  2. IPC_RMID immediately removes the semaphore set and wakes up all processes waiting for the semaphore set

Return value: return 0 on success, -1 on failure and set errno

Practical case: Using semaphores to achieve a simple synchronous waiting and communication between parent and child processes, that is, using PV to achieve the same effect as locks, to achieve synchronous printing of parent and child processes, and no random entry (reentrant in the middle of assembly instructions) problem, guaranteed atomic operation

#include <sys/types.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <sys/wait.h>


#define ERR_EXIT(m) \
    do { perror(m); exit(EXIT_FAILURE); } while (0);

//联合结构体


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*/
};


int initsem(key_t key) {
    int semid = -1;
    //创建一个信号量集合IPC, 其中包含一个信号量
    if (-1 == (semid = semget(key, 1, 0666 | IPC_CREAT))) {
        ERR_EXIT("semget");
    }
    //设置信号量val
    union semun sem_un;
    sem_un.val = 1;

    if (-1 == semctl(semid, 0, SETVAL, sem_un)) {
        ERR_EXIT("semctl");
    }

    return semid;
}

//销毁
void destorysem(int semid) {

    if (-1 == semctl(semid, 0, IPC_RMID)) {
        ERR_EXIT("semctl destorysem");
    }
}

void P(int semid) {
    //因为信号量集合中就只是一个
    //所以sembuf无需设置为vector
    struct sembuf op;//操作
    op.sem_num = 0;
    op.sem_op = -1;// - 1
    op.sem_flg = SEM_UNDO;
    if (-1 == semop(semid, &op, 1)) {
        ERR_EXIT("semop P");
    }
}

void V(int semid) {
    struct sembuf op;//操作
    op.sem_num = 0;
    op.sem_op = 1;// + 1
    op.sem_flg = SEM_UNDO;
    if (-1 == semop(semid, &op, 1)) {
        ERR_EXIT("semop V");
    }

}

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

    if (2 != argc) {
        fprintf(stderr, "usage: %s <filepath>", argv[0]);
        exit(EXIT_FAILURE);
    }

    //1.获取唯一标识key
    key_t key = ftok(argv[1], 'a');

    //2. 创建信号量集合
    int semid = initsem(key);
    int cnt = 10;
    pid_t pid = fork();
    if (pid == -1) {
        ERR_EXIT("fork");
    }

    if (pid == 0) {
        //son process
      while (cnt > 0) {
         // P(semid);
          printf("I am son process\n");
          sleep(1);
          //V(semid);
          cnt -= 1; 
      }

    } else {
      //fa process
      while (cnt > 0) { 
        //P(semid);
        printf("I am fa process\n");
        sleep(1);
        //V(semid);
        cnt -= 1;
      }
      waitpid(pid, NULL, 0);
      destorysem(semid);
    }


    return 0;
}

Linux IPC Shared Memory

Shared memory is the fastest IPC mechanism because it does not involve any data transfer. Instead, it uses a common physical memory map and mounts it to the process address space, so that multiple processes can see the same physical memory for communication. .

However, there are also certain drawbacks, that is, synchronization cannot be supported, and we need to use other communication mechanisms to synchronize the process's access to shared memory

eg : My process A has written data to the shared memory and needs to communicate with process B to establish synchronization. At this time, there is a big problem. How can process B know that process A has written data.    At this time, we You need to use semaphore to achieve synchronization. ---    This is very important to understand why the use of shared memory needs to cooperate with other communication mechanisms to complete synchronization, because shared memory itself does not support synchronous access. Unlike semaphores, they support atomic operations. of.

 

  • shmget

 

Function: apply for shared memory

shmflag : 9-bit permission flag: we usually use 0x666 | IPC_CREAT | IPC_EXCL

Insert a permission introduction: r : read permission w : writable permission X : executable permission. The so-called 9-bit permission flag is aimed at: the file owner, the file owner group, and other personnel's access permission restrictions

  • shmat

Function: connect, mount, and associate physical memory to the process address space

shmaddr: NULL automatically selects the connection address, not NULL, it is manually assigned to the address you want to mount

We still fill in NULL

Return Val: Returns a ptr pointing to the first address of shared physical memory. The first address of the connection in the virtual space

  • shmdt

 

Function: Unmount, detach the shared memory from the current process.

shmaddr : The return val of the previous shmat, the first address of the shared memory mapped in the virtual address space. 

Return Val:          success return 0     failure return -1

  • shmctl

Function: operate on shared memory IPC

cmd: IPC_RMID delete shared memory

buf can pass NULL

Practical case: use shared memory to realize communication between parent and child processes, the parent process writes data to the shared memory, and the child process prints the written data synchronously when writing.

#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>

#define ERR_EXIT(m) \
    do { perror(m); exit(EXIT_FAILURE); } while (0);
#define SHMSIZE 1024

//联合结构体

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*/
};



int initsem(key_t key) {

	int semid = -1;

	if (-1 == (semid = semget(key, 1, 0666 | IPC_CREAT))) {
		ERR_EXIT("semid");
	}
	//设置信号量集合中编号为0号的信号量val 为 2
	union semun sem_un;
	sem_un.val = 1;

	if (-1 == semctl(semid, 0, SETVAL, sem_un)) {
		ERR_EXIT("semop set val");
	}

	return semid;
}

void destorysem(int semid) {
	if (-1 == semctl(semid, 0, IPC_RMID)) {
		ERR_EXIT("semctl destorysem");
	}
} 


void P(int semid) {
	struct sembuf op;
	op.sem_num = 0;
	op.sem_op = -1;
	op.sem_flg = SEM_UNDO;
	if (-1 == semop(semid, &op, 1)) {
		ERR_EXIT("semop P");
	}
}


void V(int semid) {
	struct sembuf op;
	op.sem_num = 0;
	op.sem_op = 1;
	op.sem_flg = SEM_UNDO;
	if (-1 == semop(semid, &op, 1)) {
		ERR_EXIT("semop V");
	}
} 


void waitforzero(int semid) {
	struct sembuf op;
	op.sem_num = 0;
	op.sem_op = 0;
	op.sem_flg = SEM_UNDO;
	if (-1 == semop(semid, &op, 1)) {
		ERR_EXIT("semop zero");
	}
} 


int main(int argc, char* argv[]) {
    
	if (2 != argc) {
		fprintf(stderr, "usage: %s <filepath>", argv[0]);
		exit(EXIT_FAILURE);
	}

	key_t key1 = ftok(argv[1], 'c');
	key_t key2 = ftok(argv[1], 'C');
	//创建信号量集合IPC
	int semid = initsem(key1);
	int shmid = -1;

	pid_t pid = fork();

	if (pid < 0) {
		ERR_EXIT("fork");
	}

	if (pid == 0) {
		//son process
		waitforzero(semid);

		if (-1 == (shmid = shmget(key2, SHMSIZE, 0666 | IPC_CREAT))) {
			ERR_EXIT("shmget");
		}

		void* p = shmat(shmid, NULL, 0);

        if ((void*)-1 == p)
        {
            ERR_EXIT("shmat");
        }

        char buf[SHMSIZE] = {0};
        memcpy(buf, p, SHMSIZE);
        printf("%s\n", buf);

        //卸载共享内存
        if (-1 == shmdt(p))
        {
            ERR_EXIT("shmdt in parent");
        }

		V(semid);//为父进程归还这个资源.


	} else {
		// fa process

		if (-1 == (shmid = shmget(key2, SHMSIZE, 0666 | IPC_CREAT))) {
			ERR_EXIT("shmget");
		}

		void* p;
		p = shmat(shmid, NULL, 0);

 		if ((void*)-1 == p)
        {
            ERR_EXIT("shmat");
        }


		char buff[SHMSIZE];
		printf("请输入要传输给子进程的数据\n");
		scanf("%s", buff);
		memcpy(p, buff, strlen(buff));

		P(semid);

		waitpid(pid, NULL, 0);
		//通信完成, 销毁所有IPC
		destorysem(semid);
		//卸载内存, 销毁shmid共享内存
		if (-1 == shmdt(p)) {
			ERR_EXIT("shmdt");
		}

		if (-1 == shmctl(shmid, IPC_RMID, NULL)) {
			ERR_EXIT("shmctl destoryshm");
		}

	}

	return 0;
}

Guess you like

Origin blog.csdn.net/weixin_53695360/article/details/124408041