printf("%s",m);
fflush(stdout);
sleep
(1);
}
}
编译运行方法:
#gccsingle.c–osingle
#time./single
单线程执行结果:
多线程代码:
#multi_process.c
#include
#include
#include
#defineNUM5
intmain(void)
{
pid_tpid[3];
inti;
for(i=1;i<=3;i++){
pid[i-1]=fork();
if(pid[i-1]==0||pid[i-1]==-1)
break;
}
if(pid[0]==0){
execl("print","print","Good",NULL);
}
else{
if(pid[1]==0){
execl("print","print","Hello",NULL);
}
else{
if(pid[2]==0){
execl("print","print","201608030222",NULL);
}
wait();
wait();
wait();
exit(0);
}
}
return0;
}
实验分析:
第二个实验结果,第二个程序的多线程因为是并发执行,而且是有三个线程,所以在时间上几乎是单线程的1/3,而且因为是并发的,所以打印结果是无序的。
实验总结
本次实验首先要明确进程和程序的区别,我通过在Linux操作系统的进程创建和终止操作,运行父进程和子进程,查看运行结果和进程状态的变化。
实验过程中也了解了父进程与子进程的运行过程及其机制。
实验二进程同步
一、实验目的:
掌握基本的同步算法,理解经典进程同步问题的本质;学习使用Linux的进程同步机制,掌握相关API的使用方法;能利用信号量机制,采用多种同步算法实现不会发生死锁的哲学家进餐程序。
二、实验平台:
虚拟机:
VMWare9以上
操作系统:
Ubuntu12.04以上
编辑器:
Gedit|Vim
编译器:
Gcc
三、实验内容:
(1)以哲学家进餐模型为依据,在Linux控制台环境下创建5个进程,用semget函数创建一个信号量集(5个信号量,初值为1),模拟哲学家的思考和进餐行为:
每一位哲学家饥饿时,先拿起左手筷子,再拿起右手筷子;筷子是临界资源,为每一支筷子定义1个互斥信号量;想拿到筷子需要先对信号量做P操作,使用完释放筷子对信号量做V操作。
伪代码描述:
semaphorechopstick[5]={1,1,1,1,1};
•第i位哲学家的活动可描述为:
do{
printf("%disthinking\n",i);
printf("%dishungry\n",i);
wait(chopstick[i]);//拿左筷子
wait(chopstick[(i+1)%5]);//拿右筷子
printf("%diseating\n",i);
signal(chopstick[i]);//放左筷子
signal(chopstick[(i+1)%5]);//放右筷子
…
}while[true];
运行该组进程,观察进程是否能一直运行下去,若停滞则发生了什么现象?
并分析原因。
实验结果:
源码如下:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
unionsemun
{
intval;
structsemid_ds*buf;
unsignedshort*array;
structseminfo*_buf;
};
#defineERR_EXIT(m)\
do{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
//获取互斥信号量
voidwait_mutex(intmutex)
{
structsembufsb={0,-1,0};
semop(mutex,&sb,1);//对互斥信号量进行操作
}
//取得筷子
voidwait_v(intsemid,intnum)
{
structsembufsb={num,-1,0};
semop(semid,&sb,1);
}
//释放筷子
voidsignal_p(intsemid,intnum)
{
structsembufsb={num,1,0};
semop(semid,&sb,1);
}
//释放互斥变量mutex
voidsignal_mutex(intsemid0)
{
structsembufsb={0,1,0};
semop(semid0,&sb,1);
}
//ph函数
voidph(intnum,intsemid,intsemid0)
{
intleft=num;
intright=(num+1)%5;
for(;;)
{
printf("%disthinking\n",num);
sleep
(1);
printf("%dishungry\n",num);
sleep
(1);
//wait操作,控制哲学家最多4人能进餐
wait_mutex(semid0);
wait_v(semid,left);
wait_v(semid,right);
printf("%diseating\n",num);
sleep
(1);
//signal操作
signal_p(semid,right);//释放右筷子
signal_p(semid,left);//释放左快子
signal_mutex(semid0);//释放互斥信号量
}
}
intmain(intargc,char*argv[])
{
intsemid,semid0;
//创建两个信号量集
semid0=semget(IPC_PRIVATE,1,IPC_CREAT|0666);
semid=semget(IPC_PRIVATE,5,IPC_CREAT|0666);
//
unionsemunsu;
su.val=1;
inti;
for(i=0;i<5;i++)
{
//semctl()系统调用在一个信号量集(或集合中的单个信号量)上执行各种控制操作
semctl(semid,i,SETVAL,su);
}
//设定semid0信号量的初始值
unionsemunsu0;
su0.val=4;
semctl(semid0,0,SETVAL,su0);
//创建4个子进程
intnum=0;
pid_tpid;
for(i=1;i<5;i++)
{
pid=fork();
if(pid<0){ERR_EXIT("fork");}
if(pid==0){num=i;break;}
}
//第num个哲学家要做的事
ph(num,semid,semid0);
return0;
}
执行结果
实验总结
哲学家进餐的问题是操作系统信号量同步的经典例题了。
这次我通过解决哲学家进餐的哲学问题从而对进程同步有一个更好的理解,解决这个问题书中给出了三种解决方法。
我在实验中也是用这三种方法去定义信号量解决死锁问题。
通过信号量的获取与wait操作去控制进餐是通过互斥信号量的获取,若没有信号量便不能执行,而且只有四个哲学家能同时进餐也避免了死锁的出现。
实验三添加内核模块
一、实验目的:
学习Linux模块的基本概念和原理,学习内核模块编程的基本技术,利用内核模块编程访问进程描述符,操作内核的基本数据结构,加深对进程的理解;理解proc文件系统的作用,学习proc文件的创建方法,掌握这种用户态和核心态通信的方法。
二、实验平台:
虚拟机:
VMWare9
操作系统:
Ubuntu12.04
编辑器:
Gedit|Vi
三、实验内容:
(1)阅读内核模块实例hello.c,掌握内核模块的主要构成;阅读Makefile文件,理解内核模块的编译方法及执行过程;掌握模块安装、卸载,以及查看模块信息的方法。
将hello.o和Makefile文件放在一个文件夹workspace中,使用make命令使其生成了hello.ko等文件,如图:
使用命令载入模块,如图:
使用命令显示载入系统的模块,如图:
使用dmesg命令查看系统的内核模块信息如图:
使用sudormmodhello命令卸载该模块:
(2)设计一个模块,功能是列出系统中所有内核进程的程序名、PID号和进程状态。
主要步骤:
阅读内核源代码,了解进程描述符task_struct中与本实验有关的成员项,以及访问进程队列的宏for_each_process;
编写readprocess模块,获取进程信息;
修改Makefile文件,编译、安装模块,查看输出信息;
查看模块信息,卸载模块。
源代码如下:
readprocess.c代码:
#include
#include
#include
#include
MODULE_LICENSE("GPL");
staticintmod_init_readprocess(void);
staticvoidmod_exit_readprocess(void);
module_init(mod_init_readprocess);
module_exit(mod_exit_readprocess);
intmod_init_readprocess(void)
{
printk(KERN_INFO"-------start-------\n");
structtask_struct*p;
printk("displaymoudleinfois:
\n");
for_each_process(p)
{
printk("NAME:
%s\t\tPID:
%d\t\tSTATE:
%ld\t\t\n",p->comm,p->pid,p->state);
}
return0;
}
voidmod_exit_readprocess(void)
{
printk(KERN_INFO"--------end--------\n");
}
Makefile代码:
ifneq($(KERNELRELEASE),)
obj-m:
=readprocess.o
else
KDIR:
=/lib/modules/$(shelluname-r)/build
PWD:
=$(shellpwd)
default:
$(MAKE)-C$(KDIR)M=$(PWD)modules
clean:
$(MAKE)-C$(KDIR)M=$(PWD)clean
Endif
将process.c和Makefile文件放在同一个文件夹下使用make函数生成后缀为.ko文件:
使用命令载入模块,如图:
使用lsmod命令显示载入系统的模块,如图:
使用dmesg命令查看到系统的内核模块信息,如图:
使用sudorrmodhello命令卸载该模块,如图:
(3)利用内核模块编程,在/proc目录下用自己的学号创建一个目录,如/proc/201300834101然后在学号目录下创建一个processinfo文件,如/proc/201300834101/processinfo,此文件为只读文件,用于显示所有内核进程的程序名、PID号和进程状态。
主要步骤:
修改
(2)中readprocess模块,在模块初始化函数中创建目录及proc文件,并定义产生proc文件内容的函数(获取进程信息);在卸载模块函数中删除相应的proc文件及目录;
修改Makefile文件,编译、安装模块;
执行cat/proc/201300834101/processinfo命令,查看进程信息。
processinfo文件源代码:
#include
#include
#include
#include
#include
MODULE_LICENSE("GPL");
staticintmod_init_readprocess(void);
staticvoidmod_exit_readprocess(void);
module_init(mod_init_readprocess);
module_exit(mod_exit_readprocess);
structproc_dir_entry*feeyu_dir,*processinfo_file;
intprocessinfo_read_procmem(char*page,char**start,off_toffset,intcount,int*eof,void*data);
intmod_init_readprocess(void)
{
printk(KERN_INFO"-------Let'sGo-------\n");
feeyu_dir=proc_mkdir("201300824419",NULL);
processinfo_file=create_proc_read_entry("processinfo",0,cg_dir,processinfo_read_procmem,NULL);
return0;
}
voidmod_exit_readprocess(void)
{
remove_proc_entry("processinfo",cg_dir);
remove_proc_entry("201300824419",NULL);
printk(KERN_INFO"--------ThankYou--------\n");
}
intprocessinfo_read_procmem(char*page,char**start,off_toffset,intcount,int*eof,void*data)
{
intlen=0;
structtask_struct*p;
printk("所有内核进程信息:
\n");
for_each_process(p)
{
len+=sprintf(page+len,"NAME:
%sPID:
%dSTATE:
%ld\n",p->comm,p->pid,p->state);
}
returnlen;
}
Makefile文件源代码:
ifneq($(KERNELRELEASE),)
obj-m:
=processinfo.o
else
KDIR:
=/lib/modules/$(shelluname-r)/build
PWD:
=$(shellpwd)
default:
$(MAKE)-C$(KDIR)M=$(PWD)modules
clean:
$(MAKE)-C$(KDIR)M=$(PWD)clean
Endif
加载模块:
查看模块是否存在:
查看模块信息:
查看进程信息:
卸载模块后
实验总结
这次实验内容较多,重要是通过一些命令和代码对内核模块的进程进行操作。
使用命令对系统内核模块进行操作,通过processinfo文件和makefile文件对内核中的进程进行读写等操作。
实验很繁杂。
但思路清晰后也能较完整的把整个实验做下来。
通过这次实验,我对内核模块的操作及内核的一些进程上的操作有的一定的了解。
实验四统计操作系统缺页次数
实验目的
学习虚拟内存的基本原理和Linux虚拟内存管理技术;
深入理解、掌握Linux的按需调页过程;
掌握内核模块的概念和操作方法,和向/proc文件系统中增加文件的方法;
综合运用内存管理、系统调用、proc文件系统、内核编译的知识。
实验内容
1.原理
Linux的虚拟内存技术采用按需调页,当CPU请求一个不在内存中的页面时,会发生缺页,缺页被定义为一种异常(缺页异常),会触发缺页中断处理流程。
每种CPU结构都提供一个do_page_fault处理缺页中断。
由于每发生一次缺页都要进入缺页中断服务函数do_page_fault一次,所以可以认为执行该函数的次数就是系统发生缺页的次数。
因此可以定义一个全局变量pfcount作为计数变量,在执行do_page_fault时,该变量值加1。
本实验通过动态加载模块的方法,利用/proc文件系统作为中介来获取该值。
2.实验环境
操作系统:
Ubuntu12.04(内核版本为3.2.0-23-generic-pae)
内核源码:
linux-3.2.58
实验过程
1.下载一