实验三 同步问题
16281141
1. 实验目的
系统调用的进一步理解。
进程上下文切换。
同步的方法。
2 实验题目
1)通过fork的方式,产生4个进程P1,P2,P3,P4,每个进程打印输出自己的名字,例如P1输出“I am the process P1”。要求P1最先执行,P2、P3互斥执行,P4最后执行。通过多次测试验证实现是否正确。
在主函数中,使用5个信号量控制整个进程,其中2个信号量在1执行完之后被同时设置为1从而实现P1在P2和P3之前,1个信号量被用作P2和P3的互斥,另外2个信号量分别在P2和P3执行完之后被设置为1,从而实现P4在所有进程之后。实验结果与源代码如下。可以看出原来不同步的顺序为2143,经过同步的进程为1234或1324
实现代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/sem.h>
union semun
{
int val;
struct semid_ds *buf;
unsigned short *arry;
};
void set_value_of_sem(int sem_id, int sem_num, int val) {
union semun sem_union;
sem_union.val = val;
if (semctl(sem_id, sem_num, SETVAL, sem_union) == -1)
{
printf("%d %s\n", sem_id, “error - set value of semaphore”);
exit(1);
}
}
void set_values_of_sem(int sem_id, unsigned short *arry) {
union semun sem_union;
sem_union.arry = arry;
if (semctl(sem_id, sizeof(arry) - 1, SETALL, sem_union) == -1)
{
printf("%d %s\n", sem_id, “error - set values of semaphore”);
exit(1);
}
}
void delete_sem(int sem_id) {
union semun sem_union;
if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
{
printf("%s\n", “error - delete semaphore”);
exit(1);
}
}
void signal(int sem_id, int sem_num) {
struct sembuf sem_buff;
sem_buff.sem_num = sem_num;
sem_buff.sem_op = 1;
sem_buff.sem_flg = 0;
if (semop(sem_id, &sem_buff, 1) == -1)
{
printf("%d %s\n", sem_id, "error - signal");
exit(1);
}
}
void wait(int sem_id, int sem_num) {
struct sembuf sem_buff;
sem_buff.sem_num = sem_num;
sem_buff.sem_op = -1;
sem_buff.sem_flg = 0;
if (semop(sem_id, &sem_buff, 1) == -1)
{
printf("%d %s\n", sem_id, "error - wait");
exit(1);
}
}
int main(int argc, char const *argv[])
{
pid_t pid1, pid2, pid3;
int sem_id1, sem_id2, sem_id3;
sem_id1 = semget(1000, 2, 0666 | IPC_CREAT);
sem_id2 = semget(1001, 1, 0666 | IPC_CREAT);
sem_id3 = semget(1002, 2, 0666 | IPC_CREAT);
// printf("%d %d %d\n", sem_id1, sem_id2, sem_id3);
unsigned short init_arry[2] = {0, 0};
set_values_of_sem(sem_id1, init_arry);
set_value_of_sem(sem_id2, 0, 1);
set_values_of_sem(sem_id3, init_arry);
while ((pid1 = fork()) == -1);
if (pid1 > 0)
{
while ((pid2 = fork()) == -1);
if (pid2 > 0)
{
wait(sem_id1, 0);
wait(sem_id2, 0);
//printf("p2 pid: %d ppid: %d\n", getpid(), getppid() );
printf("I am the process P4\n");
signal(sem_id2, 0);
signal(sem_id3, 0);
exit(0);
}
else
{
wait(sem_id3, 0);
wait(sem_id3, 1);
printf("p4 pid: %d ppid: %d\n", getpid(), getppid() );
printf("I am the process P1\n");
delete_sem(sem_id1);
delete_sem(sem_id2);
delete_sem(sem_id3);
exit(0);
}
}
if (pid1 == 0)
{
while ((pid3 = fork()) == -1);
if (pid3 > 0)
{
//printf("p1 pid: %d ppid: %d\n", getpid(), getppid() );
printf("I am the process P3\n");
unsigned short arry[2] = {1, 1};
set_values_of_sem(sem_id1, arry);
exit(0);
}
else
{
wait(sem_id1, 1);
wait(sem_id2, 0);
//printf("p3 pid: %d ppid: %d\n", getpid(), getppid() );
printf("I am the process P2\n");
signal(sem_id2, 0);
signal(sem_id3, 1);
exit(0);
}
}
return 0;
}
set_value_of_sem对单信号量赋值,set_values_of_sem可以同时对多个信号量赋值,signal和wait函数与信号量同步函数含义一致。
** 2)火车票余票数ticketCount 初始值为1000,有一个售票线程,一个退票线程,各循环执行多次。添加同步机制,使得结果始终正确。要求多次测试添加同步机制前后的实验效果。
售票、退票均运行100次**售票线程的关键代码:
temp=ticketCount;
pthread_yield();
temp=temp-1;
pthread_yield();
ticketCount=temp;
退票线程的关键代码:
temp=ticketCount;
pthread_yield();
temp=temp+1;
pthread_yield();
ticketCount=temp;
)
全部代码:
#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <sched.h>
sem_t* mySem = NULL;
int tickets = 66666666;
voidsold(){
int i = 100;
while(i–){
// sem_wait(mySem);
printf(“Current tickes number is %d\n”,tickets);
int temp = tickets;
// sched_yield();
temp = temp - 1;
// sched_yield();
tickets = temp;
// sem_post(mySem);
}
}
voidreturnT(){
int i = 100;
while(i–){
// sem_wait(mySem);
printf(“Current tickes number is %d\n”,tickets);
int temp = tickets;
// sched_yield();
temp = temp + 1;
// sched_yield();
tickets = temp;
// sem_post(mySem);
}
}
int main(){
pthread_t p1,p2;
mySem = sem_open(“Ticket”, O_CREAT, 0666, 1);
pthread_create(&p1,NULL,sold,NULL);
pthread_create(&p2,NULL,returnT,NULL);
pthread_join(p1,NULL);
pthread_join(p2,NULL);
sem_close(mySem);
sem_unlink(“Ticket”);
printf(“The finall value is %d.\n”,tickets);
return 0;
}
3)一个生产者一个消费者线程同步。设置一个线程共享的缓冲区, char buf[10]。一个线程不断从键盘输入字符到buf,一个线程不断的把buf的内容输出到显示器。要求输出的和输入的字符和顺序完全一致。(在输出线程中,每次输出睡眠一秒钟,然后以不同的速度输入测试输出是否正确)。要求多次测试添加同步机制前后的实验效果。
算法实现与经典的生产者消费者模型一致,实验结果与源代码如下,test.txt与test_out.txt的内容完全一致
完整代码:”
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <time.h>
#define N 10
union semun
{
int val;
struct semid_ds *buf;
unsigned short *arry;
};
void set_init_value_of_sem(int sem_id, int init_value) {
union semun sem_union;
sem_union.val = init_value;
if (semctl(sem_id, 0, SETVAL, sem_union) == -1)
{
printf("%s\n", “error - inital semaphore”);
exit(1);
}
}
void delete_sem(int sem_id) {
union semun sem_union;
if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
{
printf("%s\n", “error - delete semaphore”);
exit(1);
}
}
void signal(int sem_id) {
struct sembuf sem_buff;
sem_buff.sem_num = 0;
sem_buff.sem_op = 1;
sem_buff.sem_flg = SEM_UNDO;
if (semop(sem_id, &sem_buff, 1) == -1)
{
printf("%s\n", "error - signal");
exit(1);
}
}
void wait(int sem_id) {
struct sembuf sem_buff;
sem_buff.sem_num = 0;
sem_buff.sem_op = -1;
sem_buff.sem_flg = SEM_UNDO;
if (semop(sem_id, &sem_buff, 1) == -1)
{
printf("%s\n", "error - wait");
exit(1);
}
}
int share_memory_id;
void *share_memory;
struct share_data
{
int in, out;
char buffer[N];
FILE *fp;
FILE *fp_out;
};
struct share_data *shared;
int empty, full, mutex;
int producer() {
wait(empty);
wait(mutex);
// printf("%p\n", shared->fp);
char ch = fgetc(shared->fp);
printf("%c", ch);
if (ch == EOF) {
signal(mutex);
// signal(full);
return 0;
}
shared->buffer[shared->in] = ch;
shared->in = (shared->in + 1) % N;
signal(mutex);
signal(full);
return 1;
}
int comsumer() {
wait(full);
wait(mutex);
printf(“out: %d “, shared->out);
char out_char = shared->buffer[shared->out];
if (out_char == EOF)
{
signal(mutex);
// signal(empty);
return 0;
}
shared->out = (shared->out + 1) % N;
fprintf(shared->fp_out, “%c”, out_char);
fflush(shared->fp_out);
printf(”%c”, out_char);
signal(mutex);
signal(empty);
return 1;
}
int main(int argc, char const *argv[])
{
share_memory_id = shmget(12345, sizeof(struct share_data), 0666|IPC_CREAT);
share_memory = shmat(share_memory_id, 0, 0);
shared = (struct share_data *)share_memory;
shared->fp = fopen("test.txt", "r");
shared->fp_out = fopen("test_out.txt", "w");
if (shared->fp == NULL)
{
printf("%s\n", "file open fail");
return 0;
}
// printf("%p\n", shared->fp);
shared->in = 0; shared->out = 0;
empty = semget(3000, 1, 0666 | IPC_CREAT);
full = semget(3001, 1, 0666 | IPC_CREAT);
mutex = semget(3002, 1, 0666 | IPC_CREAT);
set_init_value_of_sem(empty, N);
set_init_value_of_sem(full, 0);
set_init_value_of_sem(mutex, 1);
pid_t pid1, pid2;
while ((pid1 = fork()) == -1);
if (pid1 > 0)
{
while ((pid2 = fork()) == -1);
if (pid2 > 0)
{
while (producer()) {
// usleep(100000);
}
}
else {
while (producer()) {
// usleep(100000);
}
}
}
else
{
while (comsumer())
;
printf("\n");
}
fclose(shared->fp_out);
fclose(shared->fp);
return 0;
}
4)(2) 进程通信问题。阅读并运行共享内存、管道、消息队列三种机制的代码实验测试a)通过实验测试,验证共享内存的代码中,receiver能否正确读出sender发送的字符串?如果把其中互斥的代码删除,观察实验结果有何不同?如果在发送和接收进程中打印输出共享内存地址,他们是否相同,为什么?b)有名管道和无名管道通信系统调用是否已经实现了同步机制?通过实验验证,发送者和接收者如何同步的。比如,在什么情况下,发送者会阻塞,什么情况下,接收者会阻塞?c)消息通信系统调用是否已经实现了同步机制?通过实验验证,发送者和接收者如何同步的。比如,在什么情况下,发送者会阻塞,什么情况下,接收者会阻塞?
a)sender的代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <string.h>
int main(int argc, char *argv[])
{
key_t key;
int shm_id;
int sem_id;
int value = 0;
//1.Product the key
key = ftok(".", 0xFF);
//2. Creat semaphore for visit the shared memory
sem_id = semget(key, 1, IPC_CREAT|0644);
if(-1 == sem_id)
{
perror(“semget”);
exit(EXIT_FAILURE);
}
//3. init the semaphore, sem=0
if(-1 == (semctl(sem_id, 0, SETVAL, value)))
{
perror("semctl");
exit(EXIT_FAILURE);
}
//4. Creat the shared memory(1K bytes)
shm_id = shmget(key, 1024, IPC_CREAT|0644);
if(-1 == shm_id)
{
perror("shmget");
exit(EXIT_FAILURE);
}
//5. attach the shm_id to this process
char *shm_ptr;
shm_ptr = shmat(shm_id, NULL, 0);
if(NULL == shm_ptr)
{
perror("shmat");
exit(EXIT_FAILURE);
}
struct sembuf sem_b;
sem_b.sem_num = 0; //first sem(index=0)
sem_b.sem_flg = SEM_UNDO;
sem_b.sem_op = 1; //Increase 1,make sem=1
while(1)
{
if(0 == (value = semctl(sem_id, 0, GETVAL)))
{
printf("\nNow, snd message process running:\n");
printf("\tInput the snd message: “);
scanf(”%s", shm_ptr);
if(-1 == semop(sem_id, &sem_b, 1))
{
perror("semop");
exit(EXIT_FAILURE);
}
}
//if enter "end", then end the process
if(0 == (strcmp(shm_ptr ,"end")))
{
printf("\nExit sender process now!\n");
break;
}
}
shmdt(shm_ptr);
return 0;
}
receiver的代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <string.h>
int main(int argc, char *argv[])
{
key_t key;
int shm_id;
int sem_id;
int value = 0;
//1.Product the key
key = ftok(".", 0xFF);
//2. Creat semaphore for visit the shared memory
sem_id = semget(key, 1, IPC_CREAT|0644);
if(-1 == sem_id)
{
perror(“semget”);
exit(EXIT_FAILURE);
}
//3. init the semaphore, sem=0
if(-1 == (semctl(sem_id, 0, SETVAL, value)))
{
perror(“semctl”);
exit(EXIT_FAILURE);
}
//4. Creat the shared memory(1K bytes)
shm_id = shmget(key, 1024, IPC_CREAT|0644);
if(-1 == shm_id)
{
perror(“shmget”);
exit(EXIT_FAILURE);
}
//5. attach the shm_id to this process
char *shm_ptr;
shm_ptr = shmat(shm_id, NULL, 0);
if(NULL == shm_ptr)
{
perror(“shmat”);
exit(EXIT_FAILURE);
}
//6. Operation procedure
struct sembuf sem_b;
sem_b.sem_num = 0; //first sem(index=0)
sem_b.sem_flg = SEM_UNDO;
sem_b.sem_op = -1; //Increase 1,make sem=1
while(1)
{
if(1 == (value = semctl(sem_id, 0, GETVAL)))
{
printf("\nNow, receive message process running:\n");
printf("\tThe message is : %s\n", shm_ptr);
if(-1 == semop(sem_id, &sem_b, 1))
{
perror("semop");
exit(EXIT_FAILURE);
}
}
//if enter "end", then end the process
if(0 == (strcmp(shm_ptr ,“end”)))
{
printf("\nExit the receiver process now!\n");
break;
}
}
shmdt(shm_ptr);
//7. delete the shared memory
if(-1 == shmctl(shm_id, IPC_RMID, NULL))
{
perror(“shmctl”);
exit(EXIT_FAILURE);
}
//8. delete the semaphore
if(-1 == semctl(sem_id, 0, IPC_RMID))
{
perror(“semctl”);
exit(EXIT_FAILURE);
}
return 0;
}
Receiver正确的输出了Sender写入共享内存中的数据。删除互斥代码后,sender开始运行后,scanf会自动阻塞,等待用户输入后再度唤醒。而receiver由于没有了信号量的限制,无限输出,难以继续运行。
b)
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main()
{
int filedes[2];
char buf[80];
pid_t pid_1;
if(-1 == pipe(filedes))
{
perror(“pipe”);
exit(EXIT_FAILURE);
}
pid_1 = fork();
if(pid_1 > 0){
// father thread
sleep(2);
printf("This is the father thread.\n");
char s[] = "Hello, this is written by father.\n";
write(filedes[1],s,sizeof(s));
close(filedes[0]);
close(filedes[1]);
} else if(pid_1 == 0)
{
// child thread
printf("This is the child thread.\n");
read(filedes[0],buf,sizeof(buf));
printf("%s\n",buf);
close(filedes[0]);
close(filedes[1]);
}
return 0;
}
在父进程未向管道写入数据前,子进程在原地阻塞;当父进程执行完毕写入操作时,子进程才重新获得CPU得以将管道内容输出
c)client.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <signal.h>
#define BUF_SIZE 128
//Rebuild the strcut (must be)
struct msgbuf
{
long mtype;
char mtext[BUF_SIZE];
};
int main(int argc, char *argv[])
{
//1. creat a mseg queue
key_t key;
int msgId;
printf(“THe process(%s),pid=%d started~\n”, argv[0], getpid());
key = ftok(".", 0xFF);
msgId = msgget(key, IPC_CREAT|0644);
if(-1 == msgId)
{
perror(“msgget”);
exit(EXIT_FAILURE);
}
//2. creat a sub process, wait the server message
pid_t pid;
if(-1 == (pid = fork()))
{
perror(“vfork”);
exit(EXIT_FAILURE);
}
//In child process
if(0 == pid)
{
while(1)
{
alarm(0);
alarm(100); //if doesn’t receive messge in 100s, timeout & exit
struct msgbuf rcvBuf;
memset(&rcvBuf, ‘\0’, sizeof(struct msgbuf));
msgrcv(msgId, &rcvBuf, BUF_SIZE, 2, 0);
printf(“Server said: %s\n”, rcvBuf.mtext);
}
exit(EXIT_SUCCESS);
}
else //parent process
{
while(1)
{
usleep(100);
struct msgbuf sndBuf;
memset(&sndBuf, ‘\0’, sizeof(sndBuf));
char buf[BUF_SIZE] ;
memset(buf, ‘\0’, sizeof(buf));
printf("\nInput snd mesg: “);
scanf(”%s", buf);
strncpy(sndBuf.mtext, buf, strlen(buf)+1);
sndBuf.mtype = 1;
if(-1 == msgsnd(msgId, &sndBuf, strlen(buf)+1, 0))
{
perror(“msgsnd”);
exit(EXIT_FAILURE);
}
//if scanf “end~”, exit
if(!strcmp(“end~”, buf))
break;
}
printf(“THe process(%s),pid=%d exit~\n”, argv[0], getpid());
}
return 0;
}
sever.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <signal.h>
#define BUF_SIZE 128
//Rebuild the strcut (must be)
struct msgbuf
{
long mtype;
char mtext[BUF_SIZE];
};
int main(int argc, char argv[])
{
//1. creat a mseg queue
key_t key;
int msgId;
key = ftok(".", 0xFF);
msgId = msgget(key, IPC_CREAT|0644);
if(-1 == msgId)
{
perror(“msgget”);
exit(EXIT_FAILURE);
}
printf(“Process (%s) is started, pid=%d\n”, argv[0], getpid());
while(1)
{
alarm(0);
alarm(600); //if doesn’t receive messge in 600s, timeout & exit
struct msgbuf rcvBuf;
memset(&rcvBuf, ‘\0’, sizeof(struct msgbuf));
msgrcv(msgId, &rcvBuf, BUF_SIZE, 1, 0);
printf(“Receive msg: %s\n”, rcvBuf.mtext);
struct msgbuf sndBuf;
memset(&sndBuf, ‘\0’, sizeof(sndBuf));
strncpy((sndBuf.mtext), (rcvBuf.mtext),strlen(rcvBuf.mtext)+1);
sndBuf.mtype = 2;
if(-1 == msgsnd(msgId, &sndBuf, strlen(rcvBuf.mtext)+1, 0))
{
perror(“msgsnd”);
exit(EXIT_FAILURE);
}
//if scanf “end~”, exit
if(!strcmp(“end~”, rcvBuf.mtext))
break;
}
printf(“THe process(%s),pid=%d exit~\n”, argv[0], getpid());
return 0;
}
server端正常工作,client接收到server回复。
5)阅读Pintos操作系统,找到并阅读进程上下文切换的代码,说明实现的保存和恢复的上下文内容以及进程切换的工作流程。
int main(){
…
/ 获取命令行参数 */
char *argv = parse_options (argv);
/ 初始化操作系统进程 /
thread_init ();
…
/ **空闲进程 idel thread,启动中断 /
thread_start ();
…
/ 调用命令行指示的程序开始执行 */
run_actions (argv);
…
}
static void run_actions (char **argv)
{
struct action // 执行动作描述结构体 {
char *name;
int argc;
void (*function) (char **argv);
};
/* 可识别命令行参数列表 */
static const struct action actions[] =
{
{“run”, 2, run_task},
…
};
while (argv != NULL)
{
…
/ 找到命令,传入参数,调用执行 */
a->function (argv);
argv += a->argc;
}
}
static void run_task (char **argv)
{
const char *task = argv[1]; // 获取运行需要的参数
printf (“Executing ‘%s’:\n”, task);
#ifdef USERPROG
process_wait (process_execute (task)); // 创建进程执行(针对用户程序) #else run_test (task);
#endif
printf (“Execution of ‘%s’ complete.\n”, task);
系统加载用户程序执行的基本方式是通过process_wait (process_execute (task));语句创建并执行任务。