我与Linux的爱恋:了解信号量+共享内存+消息队列的应用
🔥个人主页:guoguoqiang. 🔥专栏:Linux的学习
文章目录
- 信号量
- 共享内存应用---Server&Client通信
- client.cc
- server.cc
- namepipe.hpp
- Shm.hpp
- 消息队列——实现Client&Server
- Com.hpp
- Client.cc
- Server.cc
信号量
-
信号量的定义与概念
- 定义:信号量是一种用于进程间同步和互斥的机制,它本质上是一个非负整数计数器。这个计数器用于控制对共享资源的访问,多个进程可以通过操作这个信号量来协调它们对共享资源的使用。
- 作用原理:信号量的值表示可用资源的数量。当一个进程需要访问共享资源时,它首先要获取信号量(对信号量进行P操作,也称为等待操作)。如果信号量的值大于0,那么进程可以获取信号量,信号量的值减1,表示可用资源减少了一个;如果信号量的值为0,那么进程会被阻塞,直到信号量的值大于0(即有其他进程释放了信号量)。当进程使用完共享资源后,它会释放信号量(对信号量进行V操作,也称为信号操作),信号量的值加1,表示可用资源增加了一个,并且如果有其他进程正在等待这个信号量,那么会唤醒其中一个等待的进程。
1.多个执行流(进程),能看到的同一份资源:共享资源。
2.被保护起来的资源—临界资源—同步和互斥-----用互斥的方式保护共享资源----临界资源
3.互斥:任何时候只能有一个进程在访问共享资源
4.资源要被程序员访问,资源被访问就是通过代码来访问----代码=访问共享资源的代码(临界区)+不访问共享资源的代码(非临界区)
5.对共享资源的保护—临界资源—本质是对访问共享资源的代码进行保护(通过加锁保护)
-
信号量的操作
P 操作(wait/Down/Proberen):将信号量的值减1。当信号量的值大于0时,线程可以继续执行;如果信号量值为0或负值,则线程进入等待队列,直到信号量的值大于0。
V 操作(signal/Up/Verhogen):将信号量的值加1,并唤醒一个正在等待的线程(如果有)。
申请信号量的本质:就是对公共资源的一种预定机制
-
函数原型与功能
- 在Unix/Linux系统中,
semget
函数用于创建一个新的信号量集或者获取一个已经存在的信号量集的标识符。其函数原型如下:
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semget(key_t key, int nsems, int semflg);
- 这个函数主要通过给定的键值(
key
)来定位信号量集,根据nsems
确定信号量集中信号量的数量,同时按照semflg
参数指定的方式来创建或获取信号量集。成功调用后会返回一个信号量集标识符,供后续操作(如semop
和semctl
)使用。
- 在Unix/Linux系统中,
-
参数详细解释
key
参数:- 这是一个用于唯一标识信号量集的键值,类型为
key_t
。通常这个键值是通过ftok
函数生成的。ftok
函数会基于一个文件路径和一个项目标识符(proj_id
)来生成一个相对唯一的键值。例如:
key_t key = ftok(".", 'a');
- 这里以当前目录(用
"."
表示)和字符'a'
作为参数生成键值。如果ftok
返回-1
,则表示生成键值失败,可能是因为指定的文件不存在或者没有权限访问该文件等原因。
- 这是一个用于唯一标识信号量集的键值,类型为
nsems
参数:- 表示要创建或获取的信号量集中信号量的个数。例如,如果
nsems
设置为3
,那么创建或获取的信号量集将包含3个信号量。这个参数在创建新的信号量集时必须大于0;在获取已存在的信号量集时,如果nsems
的值小于信号量集中实际的信号量个数,只会获取前面nsems
个信号量;如果nsems
的值大于实际信号量个数,其行为在不同的系统实现中可能会有所不同,但通常会返回错误。
- 表示要创建或获取的信号量集中信号量的个数。例如,如果
semflg
参数:- 这是一个标志位参数,用于控制信号量集的创建和访问方式。主要的标志位有以下几种:
IPC_CREAT
:如果信号量集不存在,则创建一个新的信号量集;如果信号量集已经存在,则返回已经存在的信号量集的标识符。例如:
int semid = semget(key, 1, IPC_CREAT | 0666);
- 这里使用
IPC_CREAT
标志和权限0666
(表示所有者、所属组和其他用户都有读写权限)来创建或获取信号量集。 IPC_EXCL
:这个标志通常和IPC_CREAT
一起使用。当同时使用IPC_CREAT
和IPC_EXCL
时,如果信号量集已经存在,则semget
函数返回-1
,用于确保创建的是一个全新的信号量集。例如:
int semid = semget(key, 1, IPC_CREAT | IPC_EXCL | 0666);
- 这种方式可以用于在程序中保证只有一个进程能够创建特定的信号量集,避免多个进程意外地创建多个相同的信号量集。
- 权限位(如
0666
):除了IPC_CREAT
和IPC_EXCL
标志外,semflg
还可以包含权限位,其格式和文件权限的格式相同。用于设置信号量集的所有者、所属组和其他用户的读写权限。例如,0666
表示所有者、所属组和其他用户都有读写权限,0444
表示只有读权限等。
- 这是一个标志位参数,用于控制信号量集的创建和访问方式。主要的标志位有以下几种:
-
返回值
- 如果
semget
函数调用成功,它会返回一个非负整数,这个整数就是信号量集的标识符(semid
)。后续的semop
(对信号量进行操作)和semctl
(控制信号量)等操作都需要使用这个标识符来指定信号量集。 - 如果
semget
函数调用失败,它会返回-1
,并且会设置全局变量errno
来指示具体的错误原因。常见的错误原因包括:EACCESS
:权限不足,可能是因为semflg
中指定的权限不符合要求,或者当前进程没有足够的权限来创建或访问信号量集。EEXIST
:当使用IPC_CREAT | IPC_EXCL
标志时,如果信号量集已经存在,就会返回这个错误,表示资源已经存在。ENOENT
:如果key
参数对应的信号量集不存在,并且没有使用IPC_CREAT
标志,就会返回这个错误,表示没有找到对应的资源。ENOMEM
:系统没有足够的内存来创建新的信号量集。ENOSPC
:系统已经达到了信号量集数量的上限,无法再创建新的信号量集。
- 信号量的P操作(semop):
semop
系统调用用于对信号量进行操作,实现P操作(等待操作)。函数原型如下:#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semop(int semid, struct sembuf *sops, unsigned nsops);
semid
是信号量集的标识符,通过semget
获取。sops
是一个指向struct sembuf
结构体数组的指针,这个结构体定义了要对信号量进行的操作。nsops
表示sops
数组中元素的个数,即要进行的操作的个数。struct sembuf
结构体通常包含三个成员:sem_num
(表示信号量集中的第几个信号量)、sem_op
(操作的值,对于P操作通常为 - 1)和sem_flg
(一些操作标志)。
- 信号量的V操作(semop):
-
- 函数原型与功能
- 在Unix/Linux系统中,
semop
函数用于对信号量进行操作。其函数原型如下:
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semop(int semid, struct sembuf *sops, unsigned nsops);
- 这个函数的主要功能是对由
semid
标识的信号量集执行一系列操作,这些操作由sops
指向的struct sembuf
结构体数组定义,nsops
表示数组中元素的个数,即要执行的操作的数量。
- 如果
-
参数详细解释
semid
参数:- 这是信号量集的标识符,通过
semget
函数创建或获取信号量集时返回。它用于唯一地标识要进行操作的信号量集。例如,如果之前通过semget
获取到一个信号量集的标识符为semid
,那么在semop
函数中就将semid
设置为这个值,以对相应的信号量集进行操作。
- 这是信号量集的标识符,通过
sops
参数:- 这是一个指向
struct sembuf
结构体数组的指针。struct sembuf
结构体用于定义对信号量的具体操作,其定义如下:struct sembuf { unsigned short sem_num; short sem_op; short sem_flg; };
sem_num
参数:表示信号量集中的第几个信号量。信号量集可以包含多个信号量,这个参数用于指定要操作的是其中的哪一个。例如,如果信号量集包含3个信号量,sem_num
的值可以是0、1或2,分别对应这3个信号量。sem_op
参数:这是操作的值,用于执行信号量的P操作(等待操作)和V操作(信号操作)等。如果sem_op
的值小于0,表示执行P操作,即请求获取信号量资源。其绝对值表示请求获取的资源数量,通常在实现互斥或同步时设置为 - 1。如果sem_op
的值大于0,表示执行V操作,即释放信号量资源,其值表示释放的资源数量。如果sem_op
的值等于0,表示等待信号量的值变为0。sem_flg
参数:这是一些操作标志,用于控制操作的行为。主要的标志位有以下几种:IPC_NOWAIT
:如果设置了这个标志位,当执行P操作(sem_op < 0
)且信号量的值不满足操作要求(例如,信号量的值小于sem_op
的绝对值)时,semop
函数不会阻塞等待,而是立即返回,并将返回值设置为 - 1,同时设置errno
为EAGAIN
,表示资源暂时不可用。如果没有设置这个标志位,在这种情况下,semop
函数会阻塞等待,直到信号量的值满足操作要求。SEM_UNDO
:这个标志用于在进程异常终止时自动撤销对信号量的操作。当进程执行了带有SEM_UNDO
标志的操作后,如果进程异常终止,系统会自动执行相应的反向操作,以恢复信号量之前的状态。这在防止因进程意外终止而导致信号量状态混乱方面非常有用。
- 这是一个指向
nsops
参数:- 这个参数表示
sops
指向的struct sembuf
结构体数组中元素的个数,即要执行的操作的数量。例如,如果要对两个信号量分别进行操作,可以定义一个包含两个struct sembuf
结构体的数组,并将nsops
设置为2。
- 这个参数表示
-
返回值
- 如果
semop
函数调用成功,它会返回0,表示操作顺利完成。 - 如果调用失败,它会返回 - 1,并且会设置全局变量
errno
来指示具体的错误原因。常见的错误原因包括:E2BIG
:当nsops
的值大于系统限制(即一次允许执行的最大操作数量)时,会返回这个错误。EACCESS
:当前进程没有足够的权限来对信号量集进行操作。EAGAIN
:当设置了IPC_NOWAIT
标志位,且执行P操作(sem_op < 0
)时信号量的值不满足操作要求,会返回这个错误。EFAULT
:sops
指向的地址无效,可能是因为指针未正确初始化或者指向了非法的内存区域。EIDRM
:信号量集的标识符semid
所对应的信号量集已经被删除。EINVAL
:参数semid
无效,或者sops
数组中的某个struct sembuf
结构体的成员设置不合理(如sem_num
超出信号量集的范围、sem_op
的值不符合要求等)等情况会返回这个错误。ENOMEM
:系统没有足够的内存来完成信号量操作。- 同样是通过
semop
系统调用实现V操作,只是在struct sembuf
结构体中的sem_op
成员设置为1,表示释放信号量。例如,当一个进程完成对共享资源的使用后,会执行V操作来增加信号量的值,使其他等待的进程有机会获取信号量。
- 控制信号量(semctl):
semctl
系统调用用于对信号量集进行控制操作,如初始化信号量的值、获取信号量集的状态信息、删除信号量集等。函数原型如下:#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semctl(int semid, int semnum, int cmd,...);
semid
是信号量集的标识符,semnum
表示信号量集中的第几个信号量,cmd
是要执行的控制命令。根据不同的cmd
命令,可能还需要其他参数,这些参数通过可变参数列表传递。例如,当cmd
为SETVAL
时,用于设置信号量的值,需要一个额外的参数来指定信号量的初始值。-
- 函数原型与功能
- 在Unix/Linux系统中,
semctl
函数用于对信号量集进行控制操作。其函数原型如下:
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semctl(int semid, int semnum, int cmd,...);
- 这个函数可以对信号量集进行多种控制操作,如获取信号量集的状态信息、设置信号量的值、删除信号量集等。具体的操作由
cmd
参数指定,根据不同的cmd
命令,可能还需要其他参数(通过可变参数列表传递)。
- 如果
-
参数详细解释
semid
参数:- 这是信号量集的标识符,是通过
semget
函数创建或获取信号量集时返回的。它用于唯一地标识要进行控制操作的信号量集。例如,如果之前通过semget
获取到一个信号量集的标识符为semid
,那么在semctl
函数中就将semid
设置为这个值来对相应的信号量集进行操作。
- 这是信号量集的标识符,是通过
semnum
参数:- 表示信号量集中的第几个信号量。信号量集可以包含多个信号量,这个参数用于指定要对其中的哪一个信号量进行操作。例如,如果信号量集包含3个信号量,
semnum
的值可以是0、1或2,分别对应这3个信号量。不过,有些cmd
命令会忽略这个参数,因为它们是对整个信号量集进行操作的。
- 表示信号量集中的第几个信号量。信号量集可以包含多个信号量,这个参数用于指定要对其中的哪一个信号量进行操作。例如,如果信号量集包含3个信号量,
cmd
参数:- 这个参数指定了要对信号量集执行的控制命令,它有多种取值,以下是一些常见的命令:
GETVAL
:用于获取指定信号量(由semnum
指定)的值。当cmd
为GETVAL
时,不需要额外的参数(可变参数列表为空)。返回值是指定信号量的值。例如:
int val = semctl(semid, 0, GETVAL);
- 这里获取信号量集中第0个信号量的值,并将其存储在
val
变量中。 SETVAL
:用于设置指定信号量(由semnum
指定)的值。当cmd
为SETVAL
时,需要一个额外的参数,这个参数是一个整数,表示要设置的信号量的值。例如:
int value = 1; if (semctl(semid, 0, SETVAL, value) == -1) { perror("semctl - set value"); }
- 这里将信号量集中第0个信号量的值设置为1。
IPC_RMID
:用于删除信号量集。当cmd
为IPC_RMID
时,不需要额外的参数(可变参数列表为空)。这个命令会将标识符为semid
的信号量集标记为删除。信号量集被标记为删除后,不能再对其进行操作,但已经存在的信号量操作(如正在等待或执行semop
操作的进程)可能会继续进行,直到系统完成清理工作。例如:
if (semctl(semid, 0, IPC_RMID) == -1) { perror("semctl - delete semaphore set"); }
GETALL
:用于获取信号量集中所有信号量的值。当cmd
为GETALL
时,需要一个额外的参数,这个参数是一个指向整数数组的指针,用于存储获取到的所有信号量的值。例如:
int values[nsems]; if (semctl(semid, 0, GETALL, values) == -1) { perror("semctl - get all values"); }
- 这里
nsems
是信号量集中信号量的个数,values
数组用于存储所有信号量的值。 SETALL
:用于设置信号量集中所有信号量的值。当cmd
为SETALL
时,需要一个额外的参数,这个参数是一个指向整数数组的指针,数组中的元素用于设置各个信号量的值。例如:
int new_values[nsems]; // 初始化new_values数组 if (semctl(semid, 0, SETALL, new_values) == -1) { perror("semctl - set all values"); }
- 这里
nsems
是信号量集中信号量的个数,new_values
数组中的元素用于设置各个信号量的值。 - 除了上述命令外,还有其他一些用于获取和设置信号量集状态信息、权限信息等的命令,如
GETPID
(获取最后一个操作信号量的进程ID)、GETNCNT
(获取等待信号量值大于当前值的进程数量)等。
- 这个参数指定了要对信号量集执行的控制命令,它有多种取值,以下是一些常见的命令:
-
返回值
- 根据
cmd
命令的不同,返回值也有所不同。 - 对于一些命令(如
GETVAL
、GETPID
等获取信息的命令),返回值是命令执行的结果,如获取到的信号量的值、最后一个操作信号量的进程ID等。 - 对于一些设置操作的命令(如
SETVAL
、SETALL
等),如果操作成功,返回0;如果调用失败,返回 - 1,并且会设置全局变量errno
来指示具体的错误原因。常见的错误原因包括:EACCESS
:当前进程没有足够的权限来执行指定的cmd
操作。EFAULT
:当需要传递指针参数(如GETALL
、SETALL
操作时的数组指针)时,指针指向的地址无效,可能是因为指针未正确初始化或者指向了非法的内存区域。EIDRM
:信号量集的标识符semid
所对应的信号量集已经被删除。EINVAL
:参数semid
无效,或者semnum
的值超出信号量集的范围,或者cmd
参数的取值不合法(例如,指定了一个不被支持的操作命令)等情况会返回这个错误。
设置信号量
- 根据
semctl(semid, 0, SETVAL, 1); // 将第0个信号量的值设置为1
获取信号量值
int val = semctl(semid, 0, GETVAL); // 获取第0个信号量的值
printf("Semaphore value: %d\n", val);
删除信号量
semctl(semid, 0, IPC_RMID); // 删除信号量集
3.信号量的类型
1.计数信号量(Counting Semaphore):
计数信号量有一个整型计数器,表示可用资源的数量。当计数器值为正时,表示还有资源可以被分配;为0时,表示所有资源都已经被占用;为负时,表示有等待的线程或进程。
适用于限制多个进程/线程访问一组相同的资源,例如限制最多有N个线程可以访问某个数据库连接池。
2.二元信号量(Binary Semaphore):
又称为互斥信号量(Mutex),其值只有0和1,主要用于实现互斥锁(Mutex)。值为1时表示资源空闲,0表示资源已被占用。
适用于只有一个线程可以访问某一资源的场景(类似于锁机制)。
- 信号量的应用场景
- 进程间互斥访问共享资源:
- 假设多个进程需要访问一个共享文件进行读写操作。为了避免数据混乱,需要保证在同一时刻只有一个进程能够对文件进行写操作。可以使用一个信号量来实现互斥,信号量初始值设置为1。当一个进程要对文件进行写操作时,它先执行P操作获取信号量,如果成功获取(信号量值变为0),则可以进行写操作;其他进程在这个时候执行P操作就会被阻塞。当写操作完成后,进程执行V操作释放信号量,使得其他被阻塞的进程有机会获取信号量进行写操作。
- 进程间同步操作:
- 考虑一个生产者 - 消费者模型,生产者进程生产数据并将其放入缓冲区,消费者进程从缓冲区中获取数据进行消费。可以使用两个信号量来实现同步,一个信号量
empty
表示缓冲区的空闲空间数量,初始值为缓冲区的大小;另一个信号量full
表示缓冲区中已有数据的数量,初始值为0。生产者在生产数据时,先对empty
信号量执行P操作,获取空闲空间,如果成功获取,则将数据放入缓冲区,然后对full
信号量执行V操作,表示缓冲区中有了新的数据。消费者在获取数据时,先对full
信号量执行P操作,获取已有数据,如果成功获取,则从缓冲区中取出数据,然后对empty
信号量执行V操作,表示缓冲区有了新的空闲空间。
- 考虑一个生产者 - 消费者模型,生产者进程生产数据并将其放入缓冲区,消费者进程从缓冲区中获取数据进行消费。可以使用两个信号量来实现同步,一个信号量
- 进程间互斥访问共享资源:
System V 信号量的相关操作说明:
•semget: 创建或获取一个信号量集(可以包含多个信号量)。
•semop: 对信号量执行 P 或 V 操作。
•semctl: 控制信号量,可以用于设置信号量的值、获取信号量值,或者删除信号量。
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
int main(){
//1.创建/获取信号量
key_t key=ftok("somefile",65); //获取唯一key
int semid=semget(key,1,0666|IPC_CREAT); //创建一个信号量集,包含一个信号量
//2.初始化信号量值
semctl(semid,0,SETVAL,1);//将信号量设为1
//3.p操作(减-1)
struct sembuf sb={0,-1,0}; //操作第0个信号量,执行减1操作
semop(semid,&sb,1); //执行信号量操作
printf("Entering critical section ...\n");
//4.执行临界区代码
sleep(2);
//5.V操作(加1)
sb.sem_op=1;//将信号量加1
semop(semid,&sb,1);//退出临界区
printf("Leaving critical section...\n");
//6.删除信号量
semctl(semid,0,IPC_RMID);//删除信号量
return 0;
}
共享内存应用—Server&Client通信
client.cc
#include "Shm.hpp"
#include "namedPipe.hpp"
int main()
{
// 1. 创建共享内存
Shm shm(gpathname, gproj_id, gUser);
shm.Zero();
char *shmaddr = (char *)shm.Addr();
sleep(3);
// 2. 打开管道
NamePiped fifo(comm_path, User);
fifo.OpenForWrite();
// 当成string
char ch = 'A';
while (ch <= 'Z')
{
shmaddr[ch - 'A'] = ch;
std::string temp = "wakeup";
std::cout << "add " << ch << " into Shm, " << "wakeup reader" << std::endl;
fifo.WriteNamedPipe(temp);
sleep(2);
ch++;
}
return 0;
}
server.cc
#include "Shm.hpp"
#include "namedPipe.hpp"
int main()
{
// 1. 创建共享内存
Shm shm(gpathname, gproj_id, gCreater);
char *shmaddr = (char*)shm.Addr();
shm.DebugShm();
// // 2. 创建管道
// NamePiped fifo(comm_path, Creater);
// fifo.OpenForRead();
// while(true)
// {
// // std::string temp;
// // fifo.ReadNamedPipe(&temp);
// std::cout << "shm memory content: " << shmaddr << std::endl;
// }
sleep(5);
return 0;
}
namepipe.hpp
#pragma once
#include <iostream>
#include <cstdio>
#include <cerrno>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
const std::string comm_path = "./myfifo";
#define DefaultFd -1
#define Creater 1
#define User 2
#define Read O_RDONLY
#define Write O_WRONLY
#define BaseSize 4096
class NamePiped
{
private:
bool OpenNamedPipe(int mode)
{
_fd = open(_fifo_path.c_str(), mode);
if (_fd < 0)
return false;
return true;
}
public:
NamePiped(const std::string &path, int who)
: _fifo_path(path), _id(who), _fd(DefaultFd)
{
if (_id == Creater)
{
int res = mkfifo(_fifo_path.c_str(), 0666);
if (res != 0)
{
perror("mkfifo");
}
std::cout << "creater create named pipe" << std::endl;
}
}
bool OpenForRead()
{
return OpenNamedPipe(Read);
}
bool OpenForWrite()
{
return OpenNamedPipe(Write);
}
// const &: const std::string &XXX
// * : std::string *
// & : std::string &
int ReadNamedPipe(std::string *out)
{
char buffer[BaseSize];
int n = read(_fd, buffer, sizeof(buffer));
if(n > 0)
{
buffer[n] = 0;
*out = buffer;
}
return n;
}
int WriteNamedPipe(const std::string &in)
{
return write(_fd, in.c_str(), in.size());
}
~NamePiped()
{
if (_id == Creater)
{
int res = unlink(_fifo_path.c_str());
if (res != 0)
{
perror("unlink");
}
std::cout << "creater free named pipe" << std::endl;
}
if(_fd != DefaultFd) close(_fd);
}
private:
const std::string _fifo_path;
int _id;
int _fd;
};
Shm.hpp
#ifndef __SHM_HPP__
#define __SHM_HPP__
#include <iostream>
#include <string>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
const int gCreater = 1;
const int gUser = 2;
const std::string gpathname = "/home/whb/code/111/code/lesson22/4.shm";
const int gproj_id = 0x66;
const int gShmSize = 4097; // 4096*n
class Shm
{
private:
key_t GetCommKey()
{
key_t k = ftok(_pathname.c_str(), _proj_id);
if (k < 0)
{
perror("ftok");
}
return k;
}
int GetShmHelper(key_t key, int size, int flag)
{
int shmid = shmget(key, size, flag);
if (shmid < 0)
{
perror("shmget");
}
return shmid;
}
std::string RoleToString(int who)
{
if (who == gCreater)
return "Creater";
else if (who == gUser)
return "gUser";
else
return "None";
}
void *AttachShm()
{
if (_addrshm != nullptr)
DetachShm(_addrshm);
void *shmaddr = shmat(_shmid, nullptr, 0);
if (shmaddr == nullptr)
{
perror("shmat");
}
std::cout << "who: " << RoleToString(_who) << " attach shm..." << std::endl;
return shmaddr;
}
void DetachShm(void *shmaddr)
{
if (shmaddr == nullptr)
return;
shmdt(shmaddr);
std::cout << "who: " << RoleToString(_who) << " detach shm..." << std::endl;
}
public:
Shm(const std::string &pathname, int proj_id, int who)
: _pathname(pathname), _proj_id(proj_id), _who(who), _addrshm(nullptr)
{
_key = GetCommKey();
if (_who == gCreater)
GetShmUseCreate();
else if (_who == gUser)
GetShmForUse();
_addrshm = AttachShm();
std::cout << "shmid: " << _shmid << std::endl;
std::cout << "_key: " << ToHex(_key) << std::endl;
}
~Shm()
{
if (_who == gCreater)
{
int res = shmctl(_shmid, IPC_RMID, nullptr);
}
std::cout << "shm remove done..." << std::endl;
}
std::string ToHex(key_t key)
{
char buffer[128];
snprintf(buffer, sizeof(buffer), "0x%x", key);
return buffer;
}
bool GetShmUseCreate()
{
if (_who == gCreater)
{
_shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | IPC_EXCL | 0666);
if (_shmid >= 0)
return true;
std::cout << "shm create done..." << std::endl;
}
return false;
}
bool GetShmForUse()
{
if (_who == gUser)
{
_shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | 0666);
if (_shmid >= 0)
return true;
std::cout << "shm get done..." << std::endl;
}
return false;
}
void Zero()
{
if(_addrshm)
{
memset(_addrshm, 0, gShmSize);
}
}
void *Addr()
{
return _addrshm;
}
void DebugShm()
{
struct shmid_ds ds;
int n = shmctl(_shmid, IPC_STAT, &ds);
if(n < 0) return;
std::cout << "ds.shm_perm.__key : " << ToHex(ds.shm_perm.__key) << std::endl;
std::cout << "ds.shm_nattch: " << ds.shm_nattch << std::endl;
}
private:
key_t _key;
int _shmid;
std::string _pathname;
int _proj_id;
int _who;
void *_addrshm;
};
#endif
消息队列——实现Client&Server
由于struct msgbuf中的mtype可以标识向那个进程发送消息。假设Server接受mytype为1的数据
Com.hpp
#pragma once
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
const std::string PATH = "/home/xiaoming";
const int PROJ_ID = 999;
const int BUFF_SIZE = 1024;
enum
{
MSG_CREAT_ERR = 1,
MSG_GET_ERR,
MSG_DELETE_ERR
};
int CreateMsg()
{
key_t key = ftok(PATH.c_str(), PROJ_ID);
int msgid = msgget(key, IPC_CREAT | IPC_EXCL | 0666);
if (msgid < 0)
{
perror("msg create error");
exit(MSG_CREAT_ERR);
}
return msgid;
}
int GetMsg()
{
key_t key = ftok(PATH.c_str(), PROJ_ID);
int msgid = msgget(key, IPC_CREAT | 0666);
if (msgid < 0)
{
perror("msg get error");
exit(MSG_GET_ERR);
}
return msgid;
}
Client.cc
#include "Com.hpp"
int main(){
int msgid=GetMsg();
struct msgbuf buffer;
buffer.mtype=1;
while(true){
std::cout<<"Says #";
std::string s;
std::getline(std::cin,s);
strcpy(buffer.mtext,s.c_str());
msgsnd(msgid,&buffer,BUFF_SIZE,0);
}
return 0;
}
Server.cc
#include "Com.hpp"
int main()
{
struct msgbuf buffer;
int msgid = CreateMsg();
while (true)
{
msgrcv(msgid, &buffer, BUFF_SIZE, 1, 0);
std::cout << "Client say@ " << buffer.mtext << std::endl;
}
return 0;
}