# 分类
# 共享内存
1 2 3 4 int shmget (key_t key, size_t size, int shmflg) ;void *shmat (int shmid, const void *shmaddr, int shmflg) ;int shmdt (const void *shmaddr) ;int shmctl (int shmid, int cmd, struct shmid_ds *buf) ;
shmget() returns the identifier of the System V shared memory segment associated with the value of the argument key. It may be used either to obtain the identifier of a previously created shared memory segment (when shmflg is zero and key does not have the value IPC_PRIVATE), or to create a new set.
shmflg 和文件的控制权限一样。
注意到如果 key 是 IPC_PRIVATE
,那就相当于匿名 shm,只能用于有亲属关系的进程通信。
shmat() attaches the System V shared memory segment identified by shmid to the address space of the calling process. The attaching address is specified by shmaddr with one of the following criteria:
shmaddr 一般为空 让操作系统决定
shmdt() detaches the shared memory segment located at the address specified by shmaddr from the address space of the calling process.
shmctl() performs the control operation specified by cmd on the System V shared memory segment whose identifier is given in shmid.
删除共享内存用 PC_RMID
# 实操 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <stdio.h> #include <sys/ipc.h> #include <sys/shm.h> #include <unistd.h> #include <stdlib.h> #include <assert.h> #define PATHNAME "." #define PROJ_ID 0x6666 #define PAGE_SIZE 0x1000 int main () { int shmid; key_t key = ftok(PATHNAME, PROJ_ID); assert(key != -1 ); shmid = shmget(key, PAGE_SIZE, 0640 |IPC_CREAT); assert(shmid != -1 ); char *shmptr = shmat(shmid, NULL , 0 ); printf ( "shmptr is %p\n" "procid is %d\n" ,shmptr, getpid() ); memset (shmptr, "A" , 0x10 ); getchar(); shmdt(shmptr); int res = shmctl(shmid, IPC_RMID, 0 ); assert(res != -1 ); return 0 ; }
利用一个可以访问的目录创建 shmid(proj 号和当前目录可以返回一个唯一的 shmid)
在当前路径下创建一次共享内存 不删除的话就一直存在。
用命令
1 2 ipcs -m # 查看所有共享内存 ipcrm -m <shmid> # 删除一个共享内存
# 实操 2
server.c 用来创建共享内存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #include <stdio.h> #include <sys/ipc.h> #include <sys/shm.h> #include <unistd.h> #include <stdlib.h> #include <assert.h> #define PATHNAME "." #define PROJ_ID 0x6666 #define PAGE_SIZE 0x1000 int main () { key_t key = ftok(PATHNAME, PROJ_ID); assert(key != -1 ); int shmid; if ((shmid = shmget(key, PAGE_SIZE, IPC_CREAT | IPC_EXCL | 0666 )) == -1 ){ perror("shmget" ); exit (-1 ); } char *shmptr = shmat(shmid, NULL , 0 ); sleep(2 ); int i = 0 ; while (i++ < 26 ) { printf ("client# %s\n" ,shmptr); sleep(1 ); } shmdt(shmptr); sleep(2 ); assert(shmctl(shmid,IPC_RMID,NULL ) != -1 ); return 0 ; }
client.c 用来连上共享内存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <stdio.h> #include <sys/ipc.h> #include <sys/shm.h> #include <unistd.h> #include <stdlib.h> #include <assert.h> #define PATHNAME "." #define PROJ_ID 0x6666 #define PAGE_SIZE 0x1000 int main () { key_t key = ftok(PATHNAME,PROJ_ID); assert(key != -1 ); int shmid = 0 ; if ((shmid = shmget(key, PAGE_SIZE, 0 )) == -1 ){ perror("shmget" ); exit (-1 ); } char *shmptr = shmat(shmid,NULL ,0 ); sleep(2 ); int i = 0 ; while (i < 26 ) { shmptr[i] = 'A' + i; i++; shmptr[i] = 0 ; sleep(1 ); } shmdt(shmptr); sleep(2 ); return 0 ; }
# 信号量
1 2 3 int semget (key_t key, int nsems, int semflg) ;int semop (int semid, struct sembuf *sops, size_t nsops) ;int semctl (int semid, int semnum, int cmd, ...) ;
The semget() system call returns the System V semaphore set identifier associated with the argument key.
创建方法两种 一种用 key 里的 IPC_PRIVATE
,一种用 semflg 里的 IPC_CREAT
nsems 是信号量个数。
semop 就是等待锁和释放锁
在 semctl 中 需要用到 semun
1 2 3 4 5 6 7 union semun { int val; struct semid_ds *buf ; unsigned short *array ; struct seminfo *__buf ; };
semop 中 需要用到
1 2 3 4 5 struct sembuf { unsigned short sem_num; short sem_op; short sem_flg; }
sem_op 参数 :
sem_op > 0 信号加上 sem_op 的值,表示进程释放控制的资源;
sem_op = 0 如果没有设置 IPC_NOWAIT,则调用进程进入睡眠状态,直到信号量的值为 0;否则进程不回睡眠,直接返回 EAGAIN
sem_op < 0 信号加上 sem_op 的值。若没有设置 IPC_NOWAIT ,则调用进程阻塞,直到资源可用;否则进程直接返回 EAGAIN
sem_flg 参数 :
该参数可设置为 IPC_NOWAIT 或 SEM_UNDO 两种状态。只有将 sem_flg 指定为 SEM_UNDO 标志后,semadj (所指定信号量针对调用进程的调整值)才会更新。 此外,如果此操作指定 SEM_UNDO,系统更新过程中会撤消此信号灯的计数(semadj)。此操作可以随时进行 — 它永远不会强制等待的过程。调用进程必须有改变信号量集的权限。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 #include <stdio.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/sem.h> #include <unistd.h> #include <stdlib.h> #include <assert.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/ipc.h> #include <sys/sem.h> class CSEM { private: union semun // 用于信号灯操作的共同体。 { int val; struct semid_ds *buf ; unsigned short *arry; }; int sem_id; public: bool init (key_t key) ; bool wait () ; bool post () ; bool destroy () ; }; int main (int argc, char *argv[]) { CSEM sem; if (sem.init(0x5000 )==false ) { printf ("sem.init failed.\n" ); return -1 ; } printf ("sem.init ok\n" ); if (sem.wait() == false ) { printf ("sem.wait failed.\n" ); return -1 ; } printf ("sem.wait ok\n" ); sleep(10 ); if (sem.post() == false ) { printf ("sem.post failed.\n" ); return -1 ; } printf ("sem.post ok\n" ); } bool CSEM::init (key_t key) { if ( (sem_id=semget(key,1 ,0640 )) == -1 ) { if (errno == ENOENT) { if ( (sem_id=semget(key,1 ,0640 |IPC_CREAT)) == -1 ) { perror("init 1 semget()" ); return false ; } union semun sem_union ; sem_union.val = 1 ; if (semctl(sem_id,0 ,SETVAL,sem_union) < 0 ) { perror("init semctl()" ); return false ; } } else { perror("init 2 semget()" ); return false ; } } return true ; } bool CSEM::destroy () { if (semctl(sem_id,0 ,IPC_RMID) == -1 ) { perror("destroy semctl()" ); return false ; } return true ; } bool CSEM::wait () { struct sembuf sem_b ; sem_b.sem_num = 0 ; sem_b.sem_op = -1 ; sem_b.sem_flg = SEM_UNDO; if (semop(sem_id, &sem_b, 1 ) == -1 ) { perror("wait semop()" ); return false ; } return true ; } bool CSEM::post () { struct sembuf sem_b ; sem_b.sem_num = 0 ; sem_b.sem_op = 1 ; sem_b.sem_flg = SEM_UNDO; if (semop(sem_id, &sem_b, 1 ) == -1 ) { perror("post semop()" ); return false ; } return true ; }
这个代码最重要的一点是,在 semop 为 - 1 这里
等待一个其他进程执行 (semop(sem_id, &sem_b, 1)
,其中 sem_b.sem_op = 1
, 也就是释放一个资源.
而最初的资源数量是 SETVAL
所创建的
1 2 3 4 5 6 7 8 9 10 bool CSEM::wait () { struct sembuf sem_b ; sem_b.sem_num = 0 ; sem_b.sem_op = -1 ; sem_b.sem_flg = SEM_UNDO; if (semop(sem_id, &sem_b, 1 ) == -1 ) { perror("wait semop()" ); return false ; } return true ; }
# 信号量对共享内存加锁
就是在共享内存操作之中使用上文提到的 wait 和 post,这其实就是个 P-V 操作,搞这么神神叨叨的。
然后这 PV 之间才对共享内存操作.
部分代码再上文。
client.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 char *shmptr = 0 ;int shm_register () { int shmid = shmget ( (key_t )0x5005 , 1024 , 0640 |IPC_CREAT); if ( shmid == -1 ) { printf ("shmget() failed\n" ); return -1 ; } shmptr = (char *)shmat (shmid, 0 ,0 ); return shmid; } int main (int argc, char *argv[]) { CSEM sem; if (sem.init (0x5000 )==false ) { printf ("sem.init failed.\n" ); return -1 ; } printf ("sem.init ok\n" ); if (sem.wait () == false ) { printf ("sem.wait failed.\n" ); return -1 ; } printf ("sem.wait ok\n" ); printf ("Proc1 write 0x10 A to shm" ); int shmid = shm_register (); memset (shmptr, 'A' , 0x10 ); getchar (); if (sem.post () == false ) { printf ("sem.post failed.\n" ); return -1 ; } printf ("sem.post ok\n" ); getchar (); printf ("Proc1 shm contents: %s\n" , shmptr); shmdt (shmptr); }
server.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 char *shmptr = 0 ;int shm_attach () { int shmid = shmget ( (key_t )0x5005 , 1024 , 0 ); if ( shmid == -1 ) { printf ("shmget() failed\n" ); return -1 ; } shmptr = (char *)shmat (shmid, 0 ,0 ); return shmid; } int main (int argc, char *argv[]) { CSEM sem; if (sem.init (0x5000 )==false ) { printf ("sem.init failed.\n" ); return -1 ; } printf ("sem.init ok\n" ); if (sem.wait () == false ) { printf ("sem.wait failed.\n" ); return -1 ; } printf ("sem.wait ok\n" ); int shmid = shm_attach (); printf ("Proc2 contents is %s\n" , shmptr); memset (shmptr, 'B' , 0x10 ); getchar (); if (sem.post () == false ) { printf ("sem.post failed.\n" ); return -1 ; } printf ("sem.post ok\n" ); shmdt (shmptr); }
client 先初始化信号量,然后获取资源,申请到共享内存,写入’A’,释放资源
server 连上这个信号量,连上这个 shm,打印出 client 写入的’A’,获取资源,写入’B’
client 释放资源之后再看内存,已经变成’B’的形状了