OS第三次实验-计科1601-16281011

一、

实验题目

通过fork的方式,产生4个进程P1,P2,P3,P4,每个进程打印输出自己的名字,例如P1输出“I am the process P1”。要求P1最先执行,P2、P3互斥执行,P4最后执行。通过多次测试验证实现是否正确。

实验思路

实验要求P1最先执行P2、P3互斥执行,因此,我设计了互斥信号量mutex,mutex初值为0,在P1执行后mutex值为1,保证了P1最先执行,P2,P3都要wait这个互斥信号量,保证了他们的互斥执行。

实验还要求P4最后执行,为此设计了两个信号量flag1和flag2,分别由P2和P3signal,P4需wait这两个信号量,之后才能执行,这样就满足了实验的全部要求

实验代码(fork1.c)

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>



#include<sys/types.h>
#include<pthread.h> 
#include <fcntl.h> 
#include <sys/stat.h> 
#include <semaphore.h>    





int main(){

pid_t p1,p2,p3,p4;

sem_t* flag1= NULL;
sem_t* flag2 = NULL;
sem_t* mutex = NULL;

flag1 = sem_open("P2", O_CREAT, 0666, 0);
flag2 = sem_open("P3", O_CREAT, 0666, 0);
mutex = sem_open("mutex", O_CREAT, 0666, 0);

p1 = fork();
if(p1 > 0){
printf("I am the process P1\n");
sem_post(mutex);
sleep(1);
while((p3=fork())==-1);
if(p3>0)sleep(1);
else if(p3 == 0){
sem_wait(mutex);
printf("I am the process P3\n");
sem_post(mutex);
sem_post(flag2);
}
}
else if(p1 == 0){
while((p2=fork())==-1);
if(p2>0){
sem_wait(mutex);
printf("I am the process P2\n");
sem_post(mutex);
sem_post(flag1);
sleep(1);
while((p4=fork())==-1);
if(p4>0)sleep(1);
else if(p4 == 0){
sem_wait(flag1);
sem_wait(flag2);
printf("I am the process P4\n");
}}
}


sem_close(flag1);
sem_close(flag2);
sem_close(mutex);
sem_unlink("P2");
sem_unlink("P3");
sem_unlink("mutex");



return 0;
}

实验运行结果及分析

在这里插入图片描述
我们可以看到,在多次运行的情况下,进程按照P1-P2-P3-P4的顺序执行,满足了题目的要求,但还有一点小问题,没有出现P1-P3-P2-P4这样的顺序,应该在P2中加入sleep(1)这样的语句使得P3也有先执行的机会。

二、

实验题目

火车票余票数ticketCount 初始值为1000,有一个售票线程,一个退票线程,各循环执行多次。添加同步机制,使得结果始终正确。要求多次测试添加同步机制前后的实验效果。

实验思路

首先在不设置同步机制的情况下测试售票和退票线程,观察结果,如果出现问题,则引入同步机制。

实验代码(train1.c)

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>



#include<sys/types.h>
#include<pthread.h> 
#include <fcntl.h> 
#include <sys/stat.h> 
#include <semaphore.h>    

int train_tickets=1000;
sem_t* mySem = NULL;


void*sell(){
    int i = 2000000 ;
    while(i--){
         sem_wait(mySem);        
         train_tickets--;
        // sched_yield();         
       
         sem_post(mySem);     
}
}

void*refund(){
    int i = 2000000;
    while(i--){
       sem_wait(mySem);         
       train_tickets++;
        // sched_yield();         
              
       sem_post(mySem);     
}
}

int main(){

    pthread_t p1,p2;
    mySem = sem_open("train_ticket", O_CREAT, 0666, 1);
    pthread_create(&p1,NULL,sell,NULL);
    pthread_create(&p2,NULL,refund,NULL);
    pthread_join(p1,NULL);
    pthread_join(p2,NULL);
    sem_close(mySem);
    sem_unlink("train_ticket");
    printf("The number of train tickets is %d.\n",train_tickets);



    return 0;
}

实验运行结果及分析

首先尝试运行售票sell和退票线程refund各200次,最后显示余票仍为1000。

但运行售票sell和退票线程refund各20000次时,出现错误。(显示余票数为5910)

错误分析
当循环次数较小时,一个时间片就能处理完所有操作,不会发生错误,但循环次数变大后,一旦时间片耗尽,就会发生进程的切换,从而导致错误。

解决方式
引入同步机制,加入信号量mysem,使得售票进程sell和退票进程refund互斥,即使运行20000次,200000次扔仍能得到正确结果1000.
在这里插入图片描述

