linux实验报告

西安郵電學院

Linux实验报告

  1     进程  

  2     线程      

  3:      互斥     

实验1进程


1.1 实验目的
 通过观察、分析实验现象,深入理解进程及进程在调度执行和内存空间等方面的特点,
掌握在POSIX 规范中fork和kill系统调用的功能和使用。

1.2 实验要求
1.2.1 实验环境要求
1. 硬件
(1) 主机:Pentium III 以上;
(2) 内存:128MB 以上;
(3) 显示器:VGA 或更高;
(4) 硬盘空间:至少100MB 以上剩余空间。
2. 软件
Linux 操作系统,内核2.4.26 以上,预装有X-Window 、vi、gcc、gdb 和任
意web 浏览器。

1.2.2 学生实验前的准备工作
学习man 命令的用法,通过它查看fork 和kill 系统调用的在线帮助,并阅读参
考资料,学会fork 与kill 的用法。
复习C 语言的相关内容。

1.3 实验内容
1.先猜想一下这个程序的运行结果。假如运行“./process 20”,输出会是什么样?

执行./process20会输出10个进程交替执行。输入要删除的进程编号,回车则删除该进程,输入q,回车删除进程组,程序结束。

2.然后按照注释里的要求把代码补充完整,运行程序。可以多运行一会儿,并在
此期间启动、关闭一些其它进程,看process 的输出结果有什么特点,记录下这个结果。

3.开另一个终端窗口,运行“ps aux|grep process”命令,看看process 究竟启动了多少个进程。回到程序执行窗口,按“数字键+回车”尝试杀掉一两个进程,再到另一个窗口看进程状况。

执行./forkTest 10   输入命令 ps aux|grep ./forkTest 后显示有12个进程在运行(10个子进程,1个父进程,最后一个为ps aux|grep ./forkTest命令的进程号 ),删除1号进程后,再输入ps aux|grep ./forkTest命令,则显示的进程数为11.

按q 退出程序再看进程情况。

1.4 回答下列问题。

1. 你最初认为运行结果会怎么样?

进程交替执行,当输入某进程编号加回车后该进程会被杀死,不再输出,当输入q加回车后剩余的所有子进程都被杀死,程序运行结束。
2. 实际的结果什么样?有什么特点?试对产生该现象的原因进行分析。

结果与上述的预期相同。

特点:各进程交替执行。无确定顺序。

分析:当程序执行到for(i = 0; i < child_proc_number; i++)循环后,调用fork()函数创建第一个子进程,然后父进程与该子进程交替执行,当子进程抢到cpu时继续往下执行if(child_pid == 0)语句中的程序段,若在执行到do_something();语句时,则调用该函数,执行死循环for(;;)输出该进程的相关内容,并在输出语句后加上sleep(SLEEP_INTERVAL);语句,给其他进程抢占cpu的机会。这个过程不一定可以连续的执行下去,因为在该过程中,父进程随时可能抢到cpu,再次执行fork();创建另一个子进程或者执行else语句(由其上次执行到的位置决定)。该子进程的执行与第一个子进程类似。自此该3个进程交替执行,当父进程执行fork()时,则继续创建新的子进程以此类推。直到父进程完成for(i = 0; i < child_proc_number; i++)循环,child_proc_number个子进程创建完成。此时child_proc_number个子进程加一个父进程一起交替性运行,当父进程再次抢到cpu时,则执行while((ch = getchar()) != 'q')循环时,则按用户的输入杀死相应的进程。用户输入q时,则杀死子进程组,while((ch = getchar()) != 'q')循环结束,当执行return 0;语句后该父进程也结束,整个程序结束。
3. proc_number 这个全局变量在各个子进程里的值相同吗?为什么?

不相同。

因为子进程会把父进程的所有资源复制一份。而父进程在创建一个新的子进程时的i值都不同,当新创建子进程执行时,将i赋值给该子进程的变量proc_number。
4. kill 命令在程序中使用了几次?每次的作用是什么?执行后的现象是什么?

当输入要删除的进程编号时会调用一次kill,最后输入q结束时会调用一次kill。

输入要删除的进程编号后,执行的结果为:输出除过已被删掉的进程的其余进程信息。

输入q则显示“已终止”,整个程序执行结束。
5. 使用kill 命令可以在进程的外部杀死进程。进程怎样能主动退出?这两种退
出方式哪种更好一些?

