在使用共享存储区通信时会遇到当多人同时运行客户端代码通过共享存储区与服务器代码做应答时,共享存储区内容还没有来得及显示,就被恶意篡改的情况(上篇文章中有源码 )。本文将通过Linux下的信号量机制(pv操作)解决这个问题。
PV操作的基本原理是在计算机操作系统课中学到的,这里不再详述。然而在Linux下PV操作基本编程是在下面的代码中学到的,现贴出来(注释是自己百度后添加的,里面很多参数的设置需要费一番功夫弄明白。):
[cpp]
//注意:在Linux下编程,中文注释有可能编译通不过。提示错误为:编码不对。(以下代码在加注释后便出现此问题,编译时去掉注释) #include <sys/types.h> #include <unistd.h> #include <signal.h>//signal的头文件。 #include <stdio.h> #include <string.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/sem.h> #define MY_SHMKEY 10071500 // need to change #define MY_SEMKEY 10071500 // need to change void sigend(int);//清除操作 int shmid, semid; int main(void) { int *shmptr, semval, local; struct sembuf semopbuf; //sembuf为信号量的数据结构。设置semopbuf中的值,通过semop指定,可改变当前指定信号量数据结构中的值。 if((shmid=shmget(MY_SHMKEY, sizeof(int), IPC_CREAT|IPC_EXCL|0666)) < 0)//IPC_CREAT如果不存在则创建,如果存在则打开。IPC_EXCL只有不存在的时候才创建,如果系统中一存在,则错误。二者同时使用保证此共享内存为新建的。0666为可读可写权限。 { /* shared memory exists, act as client */ shmid=shmget(MY_SHMKEY, sizeof(int), 0666);//获取共享存储区的ID semid=semget(MY_SEMKEY, 2, 0666);//获取信号量集ID shmptr=(int *)shmat(shmid, 0, 0); printf("Act as producer. To end, input 0 when prompted.\n\n"); printf("Input a number:\n"); scanf("%d", &local); while( local ) { semopbuf.sem_num=0;//指定操作信号量集中的第一个信号量 semopbuf.sem_op=-1; semopbuf.sem_flg=SEM_UNDO; semop(semid, &semopbuf, 1); //操作一个信号量。(p(s1)) *shmptr = local; semopbuf.sem_num=1; semopbuf.sem_op=1; semopbuf.sem_flg=SEM_UNDO; semop(semid, &semopbuf, 1); /* V(S2) */ printf("Input a number:\n"); scanf("%d", &local); } } else /* acts as server */ { semid=semget(MY_SEMKEY, 2, IPC_CREAT|0666);//创建信号量集(信号量被创建的情况有两种:1.键值为IPC_PRIVATE 2.指定一个系统中不存在的信号量集key值。同时标志中指定IPC_CREAT),2为信号量个数,即当前信号量集中有两个信号量 shmptr=(int *)shmat(shmid, 0, 0);//共享存储区挂接到shmptr上 semval=1; semctl(semid, 0, SETVAL, semval);//SETVAL:设置信号量中一个单独的信号量的值。 semval=0; semctl(semid, 1, SETVAL,semval); /* set S2=0 */ signal(SIGINT, sigend);//设置某一信号的对应动作.ctrl+c引发SIGINT,Kill命令引发SIGTERM signal(SIGTERM, sigend); printf("ACT CONSUMER!!! To end, try Ctrl+C or use kill.\n\n"); while(1) { semopbuf.sem_num=1;//指定信号量集中的第二个信号量 semopbuf.sem_op=-1;//如果其值为正数,该值会加到现有的信号内含值中。通常用于释放所控资源的使用权;如果sem_op的值为负数,而其绝对值又大于信号的现值,操作将会阻塞,直到信号值大于或等于sem_op的绝对值。通常用于获取资源的使用权;如果sem_op的值为0,则操作将暂时阻塞,直到信号的值变为0。 semopbuf.sem_flg=SEM_UNDO;//SEM_UNDO保证程序异常结束时信号量的值回复到调用semop前的值。 semop(semid, &semopbuf, 1); /* P(S2) *///第三个参数指定本次操作的信号操作数量,必>1。 printf("Shared memory set to %d\n", *shmptr); semopbuf.sem_num=0; semopbuf.sem_op=1; semopbuf.sem_flg=SEM_UNDO; semop(semid, &semopbuf, 1); /* V(S1) */ } } } void sigend(int sig) { shmctl(shmid, IPC_RMID, 0); semctl(semid, IPC_RMID, 0); exit(0); } 下面是我的代码,在上次代码的基础上给Serve和Client做PV操作。