实验报告
实验内容
实验内容:Peterson 算法。
把 Lecture08 示例 alg.8-1~8-3 拓展到多个读线程和多个写线程,应用 Peterson 算法原理设计实现共享内存互斥。
实验环境
Ubuntu 20.04.2.0(64位)
实验过程
编译运行:
可以看到主线程创建了5个写进程和5个读进程:
5个写线程和5个读线程并发执行:
其中写线程4和读线程4进入临界区对共享内存进行读写操作,其他线程在等待区:
进行写读操作:
退出当前的写线程和读线程:
可以看到,写线程4和读线程2退出了临界区:
之后写线程2和读线程3分别进入临界区对共享内存进行读写操作:
进行写读操作:
退出当前读写线程:
写线程2和读线程3退出了临界区,写线程3和读线程0进入了临界区:
继续进行写读操作:
退出当前读写线程,写线程3和读线程0退出了临界区,写线程1和读线程1离开等待区进入临界区:
进行读写操作:
退出当前读写线程,写线程1和读线程1退出了临界区,写线程0和读线程2离开等待区进入临界区:
进行读写操作:
退出当前读写线程,写线程0和读线程2退出了临界区,所有线程执行完毕,程序结束:
从代码的运行情况来看,本次实验成功实现了共享内存互斥的多读写线程。
源代码
- alg.8-0-shmdata.h
#define TEXT_SIZE 4 * 1024 /* = PAGE_SIZE, size of each message */
#define TEXT_NUM 1 /* maximal number of mesages */
/* total size can not exceed current shmmax,
or an 'invalid argument' error occurs when shmget */
/* a demo structure, modified as needed */
struct shared_struct
{
int written; /* flag = 0: buffer writable; others: readable */
char mtext[TEXT_SIZE]; /* buffer for message reading and writing */
};
#define PERM S_IRUSR | S_IWUSR | IPC_CREAT
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while (0)
- alg.8-1-shmcon.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/shm.h>
#include <fcntl.h>
#include "alg.8-0-shmdata.h"
int main(int argc, char *argv[])
{
struct stat fileattr;
key_t key; /* of type int */
int shmid; /* shared memory ID */
void *shmptr;
struct shared_struct *shared; /* structured shm */
pid_t childpid1, childpid2;
char pathname[80], key_str[10], cmd_str[80];
int shmsize, ret;
shmsize = TEXT_NUM * sizeof(struct shared_struct);
printf("max record number = %d, shm size = %d\n", TEXT_NUM, shmsize);
if (argc < 2)
{
printf("Usage: ./a.out pathname\n");
return EXIT_FAILURE;
}
strcpy(pathname, argv[1]);
if (stat(pathname, &fileattr) == -1)
{
ret = creat(pathname, O_RDWR);
if (ret == -1)
{
ERR_EXIT("creat()");
}
printf("shared file object created\n");
}
key = ftok(pathname, 0x27); /* 0x27 a project ID 0x0001 - 0xffff, 8 least bits used */
if (key == -1)
{
ERR_EXIT("shmcon: ftok()");
}
printf("key generated: IPC key = %x\n", key); /* can set any nonzero key without ftok()*/
shmid = shmget((key_t)key, shmsize, 0666 | PERM);
if (shmid == -1)
{
ERR_EXIT("shmcon: shmget()");
}
printf("shmcon: shmid = %d\n", shmid);
shmptr = shmat(shmid, 0, 0); /* returns the virtual base address mapping to the shared memory, *shmaddr=0 decided by kernel */
if (shmptr == (void *)-1)
{
ERR_EXIT("shmcon: shmat()");
}
printf("shmcon: shared Memory attached at %p\n", shmptr);
shared = (struct shared_struct *)shmptr;
shared->written = 0;
sprintf(cmd_str, "ipcs -m | grep '%d'\n", shmid);
printf("\n------ Shared Memory Segments ------\n");
system(cmd_str);
if (shmdt(shmptr) == -1)
{
ERR_EXIT("shmcon: shmdt()");
}
printf("\n------ Shared Memory Segments ------\n");
system(cmd_str);
sprintf(key_str, "%x", key);
char *argv1[] = {" ", key_str, 0};
childpid1 = vfork();
if (childpid1 < 0)
{
ERR_EXIT("shmcon: 1st vfork()");
}
else if (childpid1 == 0)
{
execv("./alg.8-2-shmread.o", argv1); /* call shm_read with IPC key */
}
else
{
childpid2 = vfork();
if (childpid2 < 0)
{
ERR_EXIT("shmcon: 2nd vfork()");
}
else if (childpid2 == 0)
{
execv("./alg.8-3-shmwrite.o", argv1); /* call shmwrite with IPC key */
}
else
{
wait(&childpid1);
wait(&childpid2);
/* shmid can be removed by any process knewn the IPC key */
if (shmctl(shmid, IPC_RMID, 0) == -1)
{
ERR_EXIT("shmcon: shmctl(IPC_RMID)");
}
else
{
printf("shmcon: shmid = %d removed \n", shmid);
printf("\n------ Shared Memory Segments ------\n");
system(cmd_str);
printf("nothing found ...\n");
}
}
}
exit(EXIT_SUCCESS);
}
- alg.8-2-shmread.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/shm.h>
#include <pthread.h>
#include <signal.h>
#include "alg.8-0-shmdata.h"
typedef struct
{
key_t key;
pthread_t thread_num;
} argu;
static int counter = 0;
/* number of process(s) in the critical section */
int max_num = 5; /* default max thread number */
int level[5];
/* level number of processes 0 .. max_num-1 */
int waiting[4];
/* waiting process of each level number 0 .. max_num-2 */
static void *ftn(void *arg)
{
void *shmptr = NULL;
struct shared_struct *shared;
int shmid;
argu *argument = (argu *)arg;
key_t key = (*argument).key;
int thread_num = (*argument).thread_num;
int lev, k, j;
printf("%*sreading thread-%3d, ptid = %lu working\n", 30, " ", thread_num, pthread_self());
for (lev = 0; lev < max_num - 1; ++lev)
{ /* there are at least max_num-1 waiting rooms */
level[thread_num] = lev;
waiting[lev] = thread_num;
while (waiting[lev] == thread_num)
{ /* busy waiting */
/* && (there exists k != thread_num, such that level[k] >= lev)) */
for (k = 0; k < max_num; k++)
{
if (level[k] >= lev && k != thread_num)
{
break;
}
if (waiting[lev] != thread_num)
{ /* check again */
break;
}
} /* if any other proces j with level[j] < lev upgrades its level to or greater than lev during this period, then process thread_num must be kicked out the waiting room and waiting[lev] != thread_num, and then exits the while loop when scheduled */
if (k == max_num)
{ /* all other processes have level of less than process thread_num */
break;
}
}
}
/* critical section of process thread_num */
printf("%*sreading thread-%3d, ptid = %lu entering the critical section\n", 30, " ", thread_num, pthread_self());
counter++;
if (counter > 1)
{
printf("ERROR! more than one processes in their critical sections\n");
kill(getpid(), SIGKILL);
}
shmid = shmget((key_t)key, TEXT_NUM * sizeof(struct shared_struct), 0666 | PERM);
if (shmid == -1)
{
ERR_EXIT("shread: shmget()");
}
shmptr = shmat(shmid, 0, 0);
if (shmptr == (void *)-1)
{
ERR_EXIT("shread: shmat()");
}
printf("%*sshmread: shmid = %d\n", 30, " ", shmid);
printf("%*sshmread: shared memory attached at %p\n", 30, " ", shmptr);
printf("%*sshmread thread-%3d ready ...\n", 30, " ", thread_num);
shared = (struct shared_struct *)shmptr;
while (1)
{
while (shared->written == 0)
{
sleep(1); /* message not ready, waiting ... */
}
printf("%*sYou wrote: %s\n", 30, " ", shared->mtext);
shared->written = 0;
if (strncmp(shared->mtext, "end", 3) == 0)
{
break;
}
} /* it is not reliable to use shared->written for process synchronization */
if (shmdt(shmptr) == -1)
{
ERR_EXIT("shmread: shmdt()");
}
printf("%*sreading thread-%3d, ptid = %lu exit the critical section\n", 30, " ", thread_num, pthread_self());
counter--;
/* end of critical section */
level[thread_num] = -1;
/* allow other process of level max_num-2 to exit the while loop
and enter his critical section */
pthread_exit(0);
}
int main(int argc, char *argv[])
{
key_t key;
sscanf(argv[1], "%x", &key);
printf("%*sshmread: IPC key = %x\n", 30, " ", key);
memset(level, (-1), sizeof(level));
memset(waiting, (-1), sizeof(waiting));
int i, ret;
pthread_t ptid[max_num];
argu argument[max_num];
for (i = 0; i < max_num; i++)
{
argument[i].key = key;
argument[i].thread_num = i;
}
printf("%*sreading thread number = %d\n", 30, " ", max_num);
printf("%*sreading main(): pid = %d, ptid = %lu.\n", 30, " ", getpid(), pthread_self());
for (i = 0; i < max_num; i++)
{
ret = pthread_create(&ptid[i], NULL, &ftn, (void *)&argument[i]);
if (ret != 0)
{
fprintf(stderr, "pthread_create error: %s\n", strerror(ret));
}
}
for (i = 0; i < max_num; i++)
{
ret = pthread_join(ptid[i], NULL);
if (ret != 0)
{
perror("pthread_join()");
}
}
return 0;
}
- alg.8-3-shmwrite.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/shm.h>
#include <pthread.h>
#include <signal.h>
#include "alg.8-0-shmdata.h"
typedef struct
{
key_t key;
pthread_t thread_num;
} argu;
// #define MAX_N 1024
static int counter = 0;
/* number of process(s) in the critical section */
int max_num = 5; /* default max thread number */
int level[5];
/* level number of processes 0 .. max_num-1 */
int waiting[4];
/* waiting process of each level number 0 .. max_num-2 */
static void *ftn(void *arg)
{
void *shmptr = NULL;
struct shared_struct *shared;
int shmid;
char buffer[BUFSIZ + 1]; /* 8192bytes, saved from stdin */
argu *argument = (argu *)arg;
key_t key = (*argument).key;
int thread_num = (*argument).thread_num;
int lev, k, j;
printf("writing thread-%3d, ptid = %lu working\n", thread_num, pthread_self());
for (lev = 0; lev < max_num - 1; ++lev)
{ /* there are at least max_num-1 waiting rooms */
level[thread_num] = lev;
waiting[lev] = thread_num;
while (waiting[lev] == thread_num)
{ /* busy waiting */
/* && (there exists k != thread_num, such that level[k] >= lev)) */
for (k = 0; k < max_num; k++)
{
if (level[k] >= lev && k != thread_num)
{
break;
}
if (waiting[lev] != thread_num)
{ /* check again */
break;
}
} /* if any other proces j with level[j] < lev upgrades its level to or greater than lev during this period, then process thread_num must be kicked out the waiting room and waiting[lev] != thread_num, and then exits the while loop when scheduled */
if (k == max_num)
{ /* all other processes have level of less than process thread_num */
break;
}
}
}
/* critical section of process thread_num */
printf("writing thread-%3d, ptid = %lu entering the critical section\n", thread_num, pthread_self());
counter++;
if (counter > 1)
{
printf("ERROR! more than one processes in their critical sections\n");
kill(getpid(), SIGKILL);
}
shmid = shmget((key_t)key, TEXT_NUM * sizeof(struct shared_struct), 0666 | PERM);
if (shmid == -1)
{
ERR_EXIT("shmwite: shmget()");
}
shmptr = shmat(shmid, 0, 0);
if (shmptr == (void *)-1)
{
ERR_EXIT("shmwrite: shmat()");
}
printf("shmwrite: shmid = %d\n", shmid);
printf("shmwrite: shared memory attached at %p\n", shmptr);
printf("shmwrite thread-%3d ready ...\n", thread_num);
shared = (struct shared_struct *)shmptr;
while (1)
{
while (shared->written == 1)
{
sleep(1); /* message not read yet, waiting ... */
}
printf("Enter some text: ");
fgets(buffer, BUFSIZ, stdin);
strncpy(shared->mtext, buffer, TEXT_SIZE);
printf("shared buffer: %s\n", shared->mtext);
shared->written = 1; /* message prepared */
if (strncmp(buffer, "end", 3) == 0)
{
break;
}
}
/* detach the shared memory */
if (shmdt(shmptr) == -1)
{
ERR_EXIT("shmwrite: shmdt()");
}
printf("writing thread-%3d, ptid = %lu exit the critical section\n", thread_num, pthread_self());
counter--;
/* end of critical section */
level[thread_num] = -1;
/* allow other process of level max_num-2 to exit the while loop
and enter his critical section */
pthread_exit(0);
}
int main(int argc, char *argv[])
{
key_t key;
sscanf(argv[1], "%x", &key);
printf("%*sshmread: IPC key = %x\n", 30, " ", key);
memset(level, (-1), sizeof(level));
memset(waiting, (-1), sizeof(waiting));
int i, ret;
pthread_t ptid[max_num];
argu argument[max_num];
for (i = 0; i < max_num; i++)
{
argument[i].key = key;
argument[i].thread_num = i;
}
printf("writing thread number = %d\n", max_num);
printf("writing main(): pid = %d, ptid = %lu.\n", getpid(), pthread_self());
for (i = 0; i < max_num; i++)
{
ret = pthread_create(&ptid[i], NULL, &ftn, (void *)&argument[i]);
if (ret != 0)
{
fprintf(stderr, "pthread_create error: %s\n", strerror(ret));
}
}
for (i = 0; i < max_num; i++)
{
ret = pthread_join(ptid[i], NULL);
if (ret != 0)
{
perror("pthread_join()");
}
}
return 0;
}