进程自己调用exit()或程序执行完后return().

进程自己调用exit()结束的方式会比较好。
6.补充后的程序源代码。

#include <stdio.h>

#include <sys/types.h>

#include <unistd.h>

#include <signal.h>

#include <ctype.h>

#define MAX_CHILD_NUMBER 10

#define SLEEP_INTERVAL 2

int proc_number = 0;

void do_something();

int main(int argc, char *argv[])

{

       int child_proc_number = MAX_CHILD_NUMBER;

       int i;

       char ch;

       pid_t child_pid;

       pid_t pid[10] = {0};

       if(argc > 1)

       {

              child_proc_number = atoi(argv[1]);

              child_proc_number = (child_proc_number > 10) ? 10:child_proc_number;

       }

       for(i = 0; i < child_proc_number; i++)

       {

              child_pid = fork();

              if(child_pid == 0)

              {

                     proc_number = i;

                     pid[i] = getpid();

                     do_something();

              }

              else if(child_pid < 0)

              {

                     perror("fork error!\n");

              }

              else

              {

                     pid[i] = child_pid;

              }

       }

       while((ch = getchar()) != 'q')

       {

              if(isdigit(ch))

              {

                     if(pid[ch - '0'] != -1)

                     {

                            kill(pid[ch-'0'],SIGTERM);

                            pid[ch-'0'] = -1;

                     }

              }

       }

       kill(0,SIGTERM);

       return 0;

}

void do_something()

{

       for(;;)

       {

              printf("This prcess is NO.%*d\n",proc_number+3,proc_number);

              sleep(SLEEP_INTERVAL);

       }

}

实验2线程


2.1 实验目的
 通过观察、分析实验现象,深入理解线程及线程在调度执行和内存空间等方面的特点,并掌握线程与进程的区别。掌握在POSIX 规范中pthread_create() 函数的功能和使用方法

2.2 实验要求
2.2.1 实验环境要求
1. 硬件
(1) 主机:Pentium III 以上;
(2) 内存:128MB 以上;
(3) 显示器:VGA 或更高;
(4) 硬盘空间:至少100MB 以上剩余空间。
2. 软件
Linux 操作系统,内核2.4.26 以上,预装有X-Window 、vi、gcc、gdb 和任
意web 浏览器。

2.2.2 学生实验前的准备工作
阅读参考资料,了解线程的创建等相关系统调用。

2.3 实验内容
按照注释里的要求把代码补充完整,正确编译程序后,先预计一下这个程序的运行结果。具体的结果会是什么样?运行程序。开另一个终端窗口,运行“ps aux”命令,看看thread 的运行情况,注意查看thread 的CPU 占用率,并记录下这个结果。

预计输出的前三列数不相等,main_counter的值与sum的值相等,且为前三列显示的线程运行次数之和。

运行ps aux命令后,./thread的CPU占用率为197

2.4 实验中的问题

1. 你最初认为前三列数会相等吗?最后一列斜杠两边的数字是相等,还是大于或者
小于关系?

 不会相等,因为三个进程交替执行,每个进程运行的时间及次数都不确定。

  main_count的值应该等于sum的值等于3个线程执行次数的总和。
2. 最后的结果如你所料吗?有什么特点?试对原因进行分析。

不,前3列的输出结果与预期结果相同,但main_count的值大于sum的值。

分析:因为main_counter属于临界资源,可能会出现当一个线程正在执行main_counter++时,另一个线程也要执行main_counter++的情况,这样就会使main_counter的值不准确。又线程运行顺序的不确定性(交替性),可能在一个线程执行了main_counter++后,还未执行counter[thread_num]++时,CPU便被主线程抢去,执行for(i = 0; i < MAX_THREAD; i++)循环,使sum += counter[i];此时sum的值便比main_counter的值小了。再输出个各线程巡行的次数,及main_counter和sum的值,结果与预期的理论值不符。
3. thread 的CPU 占用率是多少?为什么会这样?

197。因为三个线程调用的函数执行的是一个死循环for(;;),使得CPU的利用率过高。


4. thread_worker()内是死循环,它是怎么退出的?你认为这样退出好吗?

 输入q回车,主线程会退出while循环,执行return()函数,结束整个程序。自然子线程也都被迫终止,死循环也就退出了!这样的退出不好。