三、

实验题目

一个生产者一个消费者线程同步。设置一个线程共享的缓冲区, char buf[10]。一个线程不断从键盘输入字符到buf,一个线程不断的把buf的内容输出到显示器。要求输出的和输入的字符和顺序完全一致。(在输出线程中,每次输出睡眠一秒钟,然后以不同的速度输入测试输出是否正确)。要求多次测试添加同步机制前后的实验效果。

实验思路

字符缓冲区可以存10个字符,等效为一个拥有10个资源的信号量scanf_sem,每当读入一个新的字符,缓冲区的资源就会减少一个。

对于输出线程,缓冲区中必须有字符它才能输出,因此它的信号量print_sem初始值为0,每读入一个字符,print_sem加1。

为了防止输入10个以上字符时缓冲区溢出,利用==buf[i++%10]==来写入,就不会出现溢出的问题了。

实验代码(produce.c)

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>



#include<sys/types.h>
#include<pthread.h> 
#include <fcntl.h> 
#include <sys/stat.h> 
#include <semaphore.h>




sem_t* scanf_sem = NULL;
sem_t* print_sem = NULL;
   
char buf[10];
int i,j = 0;

   
void*scanf_char(){
    while(1){
        sem_wait(scanf_sem);
        char *t = &buf[i++%10];
        scanf("%c",t);
        sem_post(print_sem);
        }
}
   
void*print_char(){
    while(1){
        sleep(1); 
        sem_wait(print_sem);
        char *t = &buf[j++%10];  
        if(j==9)
          j=0;
        printf("%d:%c\n",j,*t);
        sem_post(scanf_sem);
    }
}
   
int main(){
    pthread_t p1,p2;
   
    scanf_sem = sem_open("scanf", O_CREAT, 0666, 10);
    print_sem = sem_open("print", O_CREAT, 0666, 0);
   
    pthread_create(&p1,NULL,scanf_char,NULL);
    pthread_create(&p2,NULL,print_char,NULL);
   
    pthread_join(p1,NULL);
    pthread_join(p2,NULL);
   
    sem_close(scanf_sem);
    sem_close(print_sem);
   
    sem_unlink("scanf");
    sem_unlink("print");
    return 0;
}

实验运行结果及分析

在这里插入图片描述

快速输入字符串时,程序依然能有序的输出字符。

1 共享内存

实验题目

通过实验测试,验证共享内存的代码中,receiver能否正确读出sender发送的字符串?如果把其中互斥的代码删除,观察实验结果有何不同?如果在发送和接收进程中打印输出共享内存地址,他们是否相同,为什么?

实验代码

sender.c

#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(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.c

#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发送的字符串。

然后我们删除其中互斥的代码。

删除互斥语句后的sender2.c

#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)
    {
        printf("\nNow, snd message process running:\n");
        printf("\tInput the snd message:  ");
        scanf("%s", shm_ptr);
       //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;
}

删除互斥语句后的receiver2.c

#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)
    {
        printf("\nNow, receive message process running:\n");
        printf("\tThe message is : %s\n", shm_ptr);

       //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;
}

在这里插入图片描述可以看到,在删除互斥访问的语句后,sender进程因为在等待用户的输入,一直停留,而receiver进程则会不停地访问共享的内存,不停输出用户输入的字符串。

打印Sender和Receiver进程中共享内存的地址
分别在Sender和Receiver中对应出加入如下语句:

printf("sender中共享内存的地址为:%p\n",shm_ptr);
printf("receiver中共享内存的地址为:%p\n",shm_ptr);

在这里插入图片描述

可以看到,两个进程中显示的内存地址并不一样,这里有什么原因呢?查阅资料,可能有下面的原因

  1. 现代操作系统中都存在ASLR(地址空间随机化),ASLR是⼀种针对缓冲区溢出的安全保护机制,具有ASLR机制的操作系统每次加载到内存的程序起始地址会随机变化。系统的这个随机化操作可能导致共享内存的地址不一致。
  1. 进程在挂载内存的时候使用的shmat()函数中的第二个参数使用的是NULL,NULL参数的含义是进程让系统分配给共享内存合适的地址。并没有直接给shmat()函数一个确切的地址,所以会导致地址的不一致。

2 管道通信

实验题目

有名管道和无名管道通信系统调用是否已经实现了同步机制?通过实验验证,发送者和接收者如何同步的。比如,在什么情况下,发送者会阻塞,什么情况下,接收者会阻塞?

