《操作系统原理》实验二:父子进程、线程与进程同步

一、实验目的

1)理解操作系统线程的概念和应用编程过程;

2)理解线程的同步概念和编程;

二、实验内容

1)在Ubantu 或Fedora 环境使用fork 函数创建一对父子进程,分别输出各自的进程号和提示信息串。

2)在Ubantu 或Fedora 环境使用pthread_create 函数创建2 个线程A 和B。线程A 在屏幕上用while 循环顺序递增地输出1-1000 的自然数;线程B 在屏幕上用while 循环顺序递减地输出1000-1 之间的自然数。为避免输出太快,每隔0.5秒输出一个数。

3)在windows 环境下,利用高级语言编程环境(限定为VS 环境或VC 环境)调用CreateThread 函数实现(2)的功能。

4)在windows 环境下,利用高级语言编程环境(限定为VS 环境或VC 环境)调用CreateThread 函数和相关的同步函数,模拟实现“生产者-消费者”问题。“生产者-消费者”模拟实验的具体要求见后面附件。

5)在windows 环境下,利用高级语言编程环境(限定为VS 环境或VC 环境)调用CreateThread 函数实现“并发地画圆和画方”。圆的中心,半径,颜色,正方形的中心,边长,颜色等参数自己确定,合适就行。圆和正方形的边界上建议取720 个点。为直观展示绘制的过程,每个点绘制后睡眠0.2 秒~0.5 秒。

6)在windows 环境下,利用高级语言编程环境(限定为VS 环境或VC 环境)调用CreateThread 函数实现“文件拷贝小工具”。功能如下:1)具有一个编辑框,让用户任意指定源目录或文件,2)具有一个编辑框,让用户任意指定目的目录或文件;3)具有“开始拷贝”按钮;4)具有“停止拷贝”按钮5)具有显示拷贝进度的label,当为目录拷贝时以文件数来统计进度,当为文件拷贝时以字节数来统计进度。

三、实验过程

1)使用fork()创建父子进程

①任务环境

Ubuntu版本:16.04 64位

Linux内核版本:4.5.0-29-generic

②编写在Linux新建一个空文档,命名为T2_1.c

并添加如下代码:

《操作系统原理》实验二:父子进程、线程与进程同步_第1张图片

说明:

由 fork 创建的新进程被称为子进程。该函数被调用一次,但返回两次。两次返回的区别是子进程的返回值是0,而父进程的返回值则是新进程(子进程)的进程 id。子进程可以调用getpid()来获取自己的pid;也可以调用getppid()来获取父进程的id。

fork之后,操作系统会复制一个与父进程完全相同的子进程,虽说是父子关系,但是在操作系统看来,他们更像兄弟关系,这2个进程共享代码空间,但是数据空间是互相独立的,子进程数据空间中的内容是父进程的完整拷贝,指令指针也完全相同,子进程拥有父进程当前运行到的位置

③通过gcc T2_1.c命令进行编译

《操作系统原理》实验二:父子进程、线程与进程同步_第2张图片

④编译产生a.out文件

《操作系统原理》实验二:父子进程、线程与进程同步_第3张图片

⑤输入./a.out运行编译后的文件

《操作系统原理》实验二:父子进程、线程与进程同步_第4张图片

2)Ubuntu下实现1-1000的正反输出

未选题

3)Windows下实现1-1000的正反输出

①编写代码:

#include  
#include  
#include  
  
//线程函数,输出0-1000或1000-0的自然数  
DWORD WINAPI print1(LPVOID n) {  
    int num = (int)n;  
    int i = 0;  
    time_t lasttime;  
    while (i < num)  
    {  
        time(&lasttime);  
        while (1)  
        {  
            time_t nowtime;  
            time(&nowtime);  
            if (nowtime - lasttime >= 0.5)  
            {  
                printf("test1->%d\n", i);  
                i++;  
                break;  
            }  
        }  
  
    }  
    printf("\n");  
    return 0;  
}  
  