5. 补充及修改后的程序源代码:

#include <stdio.h>

#include <sys/types.h>

#include <unistd.h>

#include <ctype.h>

#include <pthread.h>

#define MAX_THREAD 3

unsigned long long main_counter, counter[MAX_THREAD];    //unsigned long long 鏄瘮long杩橀暱鐨勬暣褰?€?

void* thread_worker(void *);

int main(int argc, char *argv[])

{

       int i,ch;

       int num[3];

       pthread_t thid,pthread_id[MAX_THREAD] = {0};

       for(i = 0; i < MAX_THREAD; i++)

       {

              num[i] = i;

              if(pthread_create(&thid,NULL,(void*)thread_worker,&num[i]) == 0)

              {

                     pthread_id[i] = thid;

                     //thread_worker(&i);

              }

              else

              {

                     printf("thread creater failed!\n");

              //     exit(1);

              }

       }

       do

       {

              unsigned long long sum = 0;

              /*姹傛墍鏈夌嚎绋媍ounter鐨勫拰*/

              for(i = 0; i < MAX_THREAD; i++)

              {            

                     sum += counter[i];

                     printf("%d %llu   ",i,counter[i]);

              }

              printf("\n\n%llu\n%llu\n\n",main_counter,sum);

       }while((ch = getchar()) != 'q');

       return 0;

}

void* thread_worker(void* p)

{

       int thread_num;

       thread_num = *((int*)p);

       for(;;)

       {

              main_counter++;

              counter[thread_num]++;

              sleep(1);

       }

       return NULL;

}

实验3互斥


3.1 实验目的
通过观察、分析实验现象,深入理解理解互斥锁的原理及特点
掌握在POSIX 规范中的互斥函数的功能及使用方法


3.2 实验要求
3.2.1 实验环境要求
1. 硬件
(1) 主机:Pentium III 以上;
(2) 内存:128MB 以上;
(3) 显示器:VGA 或更高;
(4) 硬盘空间:至少100MB 以上剩余空间。
2. 软件
Linux 操作系统,内核2.4.26 以上,预装有X-Window 、vi、gcc、gdb 和任意web 浏览器。

3.2.2 学生实验前的准备工作
准备好上节实验完成的程序thread.c 。
阅读参考资料,了解互斥锁的加解锁机制及相关的系统调用。

4.3 实验内容
1.找到thread.c 的代码临界区,用临界区解决main_counter 与sum 不同步的问题。

在使用到main_counter与sum临界资源的临界区上加上p,v操作。

修改后的thread.c的代码如下:

#include <stdio.h>

#include <sys/types.h>

#include <unistd.h>

#include <ctype.h>

#include <pthread.h>

#define MAX_THREAD 3

unsigned long long main_counter, counter[MAX_THREAD];    //unsigned long long 鏄瘮long杩橀暱鐨勬暣褰?€?pthread_mutex_t mutext=PTHREAD_MUTEX_INITIALIZER;

pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;

void* thread_worker(void *);

int main(int argc, char *argv[])

{

       int i,ch;

       int num[3];

       pthread_t thid,pthread_id[MAX_THREAD] = {0};

       for(i = 0; i < MAX_THREAD; i++)

       {

              num[i] = i;

              if(pthread_create(&thid,NULL,(void*)thread_worker,&num[i]) == 0)

              {

                     pthread_id[i] = thid;

             

              }

              else

              {

                     printf("thread creater failed!\n");

              //     exit(1);

              }

       }

       pthread_mutex_lock(&mutex);

       do

       {

      

              unsigned long long sum = 0;

              /*姹傛墍鏈夌嚎绋媍ounter鐨勫拰*/

              for(i = 0; i < MAX_THREAD; i++)

              {            

                     sum += counter[i];

                     printf("%d %llu   ",i,counter[i]);

              }

              printf("\n\n%llu\n%llu\n\n",main_counter,sum);

       }while((ch = getchar()) != 'q');

       pthread_mutex_unlock(&mutex);

       pthread_mutex_destroy(&mutex);

       pthread_mutex_destroy(&mutext);  

       return 0;

}

void* thread_worker(void* p)

{

       int thread_num;

      

       thread_num = *((int*)p);

       for(;;)

       {

              pthread_mutex_lock(&mutext);

              main_counter++;

              counter[thread_num]++;

              sleep(1);

              pthread_mutex_unlock(&mutext);

       }

       return NULL;

}

