https://blog.csdn.net/weixin_49638344/article/details/119652712?spm=1001.2014.3001.5502共享内存和信号量
共享内存🌔🌔🌔🌔🌔 使用共享内存通信的一般步骤1、创建或者打开共享内存 2、进程A连接(映射)共享内存,写入数据 3、进程A断开 4、进程B连接(映射)共享内存,读取数据 5、进程B断开 6、释放共享内存 示意图:
创建共享内存共享内存的映射和解除映射shmat函数: #include <sys/types.h>#include <sys/shm.h>void *shmat(int shmid, const void *shmaddr, int shmflg); shmid:共享内存标识符,shmget() 的返回值。 shmaddr:共享内存映射地址(若为 NULL 则由系统自动指定),推荐使用 NULL。 shmflg:共享内存段的访问权限和映射条件( 通常为 0 ),具体取值如下:
0:共享内存具有可读可写权限。 SHM_RDONLY:只读。 SHM_RND:(shmaddr 非空时才有效)
shmdt函数: #include <sys/types.h>#include <sys/shm.h>int shmdt(const void *shmaddr); 功能: 将共享内存和当前进程分离 参数: 返回值: 成功0 ;失败-1
共享内存操作函数示例代码本例程未使用信号量,写端停留5秒等待读端读取信息,配合信号量使用的代码见文末 写进程shm_w.c #include <sys/ipc.h>#include <sys/shm.h>#include <stdio.h>#include <string.h>#include <stdlib.h>int main(){int shmid; //共享内存标识符char *shmaddr;key_t key;key = ftok(".",1); //获取键值shmid = shmget(key,1024*4,IPC_CREAT|0666); //打开或者创建共享内存if(shmid == -1){printf("shmget NO OK\n");exit(-1);}
shmaddr = shmat(shmid,0,0); //共享内存连接到当前进程的地址空间printf("shmat ok\n");strcpy(shmaddr,"hello world"); //向内存中写入数据sleep(5); //睡眠5秒,等待内存数据被读走shmdt(shmaddr); //断开进程和内存的连接shmctl(shmid,IPC_RMID,0); //删除共享内存段printf("quit\n");return 0;} 读进程shm_r.c #include <sys/ipc.h>#include <sys/shm.h>#include <stdio.h>#include <string.h>#include <stdlib.h>int main(){int shmid;char *shmaddr;key_t key;key = ftok(".",1); //获取键值shmid = shmget(key,1024*4,0); //打开创建的共享内存,获取内存ID,if(shmid == -1){printf("shmget no ok\n");exit(-1);}shmaddr = shmat(shmid,0,0); //共享内存连接到当前进程的地址空间printf("shmat ok\n"); //表示连接成功printf("data : %s",shmaddr); //将内存地址中的数据读出,打印shmdt(shmaddr); //断开内存和当前进程的连接printf("quit\n");return 0;}
信号量🌔🌔🌔🌔🌔 多任务编程中互斥和同步的概念互斥:是指散步在不同任务之间的若干程序片断,当某个任务运行其中一个程序片段时,其它任务就不能运行它们之中的任一程序片段,只能等到该任务运行完这个程序片段后才可以运行。最基本的场景就是: 一个公共资源同一时刻只能被一个进程或线程使用,多个进程或线程不能同时使用公共资源。同步:是指散步在不同任务之间的若干程序片断,它们的运行必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。最基本的场景就是:两个或两个以上的进程或线程在运行过程中协同步调,按预定的先后次序运行。比如 A 任务的运行依赖于 B 任务产生的数据。
信号量广泛用于进程或线程间的同步和互斥,信号量本质上是一个非负的整数计数器,它被用来控制对公共资源(比如上文说的共享内存)的访问。 信号量可以用来保证两个或多个关键代码段不被并发调用。 在实际应用中两个进程间通信可能会使用多个信号量,因此 System V 的信号量以集合的概念来管理。 信号量集合数据结构:struct semid_ds,此数据结构中定义了整个信号量集的基本属性。 /* Obsolete, used only for backwards compatibility and libc5 compiles */struct semid_ds{struct ipc_perm sem_perm;/* permissions .. see ipc.h */
__kernel_time_t sem_otime;/* last semop time */
__kernel_time_t sem_ctime;/* last change time */struct sem*sem_base;/* ptr to first semaphore in array */struct sem_queue *sem_pending;/* pending operations to be processed */struct sem_queue **sem_pending_last;/* last pending operation */struct sem_undo*undo;/* undo requests on this array */unsigned short sem_nsems;/* no. of semaphores in array */}; 信号量数据结构:struct sem,此数据结构中定义了信号量的基本属性。 /* One semaphore structure for each semaphore in the system. */struct sem{int semval;/* current value *信号量的值*/int sempid;/* pid of last operation *最后一个操作信号量的进程号*/struct list_head sem_pending; /* pending single-sop operations */};
信号量的操作相关函数创建信号量数组 #include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>int semget(key_t key, int nsems, int semflg); key:进程间通信键值,通过调用 ftok() 函数得到的键值 nsems:创建的信号量的个数。如果只是访问而不创建则可以指定该参数为 0,一旦创建了该信号量, 就不能更改其信号量个数,只要不删除该信号量,重新调用该函数创建该键值的信号量,该函数只是返回以前创建的值,不会重新创建。 semflg:标识函数的行为及信号量的权限,其取值如下:
semop函数对信号量组进行操作 #include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>int semop(int semid, struct sembuf *sops, size_t nsops);struct sembuf{unsigned short sem_num;/*信号量的序号*/short sem_op;/*信号量的操作值*/short sem_flg;/*信号量的操作标识*/}; sem_op > 0:信号量的值在原来的基础上加上此值。 sem_op < 0:如果信号量的值小于 semop 的绝对值,则挂起操作进程。 sem_op = 0:对信号量的值进行是否为 0 测试。若为 0 则函数立即返回,若不为 0 则阻塞调用进程 如果信号量的值大于等于 semop 的绝对值, 则信号量的值在原来的基础上减去 semop 的绝对值。
结构体成员使用说明如下: sem_num:信号量集中信号量的序号 sem_op 取值如下: sem_flag 取值如下:
功能: 操作信号量,主要进行信号量加减操作。 参数: 返回值: 成功0 ;失败-1
semctl函数控制信号量的相关信息 #include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>int semctl(int semid, int semnum, int cmd, ...);union semun{int val;/*信号量的值*/struct semid_ds *buf;/*信号量集合信息*/unsigned short *array;/*信号量值的数组*/struct seminfo *__buf;/*信号量限制信息*/}; GETVAL:获取信号量的值。此时函数有3个参数。semctl() 函数的返回值即为信号量的值 SETVAL:设置信号量的值。此时函数有4个参数。第4个参数为联合体中的val,其值为信号量的值。 IPC_STAT:获取信号量集合的信息。此时函数有4个参数。第4个参数为联合体中的__buf IPC_SET:设置信号量集合的信息。此时函数有4个参数。第4个参数为联合体中的__buf IPC_RMID:删除信号量集。此时函数有3个参数,第2个参数semnum不起作用 GETALL:获取所有信号量的值。此时函数有4个参数,第2个参数semnum不起作用。第4个参数为联合体中的array,其值为用来存放所有信号量值的数组的首地址。 SETALL:设置所有信号量的值 ,参数说明同上 IPC_INFO:获取信号量集合的限制信息。此时函数有4个参数,第2个参数semnum不起作用。 GETPID:获取信号的进程号,即最后操作信号量的进程。此时函数有3个参数。 semctl() 函数的返回值即为信号的进程号。 GETNCNT:获取等待信号的值递增的进程数。此时函数有3个参数。semctl() 函数的返回值即为进程数。 GETZCNT:获取等待信号的值递减的进程数。此时函数有3个参数。semctl() 函数的返回值即为进程数。
功能: 对信号量集合以及集合中的信号量进行操作。 参数: 返回值: 成功0 ;失败-1
函数使用示例下例将sem设为0,父进程使用P操作会等待子进程V操作释放 #include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>#include <stdio.h>#include <unistd.h>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) */};void pGetKey(int semid){struct sembuf set;
set.sem_num = 0;
set.sem_op = -1;
set.sem_flg = SEM_UNDO;semop(semid, &set,1);printf("get key\n");}void vPutKey(int semid){struct sembuf set;
set.sem_num = 0;set.sem_op = 1;set.sem_flg = SEM_UNDO;semop(semid, &set,1);printf("put key\n");
}int main(){key_t key = ftok(".",2);int semid = semget(key,1,IPC_CREAT|0666);union semun seminit;
seminit.val = 0;semctl(semid,0,SETVAL,seminit);int pid = fork();if(pid > 0){pGetKey(semid);printf("this is father\n");vPutKey(semid);semctl(semid,0,IPC_RMID);}else if(pid == 0){printf("this is child\n");sleep(5);vPutKey(semid);}else{printf("fork error!\n");}return 0;}
信号量配合共享内存使用参考信号量的同步模型,建立两个信号量,一个为0,另一个为1 写进程shm_w.c: #include <sys/ipc.h>#include <sys/shm.h>#include <sys/sem.h>#include <stdio.h>#include <string.h>#include <stdlib.h>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) */};void P(int semid,unsigned short num){struct sembuf set;
set.sem_num = num; //信号量在数组里的序号
set.sem_op = -1; //信号量的操作值
set.sem_flg = SEM_UNDO; //信号量的操作标识semop(semid, &set,1);printf("get key\n");}void V(int semid,unsigned short num){struct sembuf set;
set.sem_num = num;set.sem_op = 1;set.sem_flg = SEM_UNDO;semop(semid, &set,1);printf("put key\n");
}
int main(){key_t key1,key2;key1 = ftok(".",1); //获取键值key2 = ftok(".",2);int shmid = shmget(key1,1024*4,IPC_CREAT|0666); //打开或者创建共享内存int semid = semget(key2,2,IPC_CREAT|0666);//打开或者创建信号量组
union semun seminit; //信号量初始化
seminit.val = 1; //第一个信号量设置为1semctl(semid,0,SETVAL,seminit);seminit.val = 0;//第二个信号量设置为0semctl(semid,1,SETVAL,seminit);P(semid,0); //给第一个信号量上锁char *shmaddr = shmat(shmid,0,0); //共享内存连接到当前进程的地址空间printf("shmat ok\n");strcpy(shmaddr,"hello world"); //向内存中写入数据shmdt(shmaddr); //断开进程和内存的连接V(semid,1); //释放第二个信号量P(semid,0); //等待第一个信号量的释放shmctl(shmid,IPC_RMID,0); //删除共享内存段semctl(semid,0,IPC_RMID); //删除信号量组
printf("quit\n");return 0;} 读进程shm_r.c: #include <sys/ipc.h>#include <sys/shm.h>#include <sys/sem.h>#include <stdio.h>#include <string.h>#include <stdlib.h>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) */};void P(int semid,unsigned short num){struct sembuf set;
set.sem_num = num; //信号量在数组里的序号
set.sem_op = -1; //信号量的操作值
set.sem_flg = SEM_UNDO; //信号量的操作标识semop(semid, &set,1);printf("get key\n");}void V(int semid,unsigned short num){struct sembuf set;
set.sem_num = num;set.sem_op = 1;set.sem_flg = SEM_UNDO;semop(semid, &set,1);printf("put key\n");
}
int main(){key_t key1,key2;key1 = ftok(".",1); //获取键值key2 = ftok(".",2);int shmid = shmget(key1,1024*4,IPC_CREAT|0666); //打开或者创建共享内存int semid = semget(key2,1,IPC_CREAT|0666);//打开或者创建信号量组
P(semid,1); //等待第二个信号量释放char *shmaddr = shmat(shmid,0,0); //共享内存连接到当前进程的地址空间printf("shmat ok\n"); //表示连接成功printf("data : %s\n",shmaddr); //将内存地址中的数据读出,打印shmdt(shmaddr); //断开内存和当前进程的连接V(semid,0); //释放第一个信号量printf("quit\n");return 0;} 因为信号量的初始化在写端,所以先执行写端 终端显示: 写端: 读端:
|