DWORD WINAPI print2(LPVOID n) {  
    int num = (int)n;  
    int i = 1000;  
    time_t lasttime;  
    while (i > num)  
    {  
        time(&lasttime);  
        while (1)  
        {  
            time_t nowtime;  
            time(&nowtime);  
            if (nowtime - lasttime >= 0.5)  
            {  
                printf("test2->%d\n", i);  
                i--;  
                break;  
            }  
        }  
  
    }  
    printf("\n");  
    return 0;  
}  
  
int main()  
{  
    HANDLE hThread[2];  
    //创建线程,并调用函数打印输出  
    hThread[0] = CreateThread(NULL, 0, print1, (LPVOID)1000, 0, NULL);  
    hThread[1] = CreateThread(NULL, 0, print2, (LPVOID)1, 0, NULL);  
    //等待所有线程结束  
    WaitForMultipleObjects(2, hThread, TRUE, INFINITE);  
  
    CloseHandle(hThread[0]);  
    CloseHandle(hThread[1]);  
  
    return 0;  
}  

②运行结果:两个线程同时运行,交替输出

《操作系统原理》实验二:父子进程、线程与进程同步_第5张图片

4)VS环境模拟“生产者-消费者”问题

任务环境:Windows 10 x64 + Visual Studio 2017 Enterprise

代码实现:

#include   
#include   
  
#define N 100  
#define TRUE 1  
typedef int Semaphore;  
Semaphore full = 0, Empty = N;            //共享资源区满槽数目和空槽数目  
int in = 0, out = 0;                      //缓冲区生产,消费数据指针  
HANDLE mutex;  
int ProducerThread[5];  
int ConsumerThread[5];  
int Buffer[N + 4];                          //缓冲区  
  
int produce_item() {                      //生产(随机数)  
    return (rand() % N + N) % N;  
}  
  
//插入资源  
int insert_item(int item)  
{  
    in %= N;  
    printf("生产到缓冲区槽: %d\n", in);  
    Buffer[in] = item;  
    return Buffer[in++];  
}  
  
//移出资源  
int remove_item() {  
    out %= N;  
    printf("                       取走缓冲区槽 %d 的数\n", out);  
    return Buffer[out++];  
}  
  
void down(HANDLE handle) {                  //wait / P  
    WaitForSingleObject(handle, INFINITE);  
}  
  
void up(HANDLE handle) {                    //signal / V  
    ReleaseSemaphore(handle, 1, NULL);  
}  
  
DWORD WINAPI producer(LPVOID v) {  
  
    int item;  
  
    while (TRUE) {  
  
        item = produce_item();  
        if (Empty > 0) {           //down(empty)  
            Empty--;  
            down(mutex);          //down(mutex)  
            insert_item(item);  
            full++;               //up(full)  
            up(mutex);            //up(mutex)  
        }  
  
        Sleep(2000);  
    }  
    return 1;  
}  
  
DWORD WINAPI consumer(LPVOID v) {  
  
    int item;  
  
    while (TRUE) {  
  
        if (full > 0) {             //down(full)  
            full--;  
            down(mutex);           //down(mutex)  
            item = remove_item();  
            Empty++;               //up(empty)  
            up(mutex);             //up(mutex)  
        }  
  
        Sleep(2000);  
    }  
    return 1;  
}  
  
int main()  
{  
    DWORD Tid;  
  
    mutex = CreateSemaphore(             //创建互斥信号量mutex  
        NULL,  
        1,  
        1,  
        NULL  
    );  
  
    for (int i = 0; i<4; i++) {  
        ProducerThread[i] = i + 1;  
        CreateThread(                    //创建生产者线程  
            NULL,                        //不能被子线程继承  
            0,                           //默认堆栈大小  
            producer,                    //生产者函数  
            &ProducerThread[i],          //传参  
            0,                           //创建后立即执行  
            &Tid                         //线程ID  
        );  
        ConsumerThread[i] = i + 1;  
        CreateThread(NULL, 0, consumer, &ConsumerThread[i], 0, &Tid);   //创建消费者线程  
    }  
  
    Sleep(20000);  
    return 0;  
}  

《操作系统原理》实验二:父子进程、线程与进程同步_第6张图片

5)Windows下实现并发画圆与画方