仔细阅读程序,编译程序后,先预计一下这个程序的运行结果。
运行程序。若程序没有响应,按ctrl+c 中断程序运行,然后再重新运行,如此
反复若干次,记录下每次的运行结果。
若产生了死锁,请修改程序,使其不会死锁。

4.4 实验中的问题

1. 你预想deadlock.c 的运行结果会如何?

 线程1 ,2会交替运行,且执行到一半会终止。
2. deadlock.c 的实际运行结果如何?多次运行每次的现象都一样吗?为什么会这样?

交替执行!每次执行到一半都会终止。

每次运行的结果不同。

线程终止是因为,线程的推进顺序不合法。如果在线程1执行pthread_mutex_lock(&mutex1);语句,申请1号资源成功后,线程2又执行pthread_mutex_lock(&mutex2);语句,申请2号资源成功。当线程1想要申请2号资源,即执行pthread_mutex_lock(&mutex2);语句或线程2想要申请1号资源,即执行pthread_mutex_lock(&mutex1);时,这两个线程将陷入无限的等待状态而产生死锁。

为避免死锁的产生,则应调换线程1或线程2对1,2号资源加锁的顺序。即使线程1,2对1,2号资源的加锁顺序一致。即一次性为其分配了它所需要的所有资源,避免了死锁的产生。
3.修改前补充的程序源代码

#include <stdio.h>

#include <sys/types.h>

#include <unistd.h>

#include <ctype.h>

#include <pthread.h>

#define LOOP_TIMES 10000

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;

pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;

void* thread_worker(void*);

void critical_section(int thread_num,int i);

int main(void)

{

int rtn,i;

    pthread_t pthread_id = 0;

    rtn = pthread_create(&pthread_id,NULL,thread_worker,NULL);

    if(rtn != 0)

    {

        printf("pthread_create ERROR!\n");

        return -1;

    }

    for(i = 0; i < LOOP_TIMES; i++)

    {

        pthread_mutex_lock(&mutex1);

        pthread_mutex_lock(&mutex2);

        critical_section(1,i);

        pthread_mutex_unlock(&mutex2);

        pthread_mutex_unlock(&mutex1);

    }

    pthread_mutex_destroy(&mutex1);

    pthread_mutex_destroy(&mutex2);

    return 0;

}

void* thread_worker(void* p)

{

    int i;

    for(i = 0; i < LOOP_TIMES; i++)

    {

        pthread_mutex_lock(&mutex2);

        pthread_mutex_lock(&mutex1);

        critical_section(2,i);

        pthread_mutex_unlock(&mutex2);

        pthread_mutex_unlock(&mutex1);

    }

}

void critical_section(int thread_num,int i)

{

    printf("Thread%d:%d\n",thread_num,i);

}

4.修改后补充的程序源代码

#include <stdio.h>

#include <sys/types.h>

#include <unistd.h>

#include <ctype.h>

#include <pthread.h>

#define LOOP_TIMES 10000

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;

pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;

void* thread_worker(void*);

void critical_section(int thread_num,int i);

int main(void)

{

    int rtn,i;

    pthread_t pthread_id = 0;

    rtn = pthread_create(&pthread_id,NULL,thread_worker,NULL);

    if(rtn != 0)

    {

        printf("pthread_create ERROR!\n");

        return -1;

    }

    for(i = 0; i < LOOP_TIMES; i++)

    {

        pthread_mutex_lock(&mutex1);

        pthread_mutex_lock(&mutex2);

        critical_section(1,i);

//      sleep(1);

        pthread_mutex_unlock(&mutex2);

        pthread_mutex_unlock(&mutex1);

    }

    pthread_mutex_destroy(&mutex1);

    pthread_mutex_destroy(&mutex2);

    return 0;

}

void* thread_worker(void* p)

{

    int i;

    for(i = 0; i < LOOP_TIMES; i++)

    {

        pthread_mutex_lock(&mutex1);

        pthread_mutex_lock(&mutex2);

        critical_section(2,i);

//      sleep(1);

        pthread_mutex_unlock(&mutex2);

        pthread_mutex_unlock(&mutex1);

    }

}

void critical_section(int thread_num,int i)

{

    printf("Thread%d:%d\n",thread_num,i);

//  sleep(1);

}  

相关推荐