操作系统实验二

  1. 1. 基于clone的系统调用
    1. 1.1. 编译&运行
    2. 1.2. 输出结果
  2. 2. 结果分析
    1. 2.1. 生产者
    2. 2.2. 消费者
  3. 3. 问题分析
    1. 3.1. 参考代码main中sleep的作用,若不修改sleep的参数会出现何种结果,为什么?
    2. 3.2. 实验过程中出现哪个问题,如何解决?
      1. 3.2.1. 无法编译
      2. 3.2.2. 主进程未等待

基于clone的系统调用

实验内容:

  1. 掌握clone()系统调用的形式和功能,了解线程间通过共享内存的方式实现通信。
  2. sem_wait和sem_post相当于P、V操作,掌握它们的使用方法,以及信号量s的说明和初始化。
  3. 掌握pthread_mutex_lock和pthread_mutex_unlock的使用方法,理解如何实现临界区的互斥。

设计原理:
用clone()创建4个轻进程,用参数指明共享内存等资源,通过共享内存模拟生产者-消费者问题
实验过程如下


基于clone的系统调用

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#define _GNU_SOURCE   
#include "sched.h"
#include<sys/types.h>
#include<sys/syscall.h>
#include<unistd.h>
#include <pthread.h>
#include "stdio.h"
#include "stdlib.h"
#include "semaphore.h"
#include "sys/wait.h"
#include "string.h"


int producer(void * args);
int consumer(void * args);
pthread_mutex_t mutex;
sem_t product;
sem_t warehouse;

char buffer[8][4];
int bp=0;

int main(int argc,char** argv){

pthread_mutex_init(&mutex,NULL);
sem_init(&product,0,0);
sem_init(&warehouse,0,8);
int clone_flag,arg,retval;
char *stack;


clone_flag=CLONE_VM|CLONE_SIGHAND|CLONE_FS| CLONE_FILES;

int i;
for(i=0;i<2;i++){
arg = i;

stack =(char*)malloc(4096);
retval=clone(producer,&(stack[4095]),clone_flag,(void*)&arg);

stack=(char*)malloc(4096);
retval=clone(consumer,&(stack[4095]),clone_flag,(void*)&arg);

sleep(55);
}

exit(1);
}

int producer(void *args){
int id = *((int*)args);
int i;
for(i=0;i<10;i++){
sleep(i+1);
sem_wait(&warehouse);
pthread_mutex_lock(&mutex);
if(id==0)
strcpy(buffer[bp],"aaa/0");
else
strcpy(buffer[bp],"bbb/0");
bp++;
printf("producer %d produce %s in %d\n",id,buffer[bp-1],bp-1);
pthread_mutex_unlock(&mutex);
sem_post(&product);
}
printf("producer %d is over!\n",id);
exit(id);
}

int consumer(void *args){
int id = *((int*)args);
int i;
for(i=0;i<10;i++)
{
sleep(10-i);
sem_wait(&product);
pthread_mutex_lock(&mutex);
bp--;
printf("consumer %d get %s in %d\n",id,buffer[bp],bp+1);
strcpy(buffer[bp],"zzz\0");
pthread_mutex_unlock(&mutex);
sem_post(&warehouse);
}
printf("consumer %d is over!\n",id);

exit(id);
}


保存为task2.c

编译&运行

1
gcc -pthread task2.c -o task2; ./task2

输出结果

输出结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
producer 0 produce aaa in 0
producer 1 produce bbb in 1
producer 0 produce aaa in 2
producer 1 produce bbb in 3
producer 0 produce aaa in 4
producer 1 produce bbb in 5
consumer 0 get bbb in 5
consumer 1 get aaa in 4
producer 0 produce aaa in 4
producer 1 produce bbb in 5
producer 0 produce aaa in 6
producer 1 produce bbb in 7
consumer 0 get bbb in 7
consumer 1 get aaa in 6
producer 1 produce bbb in 6
producer 0 produce aaa in 7
consumer 0 get aaa in 7
consumer 1 get bbb in 6
producer 1 produce bbb in 6
producer 0 produce aaa in 7
consumer 0 get aaa in 7
consumer 1 get bbb in 6
producer 1 produce bbb in 6
producer 0 produce aaa in 7
consumer 0 get aaa in 7
consumer 1 get bbb in 6
consumer 0 get bbb in 5
consumer 1 get aaa in 4
producer 1 produce bbb in 4
producer 0 produce aaa in 5
consumer 0 get aaa in 5
consumer 1 get bbb in 4
consumer 0 get bbb in 3
consumer 1 get aaa in 2
consumer 0 get bbb in 1
consumer 1 get aaa in 0
producer 1 produce bbb in 0
producer 1 is over!!!!!
producer 0 produce aaa in 1
producer 0 is over!!!!!
consumer 0 get aaa in 1
consumer 0 is over!!!!!
consumer 1 get bbb in 0
consumer 1 is over!!!!!