任务环境:Windows 10 x64 + Visual Studio 2017 Enterprise

代码实现:

#include  
#include  
#include  
  
CRITICAL_SECTION cs;//定义临界区全局变量  
  
//画圆  
DWORD WINAPI circle(LPVOID n)  
{  
    EnterCriticalSection(&cs);  
    double m = 0;  
    int cx = 0, cy = 0;  
    for (cy = 10; cy >= -10; cy--)         //表示图形的第y行,通过for循环打印所有行  
    {  
        m = 2.5*sqrt(100 - cy * cy);       //用y作为自变量,根据弦长与纵坐标y的函数关系计算出此行上的弦                                                长的一半也就是两个星形符号之间的距离的一半,并用m表示。  
        for (cx = 1; cx < 50 - m; cx++)        //以50个字符长度为基准根据弦长来确定每行左数第一个星形的位                                                  置,此位置前全印空格  
            printf(" ");  
        printf(".");  
        Sleep(100);  
        for (; cx < 50 + m; cx++)              //以50个字符宽度为基准来确定每行第二个星形的位置  
            printf(" ");  
        printf(".\n");  
        Sleep(100);  
    }  
    LeaveCriticalSection(&cs);  
    return 0;  
}  
  
//画方  
DWORD WINAPI tangle(LPVOID n)  
{  
    EnterCriticalSection(&cs);  
    int x = 0, y = 0;  
    for (y = 20; y >= 0; y--)  
    {  
        if (y == 20 || y == 0) {  
            for (x = 1; x <= 20; x++)  
            {  
                printf(" ");  
                printf(".");  
                Sleep(100);  
                printf(" ");  
            }  
            printf("\n");  
        }  
        else if (y != 20 || y != 0)  
        {  
            printf(" ");  
            printf(".");  
            Sleep(100);  
            printf(" ");  
            for (x = 2; x < 20; x++)  
            {  
                printf(" ");  
                printf(" ");  
                printf(" ");  
            }  
            printf(" ");  
            printf(".");  
            Sleep(100);  
            printf(" ");  
            printf("\n");  
  
        }  
    }  
    LeaveCriticalSection(&cs);  
    return 0;  
}  
  
int main()  
{  
    //初始化临界区  
    InitializeCriticalSection(&cs);  
  
    HANDLE hThread[2];  
    //创建线程,并调用函数打印输出  
    hThread[0] = CreateThread(NULL, 0, circle, (LPVOID)0, 0, NULL);  
    hThread[1] = CreateThread(NULL, 0, tangle, (LPVOID)0, 0, NULL);  
    //等待所有线程结束  
    WaitForMultipleObjects(2, hThread, TRUE, INFINITE);  
  
    CloseHandle(hThread[0]);  
    CloseHandle(hThread[1]);  
  
    //删除临界区  
    DeleteCriticalSection(&cs);  
  
    getchar();  
  
    return 0;  
}  

《操作系统原理》实验二:父子进程、线程与进程同步_第7张图片

6)Windows下实现文件拷贝

未选题

四、实验结果

1)使用fork()创建父子进程

《操作系统原理》实验二:父子进程、线程与进程同步_第8张图片

父子进程成功创建,并输出了各自的进程号和提示信息串

2)Ubuntu下实现1-1000的正反输出

未选题

3)Windows下实现1-1000的正反输出

《操作系统原理》实验二:父子进程、线程与进程同步_第9张图片

两个线程同时运行,交替输出

4)VS环境模拟“生产者-消费者”问题

《操作系统原理》实验二:父子进程、线程与进程同步_第10张图片

结果如图所示

5)Windows下实现并发画圆与画方

《操作系统原理》实验二:父子进程、线程与进程同步_第11张图片

两个线程同时运行,实现了并发的画圆与画方

6)Windows下实现文件拷贝

未选题

五、体会

通过本次实验,我对操作系统线程的概念和生成过程、操作系统线程的同步概念有了更深刻的理解,掌握了如何在Linux与Windows下创建线程,对操作系统的功能与原理有了进一步的了解。

你可能感兴趣的:(实验,考试与课设)