实验代码

无名管道

pipe.c

#include <stdio.h>
#include <unistd.h>     //for pipe()
#include <string.h>     //for memset()
#include <stdlib.h>     //for exit()
int main()
{
    int fd[2];
    char buf[20];
    if(-1 == pipe(fd))
    {
        perror("pipe");
        exit(EXIT_FAILURE);
    }
    write(fd[1], "hello,world", 12);
    memset(buf, '\0', sizeof(buf));
    read(fd[0], buf, 12);
    printf("The message is: %s\n", buf);
    return 0;
}
有名管道

fifo_send.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <fcntl.h>
#define FIFO "./my_fifo"

int main()
{
    char buf[] = "hello,world";
    //1. check the fifo file existed or not
    int ret;
    ret = access(FIFO, F_OK);
    if(ret != 0)    //file /tmp/my_fifo existed
    {
    	if(-1 == mkfifo(FIFO, 0766))
    	{
    	    perror("mkfifo");
    	    exit(EXIT_FAILURE);
    	}
    }

    //3.Open the fifo file
    int fifo_fd;
    fifo_fd = open(FIFO, O_WRONLY);
    if(-1 == fifo_fd)
    {
        perror("open");
        exit(EXIT_FAILURE);

    }
    //4. write the fifo file
    int num = 0;
    num = write(fifo_fd, buf, sizeof(buf));
    if(num < sizeof(buf))
    {
        perror("write");
        exit(EXIT_FAILURE);
    }
    printf("write the message ok!\n");

    close(fifo_fd);

    return 0;
}

fifo_rcv.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <fcntl.h>


#define FIFO "./my_fifo"

int main()
{
    char buf[20] ;
    memset(buf, '\0', sizeof(buf));

    //`. check the fifo file existed or not
    int ret;
    ret = access(FIFO, F_OK);
    if(ret != 0)    //file /tmp/my_fifo existed
    {
        if(-1==mkfifo(FIFO,0766))
        {
            perror("mkfifo"); 
            exit("EXIT_FAILURE");
        }
    }

//	2.Open the fifo file
    int fifo_fd;
    fifo_fd = open(FIFO, O_RDONLY);
    if(-1 == fifo_fd)
    {
        perror("open");
        exit(EXIT_FAILURE);
    }
    //4. read the fifo file
    int num = 0;
    num = read(fifo_fd, buf, sizeof(buf));
    printf("Read %d words: %s\n", num, buf);
    close(fifo_fd);
    return 0;
}

实验结果及分析

无名管道

在这里插入图片描述经过查阅资料,得到父子进程间利用无名管道同步通信的机理如下:

  1. 父进程调用pipe开辟管道,得到两个文件描述符指向管道的两端。
  2. 父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管道。
  3. 父进程关闭管道读端,子进程关闭管道写端。父进程可以往管道里写,子进程可以从管道里读,管道是用环形队列实现的,数据从写端流入从读端流出,这样就实现了进程间通信。

对pipe.c进行修改(pipe2.c),父进程将用户输入的信息不断写入管道,子进程不断将管道中的信息输出,如果内存被读进程锁定,父进程就会阻塞。如果内存被读进程锁定,同时若管道中无数据,子进程就会阻塞。
在这里插入图片描述

有名管道

在这里插入图片描述无论首先运行哪一个进程,它都会阻塞等待,只有当另一个进程开始运行后,两个进程才能正常执行。

试验有名管道的阻塞机制

编写四段程序,fifo_send.c(写阻塞),fifo_rcv.c(读阻塞),fifo_send2.c(写非阻塞),fifo_rcv2.c(读非阻塞)

(1)写进程阻塞,读进程阻塞。(如上)
先运行写进程(被阻塞),再运行读进程,一切正常。
先运行读进程(被阻塞),再运行写进程,一切正常。

(2)写进程阻塞,读进程非阻塞。

先运行写进程(被阻塞),再运行读进程,一切正常。
在这里插入图片描述

先运行读进程,程序崩溃(Segmentation fault (core dumped)),是由不等待直接读造成的。
在这里插入图片描述(3)写进程非阻塞,读进程阻塞。

先运行写进程,open调用将返回-1,打开失败。

先运行读进程(被阻塞),再运行写进程,一切正常。
在这里插入图片描述

(4)读写进程都是非阻塞

在这里插入图片描述

