实验2进程
2.1实验目的
通过观察、分析实验现象,深入理解进程及进程在调度执行和内存空间等方面的特点,
掌握在POSIX规范中fork和kill系统调用的功能和使用。
2.2实验要求
2.2.1实验环境要求
1.硬件
(1)主机:PentiumIII 以上;
(2)内存:128MB;
(3)显示器:VGA;
(4)硬盘空间:至少100MB以上剩余空间。
2.软件
操作系统:Linux;
内核:2.4.26以上
预装有:GuakeTerminal,Google浏览器
2.2.2学生实验前的准备工作
了解man命令中fork和kill的用法,查阅参考资料,学会fork与kill,复习C语言的相关内容。
2.3实验内容
通读下列代码:
/*
* POSIX 下进程控制的实验程序残缺版
*/
#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; /* 子进程的自编号,从0开始 */
void do_something();
main(int argc, char* argv[])
{
int child_proc_number = MAX_CHILD_NUMBER; /* 子进程个数 */
int i, ch;
pid_t child_pid;
pid_t pid[10]={0}; /* 存放每个子进程的id */
if (argc > 1)
{
/* 命令行参数中的第一个参数表示建立几个子进程,最多10个 */
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_proc_number个子进程
* 子进程要执行
* proc_number = i;
* do_something();
* 父进程把子进程的id保存到pid[i] */
}
/* 让用户选择杀死哪个进程。输入数字(自编号)表示杀死该进程
* 输入q退出 */
while ((ch = getchar()) != 'q')
{
if (isdigit(ch))
{
/* 在这里填写代码,向pid[ch-'0']发信号SIGTERM,
* 杀死该子进程 */
}
}
/* 在这里填写代码,杀死本组的所有进程 */
return;
}
void do_something()
{
for(;;)
{
/* 打印子进程自编号。为清晰,在每个号码前加“号码+3”个空格
* 比如号码是1,就打印" 1" */
printf("This is process No.%*d\n",
proc_number+3,
proc_number);
sleep(2); /* 主动阻塞两秒钟 */
}
}
先猜想一下这个程序的运行结果。假如运行“./process 20”,输出会是什么样?
然后按照注释里的要求把代码补充完整,运行程序。可以多运行一会儿,并在
此期间启动、关闭一些其它进程,看process 的输出结果有什么特点,记录下这个结果。
开另一个终端窗口,运行“ps aux|grep process”命令,看看process 究竟启动了多少个进程。回到程序执行窗口,按“数字键+回车”尝试杀掉一两个进程,再到另一个窗口看进程状况。
按q 退出程序再看进程情况。
2.4 实验报告
回答下列问题,写入实验报告。
1. 你最初认为运行结果会怎么样?
答:从0到9顺序打印,按下数字回车后杀死相对应进程,输入q回车后,则退出程序
2. 实际的结果什么样?有什么特点?试对产生该现象的原因进行分析。
答:
如图所示
实验结果与预期结不相同,产生的子进程数最多为10个,当产生小于10个, 则产生10个,大于10个,直接产生10个,随机输出0~9号进程,循环输 出。
输入数字键回车后,杀死该数字所对应的进程,输入q回车后,杀死所有进程,退出程序。
3. proc_number 这个全局变量在各个子进程里的值相同吗?为什么?
答,相同,子进程的资源相互独立,互不影响。
4. kill 命令在程序中使用了几次?每次的作用是什么?执行后的现象是什么?
答:kill命令在程序中使用了2次,kill(pid[ch-'0'],SIGTERM)和kill(0,SIGTERM); 第一次是杀死该进程号pid[ch-‘0’],输入进程号回车后,执行后接下来的结果中不会有该进程号,
第二次是杀死本组所有进程。即主进程以及它创建的所有子进程。执行后程序退出,进程结束。用另一个终端打开,使用命令ps -a,查看终端下运行的进程。
5. 使用kill 命令可以在进程的外部杀死进程。进程怎样能主动退出?这两种退出方式哪种更好一些?
答:进程在main函数中return,或调用exit()函数都可以正常退出,而使用kill命令则是异常退出。正常退出比较好,若在子进程退出前使用kill命令杀死其父进程,则系统会让init进程接管子进程。当用kill命令使得子进程先于父进程退出时,而父进程又没有调用wait函数等待子进程结束,子进程处于僵死状态,并且会一直保持下去,直到系统重启。子进程处于僵死状态时,内核只保存该进程的必要信息以被父进程所需,此时子进程始终占着资源,同时减少了系统可以创建的最大进程数,使用return正常退出更好一些
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; /* 子进程的自编号,从0开始 */
void do_something();
int main(int argc, char* argv[])
{
int child_proc_number = MAX_CHILD_NUMBER; /* 子进程个数 */
int i, ch;
pid_t child_pid;
pid_t pid[10]={0}; /* 存放每个子进程的id */
if (argc > 1)
{
/* 命令行参数中的第一个参数表示建立几个子进程,最多10个 */
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)
{
pid[i] = child_pid;
}
else if(child_pid == 0)
{
proc_number = i;
do_something();
}
else
{
perror("fail to fork!\n");
}
/* 在这里填写代码,建立child_proc_number个子进程
* 子进程要执行
* proc_number = i;
* do_something();
* 父进程把子进程的id保存到pid[i] */
}
/* 让用户选择杀死哪个进程。输入数字(自编号)表示杀死该进程
* 输入q退出 */
while ((ch = getchar()) != 'q')
{
if (isdigit(ch))
{
kill(pid[ch-'0'], SIGTERM);
/* 在这里填写代码,向pid[ch-'0']发信号SIGTERM,
* 杀死该子进程 */
}
}
/* 在这里填写代码,杀死本组的所有进程 */
kill(0, SIGTERM);
return 0;
}
void do_something()
{
for(;;)
{
printf("This is process No.%d and its pid is %d\n", proc_number, getpid());
sleep(2); /* 主动阻塞两秒钟 */
}
}
实验3线程
3.1实验目的
通过观察、分析实验现象,深入理解线程及线程在调度执行和内存空间等方
面的特点,并掌握线程与进程的区别。
掌握在POSIX规范中pthread_create()函数的功能和使用方法
3.2实验要求
3.2.1实验环境要求
1.硬件
(1)主机:PentiumIII 以上;
(2)内存:128MB;
(3)显示器:VGA;
(4)硬盘空间:至少100MB以上剩余空间。
2.软件
操作系统:Linux;
内核:2.4.26以上
预装有:GuakeTerminal,Google浏览器
3.2.2学生实验前的准备工作
阅读参考资料,了解线程的创建等相关系统调用。
3.3实验内容
通读下列代码:
/*
* POSIX 下线程控制的实验程序残缺版1.27 */
#include<stdio.h>
#include <sys/types.h>
#include<unistd.h>
#include <ctype.h>
#include<pthread.h>
#defineMAX_THREAD 3 /* 线程的个数*/
unsignedlong long main_counter, counter[MAX_THREAD]; /* unsignedlong
long是比long还长的整数*/
void*thread_worker(void*);
intmain(int argc, char* argv[])
{
int i, rtn, ch;
pthread_t pthread_id[MAX_THREAD] = {0};/* 存放每个线程的id*/
for(i=0; i<MAX_THREAD; i++)
{
/* 在这里填写代码,用pthread_create建一个普通的线程,
*线程id存入pthread_id[i],
}
* 线程执行的函数是thread_worker,并i作为参数传递给线程*/
/* 用户按一次回车执行下面的循环体一次。按q退出*/
do
{
unsignedlong long sum = 0;
/* 求所有线程的counter的和*/
for(i=0; i<MAX_THREAD; i++)
{
sum += counter[i];
printf("%llu ", counter[i]);
}
printf("%llu/%llu",main_counter, sum);
}
while ((ch = getchar()) != 'q');
return0;
}
void*thread_worker(void* p)
{
int thread_num;
/*在这里填写代码,把main中的i的值赋给thread_num*/
for(;;)/* 无限循环*/
{
counter[thread_num]++;/* 本线程的counter加一*/
main_counter++;/* 主counter加一*/
}
}
按照注释里的要求把代码补充完整,正确编译程序后,先预计一下这个程序的
运行结果。具体的结果会是什么样?
运行程序。开另一个终端窗口,运行“psaux”命令,看看thread的运行情
况,注意查看thread的CPU占用率,并记录下这个结果。
3.4实验报告
回答下列问题,写入实验报告。
1.你最初认为前三列数会相等吗?最后一列斜杠两边的数字是相等,还是大于或者小于关系?
答,最初我认为三列数字相等,最后一列斜杠两边的数字是也会相等的。
2.最后的结果如你所料吗?有什么特点?试对原因进行分析。
答:
最后结果有所差异,前三个值不会相等,前三个值之和等于sum值,main_counter值小于sum值,但二者相差不大。
分析:三个线程在共同争取运行thread_worker()函数,pthread_id[i]执行后main_counter++,比如在执行pthread_id[0]后pthread_id[1]又申请执行thread_worker()函数,这时候main_counter值就少加一次了,所以会发生main_counter值比sum值小的情况。
3.thread 的CPU占用率是多少?为什么会这样?
答:cpu占用率160,因为线程是无限死循环,所以占用率高。
4.thread_worker()内是死循环,它是怎么退出的?你认为这样退出好吗?
答:输入q,退出循环。子进程也会退出,这样退出不好。
5.把你的程序源代码附到实验报告后。并请保留源代码,下一个死锁实验需要使用
代码: