要求:使用信号灯集和共享内存实现:一个进程对共享内存存放数据"Nice to meet you"循环倒置,一个进程循环输出共享内存的内容,要确保倒置一次打印一次。
分析:这两个进程可以写成两个源文件,一个文件对应一个进程,从题目要求可以看出,这两个进程实现的是一种进程间的同步关系,即想要进程打印信息必须要另一进程完成数组内容的倒置,然后该进程倒置完成一次之后不能就马上再倒置,要打印进程完成对上次倒置内容的打印才可以。用PV操作来展示他们之间的关系如下:
//reverse.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <errno.h>
#include <sys/shm.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
union semun {
int val;
};
void sem_init(int semid, int num, int val) //信号灯集初始化函数
{
union semun sem;
sem.val = val;
semctl(semid, num, SETVAL, sem);
}
void sem_op(int semid, int num, int op) //pv操作函数
{
struct sembuf buf;
buf.sem_num = num;
buf.sem_op = op;
buf.sem_flg = 0;
semop(semid, &buf, 1);
}
char reverse(char *head, char *tail)
{
while (head < tail)
{
*head ^= *tail;
*tail ^= *head;
*head++ ^= *tail--;
}
}
int main(int argc, char const *argv[])
{
char s[32] = {0};
//创建key值
key_t key = ftok(".", 'a');
if (key < 0)
{
perror("ftok err");
return -1;
}
//shmget
int shmid = shmget(key, 32, IPC_CREAT | IPC_EXCL | 0666);
if (shmid <= 0)
{
if (errno = EEXIST)
shmid = shmget(key, 32, 0666);
else
{
perror("shmget err");
return -1;
}
}
//shmat
char *p = (char *)shmat(shmid, NULL, 0);
if (p == (char *)-1)
{
perror("shmat err");
return -1;
}
//semget
int semid = semget(key, 2, IPC_CREAT | IPC_EXCL | 0666);
if (semid <= 0)
{
if (errno = EEXIST)
semid = semget(key, 1, 0666);
else
{
perror("semget err");
return -1;
}
}
else
{
sem_init(semid, 0, 1); //初始化信号灯集0
sem_init(semid, 1, 0); //初始化信号灯集1
}
fputs("请输入字符串:", stdout);
fgets(p, 32, stdin);
if (p[strlen(p) - 1] == '\n')
p[strlen(p) - 1] = '\0';
char *head = NULL;
char *tail = NULL;
while (1)
{
head = p;
tail = p + strlen(p) - 1;
sem_op(semid, 0, -1);
reverse(p, p + strlen(p) - 1); //倒置
printf("倒置...%p\n",p);
sem_op(semid, 1, 1);
}
shmdt(p);
shmctl(shmid, IPC_RMID, NULL);
//删除信号灯集
semctl(semid, 0, IPC_RMID);
return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <errno.h>
#include <sys/shm.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
union semun {
int val;
};
void sem_init(int semid, int num, int val) //信号灯集初始化函数
{
union semun sem;
sem.val = val;
semctl(semid, num, SETVAL, sem);
}
void sem_op(int semid, int num, int op) //pv操作函数
{
struct sembuf buf;
buf.sem_num = num;
buf.sem_op = op;
buf.sem_flg = 0;
semop(semid, &buf, 1);
}
int main(int argc, char const *argv[])
{
// char s[32] = {0};
//创建key值
key_t key = ftok(".", 'a');
if (key < 0)
{
perror("ftok err");
return -1;
}
//shmget
int shmid = shmget(key, 32, IPC_CREAT | IPC_EXCL | 0666);
if (shmid <= 0)
{
if (errno = EEXIST)
shmid = shmget(key, 32, 0666);
else
{
perror("shmget err");
return -1;
}
}
//shmat
char *p = (char *)shmat(shmid, NULL, 0);
if (p == (char *)-1)
{
perror("shmat err");
return -1;
}
//semget
int semid = semget(key, 2, IPC_CREAT | IPC_EXCL | 0666);
if (semid <= 0)
{
if (errno = EEXIST)
semid = semget(key, 1, 0666);
else
{
perror("semget err");
return -1;
}
}
else
{
sem_init(semid, 0, 1); //初始化信号灯集0
sem_init(semid, 1, 0); //初始化信号灯集1
}
while (1)
{
sem_op(semid, 1, -1);
printf("%s\n",p); //dayin
sleep(1);
sem_op(semid, 0, 1);
}
shmdt(p);
shmctl(shmid, IPC_RMID, NULL);
//删除信号灯集
semctl(semid, 0, IPC_RMID);
return 0;
}
- 上面程序中对信号灯的初始化和PV操作进行了函数的封装,使得代码更具模块化,不显得太乱,同时功能实现起来更为方便。
- 两个数据交换使用了 ^= 的方式,这可以不设置第三个变量,数据交换完全是在原地址空间实现的