在这里插入图片描述总的来说,引起读进程阻塞的原因有,读进程阻塞的原因有三种:FIFO 中没有数据、有其他的读进程正在读取这些数据、没有写进程打开FIFO文件,对于写进程而言,阻塞的原因是管道中的空闲位置不足以容纳要写入的数据,泽写进程阻塞,直到管道中空间足够,一次性写入所有数据。

3 消息队列

实验题目

消息通信系统调用是否已经实现了同步机制?通过实验验证,发送者和接收者如何同步的。比如,在什么情况下,发送者会阻塞,什么情况下,接收者会阻塞?

实验代码

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;
}

Server.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;
}

实验结果及分析

在这里插入图片描述
客户端的子进程主要负责消息的接受,父进程主要负责消息的发送,客户端和服务器端都是以阻塞的方式读取和写入消息.

试验消息队列的同步和阻塞机制

消息队列通过msgrcv和msgsnd两个函数的flag参数控制是否阻塞,将其设置为IPC_NOWAIT表示不阻塞;如果客户端和服务器端都设置阻塞的话,就可以达到同步的目的

编写四段程序,client.c(阻塞),server.c(阻塞),client1.c(非阻塞),server1.c(非阻塞)

(1)客户端阻塞,服务器端不阻塞
在这里插入图片描述
我们可以看到当服务器端没有设置阻塞的时候,服务器端会一直接受消息队列中的空消息并向客户端转发。

(2)客服端不阻塞,服务器端阻塞

在这里插入图片描述我们可以看到当客户端不阻塞时,在客户端会无限制的打印消息队列中的空消息,即使消息队列中没有任何消息

实验题目

阅读Pintos操作系统,找到并阅读进程上下文切换的代码,说明实现的保存和恢复的上下文内容以及进程切换的工作流程。

分析

首先,我们找到关于进程上下文切换的函数timer_sleep(),它在devices/timer.c这个文件中

/* Sleeps for approximately TICKS timer ticks.  Interrupts must
   be turned on. */
void timer_sleep (int64_t ticks) 
{
  int64_t start = timer_ticks ();

  ASSERT (intr_get_level () == INTR_ON);
  while (timer_elapsed (start) < ticks) 
    thread_yield ();
}

timer_sleep()这个函数中,我们来一行一行分析,我们遇到了第一个函数timer_ticks ()

/* Returns the number of timer ticks since the OS booted. */
int64_t timer_ticks (void) 
{
  enum intr_level old_level = intr_disable ();
  int64_t t = ticks;
  intr_set_level (old_level);
  return t;
}

timer_ticks ()这个函数中,我们又遇到了函数intr_disable(),我们去查看这个函数的定义

/* Disables interrupts and returns the previous interrupt status. */
enum intr_level intr_disable (void) 
{
  enum intr_level old_level = intr_get_level ();

  /* Disable interrupts by clearing the interrupt flag.
     See [IA32-v2b] "CLI" and [IA32-v3a] 5.8.1 "Masking Maskable
     Hardware Interrupts". */
  asm volatile ("cli" : : : "memory");
  return old_level;
}

那么,intr_level又是什么呢?它的定义在devices/interrupt.h中

enum intr_level 
  {
    INTR_OFF,             /* Interrupts disabled. */
    INTR_ON               /* Interrupts enabled. */
  };

我们可以看出,intr_level表示是否允许中断,那么我们返回上一层intr_disable,它调用了intr_old_level函数,然后执行汇编代码保证这个线程不能被中断。之后返回调用intr_old_level函数的返回值。

我们继续看看*intr_get_level()*这个函数的作用是什么。

/* Returns the current interrupt status. */
enum intr_level intr_get_level (void) 
{
  uint32_t flags;
  /* Push the flags register on the processor stack, then pop the
     value off the stack into `flags'.  See [IA32-v2b] "PUSHF"
     and "POP" and [IA32-v3a] 5.8.1 "Masking Maskable Hardware
     Interrupts". */
  asm volatile ("pushfl; popl %0" : "=g" (flags));
  return flags & FLAG_IF ? INTR_ON : INTR_OFF;
}

可以看出*intr_get_level()*这个函数的作用是返回当前的中断状态,返回上一层intr_disable()函数。它的作用是,获取当前中断状态,然后将当前的中断标志改为不可中断,最后返回之前的中断状态。

返回*timer_ticks()*函数,64位的整型变量t为ticks,然后将当前的中断状态设置为之前的中断状态,最后返回t值。

最后返回*timer_sleep()*函数,64位的整型变量start变相获得了ticks的值,然后将当前的中断状态置为开,当start小于ticks后,放弃CPU,强迫线程切换。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值