结果分析

  1. clone()语句在创建进程时,可通过参数设定子进程与父进程是否共享存储空间
  2. 多个进程共享内存需要互斥机制,程序中定义了临界区变量mutex和信号量product,warehouse。临界区变量用于共享内存操作的互斥,信号量分别实现了生产者和消费者的等待。
  3. 消费者输出存储区中的数据,并且存储区中的数据随着生产者存入的数据而发生变化,说明clone()语句通过clone_flag的参数设定实现了共享内存。若在实验中除去CLONE_VM选项,将出现非预期的结果。

生产者

  • 生产者线程通过循环来模拟生产10个产品的过程。
  • 在每次生产之前,生产者线程会调用 sleep() 函数以不同的时间间隔等待(越来越慢)。
  • 生产者线程首先调用 sem_wait(&warehouse) 函数来获取 warehouse 信号量(获取后减一),以确保有可用的缓冲区空间(初始为8),否则等待消费者释放一个空间。
  • 然后,它通过调用 pthread_mutex_lock(&mutex) 函数获取互斥锁,以确保在修改共享缓冲区时不会发生冲突。
  • 生产者根据线程的 ID (0 或 1) 将字符串 “aaa” 或 “bbb” 复制到缓冲区的下一个位置,并将 bp 增加。
  • 生产者线程输出生产的产品信息。
  • 最后,生产者线程释放互斥锁(调用 pthread_mutex_unlock(&mutex))并通过调用 sem_post(&product) 函数增加 product 信号量的值,表示有一个新产品可用。

消费者

  • 每个消费者线程通过调用 clone() 函数创建,并传递一个指向 consumer 函数的指针作为入口参数。
  • 消费者线程通过循环来模拟消费10个产品的过程。
  • 在每次消费之前,消费者线程会调用 sleep() 函数以不同的时间间隔等待。
  • 消费者线程首先调用 sem_wait(&product) 函数来获取 product 信号量(初始为0,获取到后减一,否则等待),以确保有可用的产品可以消费。
  • 然后,它通过调用 pthread_mutex_lock(&mutex) 函数获取互斥锁,以确保在修改共享缓冲区时不会发生冲突。
  • 消费者从缓冲区中取出最后一个产品,将其值复制为 “zzz”,然后将 bp 减少。
  • 消费者线程输出消费的产品信息。
  • 最后,消费者线程释放互斥锁(调用 pthread_mutex_unlock(&mutex))并通过调用 sem_post(&warehouse) 函数增加 warehouse 信号量的值,表示有一个缓冲区空间可用。

问题分析

  1. 参考代码main中sleep的作用,若不修改sleep的参数会出现何种结果,为什么?
  2. 实验过程中出现哪个问题,如何解决?

参考代码main中sleep的作用,若不修改sleep的参数会出现何种结果,为什么?

在给定的代码中,main函数中的sleep(1)用于在每次创建生产者和消费者线程后休眠1秒钟。这个休眠的作用是为了确保新创建的线程有足够的时间进行初始化和启动,以防止可能的竞争条件。
在删除sleep函数后,只有生产者1和消费者1正常工作

实验过程中出现哪个问题,如何解决?

无法编译

gcc 编译多进程程序时需要加上编译选项 -pthread

主进程未等待

在多次尝试执行时,输出以下结果后便终止

1
2
3
4
producer 0 produce aaa in 0
producer 0 produce aaa in 0
producer 1 produce bbb in 1
producer 1 produce bbb in 1

猜测原因是主进程过早退出,导致子进程没有执行完,在主函数结尾加一个sleep(56);后解决

在去除main函数的sleep函数后,主进程退出,但是后台进程仍在运行,可以正常输出