processlab.docx
《processlab.docx》由会员分享,可在线阅读,更多相关《processlab.docx(26页珍藏版)》请在冰豆网上搜索。
processlab
实验四进程及进程管理
4.1使用ps命令查看进程信息
【实验内容】
ps是基本的Linux命令,通过本实验,不仅要熟悉ps命令方法,更重要的是可以了解Linux进程的组成。
【实验原理】
ps:
查看系统中的进程,Linux中可以使用ps-aux查看所有进程。
其中PID代表进程ID,TTY是该进程是由哪个控制台启动的,CMD则是命令。
如果想列出更详细的信息,则可使用命令:
“ps-auxw”。
参数w表示加宽显示的命令行,参数w可以写多次,通常最多写3次,表示加宽3次,这足以显示很长的命令行了。
【实验内容】
在shell提示符下输入如下命令,并解释输出的结果:
[root@vmroot]#ps
PIDTTYTIMECMD
16767pts/10:
00ps
18029pts/10:
00bash
[root@vmroot]#psaux
PIDTTYTIMECMD
…
4.2使用proc文件系统查看进程信息
【实验目的】
本实验将指导学员了解proc文件系统,通过proc文件系统查询进程信息,可以扩展到修改系统参数。
【实验原理】
/proc文件系统是一个虚拟文件系统,通过文件系统接口实现对内核的访问,输出系统运行状态。
它以文件系统的形式,为操作系统本身和应用进程之间的通信提供了一个界面,使应用程序能够安全,方便的获得系统当前的运行状况和内核的内部数据信息,并且可以修改某些系统的配置信息。
【实验内容】
1)认识proc文件系统的文件和目录
[root@vmroot]#cd/proc
[root@vmroot]#ls
2)通过proc文件系统查看系统当前进行状态
[root@vmroot]#cat/proc/self/status
3)查询文件句柄的当前使用情况
#cat/proc/sys/fs/file-nr
42615252458
file-nr文件显示了三个参数:
分配的文件句柄总数、当前使用的文件句柄数以及可以分配的最大文件句柄数。
如果需要增大/proc/sys/fs/file-max中的值,请确保正确设置ulimit。
对于2.4.20,通常将其设置为unlimited。
使用ulimit命令来验证ulimit设置:
[root@vmroot]#ulimit
unlimited
4)通过proc文件系统修改内核中预定的一些变量
1)修改整个系统中文件句柄的最大数量
[root@vmroot]#ls/proc/sys/fs/file-max
52458
[root@vmroot]#echo65536>/proc/sys/fs/file-max
[root@vmroot]#ls/proc/sys/fs/file-max
65536
2)修改网络TTL
[root@vmroot]#ls/proc/sys/net/ipv4/ip_default_ttl
64
[root@vmroot]#echo128>/proc/sys/net/ipv4/ip_default_ttl
[root@vmroot]#ls/proc/sys/net/ipv4/ip_default_ttl
128
3)修改系统中最大进程数量
[root@vmroot]#ls/proc/sys/kernel/pid_max
32768
[root@vmroot]#echo65536>/proc/sys/kernel/pid_max
[root@vmroot]#ls/proc/sys/kernel/pid_max
65536
4)修改普通用户的最大RTC频率
[root@vmroot]#ls/proc/sys/dev/rtc/max-user-freq
64
[root@vmroot]#echo128>/proc/sys/dev/rtc/max-user-freq
[root@vmroot]#ls/proc/sys/dev/rtc/max-user-freq
128
5)其他一些信息
[root@vmroot]#cat/proc/cpuinfo-CPU
[root@vmroot]#cat/proc/interrupts-中断
[root@vmroot]#cat/proc/ioports-设备IO端口
[root@vmroot]#cat/proc/meminfo-内存信息(i.e.memused,free,swapsize)
[root@vmroot]#cat/proc/partitions-所有设备的所有分区
[root@vmroot]#cat/proc/pci-PCI设备的信息
[root@vmroot]#cat/proc/swaps-所有Swap分区的信息
[root@vmroot]#cat/proc/version-Linux的版本号
4.3使用fork、exit和exec系统调用编写多进程程序
【实验目的】
本实验将通过编写fork等系统调用的程序,加深对系统进程及其控制的了解。
【实验原理】
fork后父子进程会同步运行,但父子进程的返回顺序是不确定的。
设两个变量global和test来检测父子进程共享资源的情况。
同时在进程退出时对exit和_exit的区别进行测试和说明。
【实验内容】
1.fork
#include
#include
#include
#include
#include
#include
//#include
intglobal=22;
charbuf[]="thetestcontent!
\n";
intmain(void)
{
inttest=0,stat;
pid_tpid;
if(write(STDOUT_FILENO,buf,sizeof(buf))!
=sizeof(buf))
{perror("writeerror!
");}
printf("forktest!
\n");
/*fork*/
pid=fork();/*weshouldchecktheerror*/
if(pid==-1)
{
perror("fork");
exit(0);
}
elseif(pid==0)
{
global++;test++;
printf("global=%dtest=%dChild,myPIDis%d\n",global,test,getpid());
exit(0);
}
/*elsebetheparent*/
global+=2;
test+=2;
printf("global=%dtest=%dParent,myPIDis%d\n",global,test,getpid());
exit(0);
//printf("global=%dtest=%dParent,myPIDis%d",global,test,getpid());
//_exit(0);
}
编译执行,并分析结果:
[root@localhostroot]#./test
thetestcontent!
forktest!
global=23test=1Child,myPIDis2751
global=24test=2Parent,myPIDis2750
可以看出父子进程打印出了各自的进程号和对应变量的值,显然global和test在父子进程间是独立的,其各自的操作不会对对方的值有影响。
将上述代码最后的两行代码替换为注释掉的_exit(0)行,重新编译,查看结果,解释原因:
[root@localhostroot]#./test
thetestcontent!
forktest!
global=23test=1Child,myPIDis2771
父进程的信息没有打印出来,其原因是:
_exit()函数直接使进程停止运行,清除其使用的内存空间,并销毁其在内核中的各种数据结构;而exit()函数则在这些基础上作了一些包装,在执行退出之前加了若干道工序。
exit()函数在调用exit系统调用之前要检查文件的打开情况,把文件缓冲区中的内容写回文件,即会"清理I/O缓冲"。
若将上述_exit(0)改为exit(0),则肯定会有打印。
另外,需要注意换行符\n会引起IO的清理操作,若下面的语句printf("global=%dtest%dParent,myPIDis%d",global,test,getpid());加上\n,则调用_exit(0)的结果和调用exit(0)的结果是一样的。
2.vfork的特点
将上述代码的pid=fork();改为pid=vfork();编译后运行结果如下:
[root@localhostroot]#./test
thetestcontent!
forktest!
global=23test=1Child,myPIDis2849
global=25test=3Parent,myPIDis2848
可以看出,vfork与fork区别在于共享的资源不一样,vfork调用后,子进程先对global和test加1,父进程运行时,在其基础之上再加2,得到上述运行结果。
即vfork的特点是:
在调用execv或者exit前子进程对变量的修改会影响到父进程,即他们是共享的;
特别注意:
父进程等待子进程调用execv或exit才继续执行。
则若子进程依赖父进程的进一步动作时,父进程又必须阻塞到子进程调用execv或者exit才会往下执行,此时就会造成“死锁”。
读者可自己设计测试一下这种“死锁”状态。
4.4使用fork和exec系统调用编写执行命令的程序
【实验目的】
本实验将通过编写fork和exec等系统调用的程序,加深对系统进程及其控制的了解。
【实验原理】
fork后调用exec族函数来调用系统命令或者程序来实现系统shell功能。
【实验内容】
execv函数族的使用
注意点:
调用execv后,程序不再返回!
在上述代码基础上,在子进程的退出代码前加入如下代码:
printf("global=%dtest%dChild,myPIDis%d\n",global,test,getpid());
if(execl("/bin/ps","ps","-au",NULL)<0)
perror("execlerror!
");
printf("thismessagewillneverbeprinted!
\n");
exit(0);
编译运行后结果为:
[root@localhostroot]#./test
thetestcontent!
forktest!
global=23test=1Child,myPIDis2909
USERPID%CPU%MEMVSZRSSTTYSTATSTARTTIMECOMMAND
root27190.00.643601032pts/1S23:
140:
00/bin/bash
root29080.00.11340276pts/1R23:
380:
00./test
root29090.00.42684736pts/1R23:
380:
00ps-au
global=25test=3Parent,myPIDis2908
waitpid的作用是等待子进程退出并回收其资源,同时可以通过WIFEXITED等宏调用可以检测子进程退出的状态。
在第一个示例fork使用的代码基础上进行修改,添加检测进程退出状态的子函数,参考代码如下:
voidexit_check(intstat)
{
if(WIFEXITED(stat))
{printf("exitnormally!
thereturncodeis:
%d\n",WEXITSTATUS(stat));}
elseif(WIFSIGNALED(stat))
{printf("exitabnormally!
thesignalcodeis:
%d\n",WTERMSIG(stat));}
}
在父进程处理global和test变量前加入如下代码:
if(waitpid(pid,&stat,0)==pid)
{exit_check(stat);}//thestatusofexitcheck
编译运行后结果为:
[root@localhostroot]#./test
thetestcontent!
forktest!
global=23test=1Child,myPIDis2973
exitnormally!
thereturncodeis:
0
global=24test=2Parent,myPIDis2972
可以看出父进程回收了退出的子进程的资源,检测到了它的退出状态。
4.5编写一个守护进程
【实验目的】
守护进程是Linux系统开发中很重要的知识点,本实验要求学员编写一个守护进程,通过本实验,学员可以熟悉守护进程的编写过程。
【实验原理】
守护进程编写的主要步骤如下:
1)将程序进入后台执行。
由于守护进程最终脱离控制终端,到后台去运行。
方法是在进程中调用fork使父进程终止,让Daemon在子进程中后台执行。
这就是常说的“脱壳”。
子进程继续函数fork()的定义如下:
pid_tfork(void);
2)脱离控制终端、登录会话和进程组。
开发人员如果要摆脱它们,不受它们的影响,一般使用setsid()设置新会话的领头进程,并与原来的登录会话和进程组脱离。
3)禁止进程重新打开控制终端。
4)重设文件权限掩码
5)关闭打开的文件描述符,并重定向标准输入、标准输出和标准错误输出的文件描述符。
进程从创建它的父进程那里继承了打开的文件描述符。
如果不关闭,将会浪费系统资源,引起无法预料的错误。
关闭三者的代码如下:
for(fd=0,fdtablesize=getdtablesize();fd close(fd);
6)改变工作目录到根目录或特定目录进程活动时,其工作目录所在的文件系统不能卸下
【实验内容】
守护进程实例包括两部分:
主程序test.c和初始化程序init.c。
主程序每隔一分钟向/tmp目录中的日志test.log报告运行状态。
初始化程序中的init_daemon函数负责生成守护进程。
读者可以利用init_daemon函数生成自己的守护进程。
1.init.c
#include
#include
#include
#include
voidinit_daemon(void)
{
intpid;
inti;
if(pid=fork()){exit(0);}//是父进程,结束父进程
elseif(pid<0){exit(-1);}//fork失败,退出
setsid();//第一子进程成为新的会话组长和进程组长
//并与控制终端分离
if(pid=fork()){exit(0);}//是第一子进程,结束第一子进程
elseif(pid<0){exit
(1);}//fork失败,退出
//第二子进程不再是会话组长
for(i=0;iclose(i);
chdir("/tmp");//改变工作目录到/tmp
umask(0);
//重设文件创建掩模
return;
}
2.test.c
#include
#include
voidinit_daemon(void);//守护进程初始化函数
intmain()
{
FILE*fp;
time_tt;
init_daemon();//初始化为Daemon
while
(1)//每隔2秒钟向test.log报告运行状态
{
sleep
(2);//睡眠2秒钟
if((fp=fopen("test.log","a"))!
=NULL)
{
t=time(0);
fprintf(fp,"I'mhereat%sn",asctime(localtime(&t)));
fclose(fp);
}
}
return0;
}
编译:
[root@vmroot]#gcc–g–otestinit.ctest.c
执行:
[root@vmroot]#./test
查看进程:
[root@vmroot]#ps–ef
从输出可以发现test守护进程的各种特性满足上面的要求。
实验五进程间通信
UNIX/LINUX系统的进程间通信机构(IPC)允许在进程间交换数据。
本实验的目的是了解和熟悉LINUX支持的管道机制、信号机制、消息队列通信机制及共享存储区机制。
5.1无名管道通信实验
【实验目的】
1、了解什么是管道
2、熟悉UNIX/LINUX支持的管道通信方式
【实验内容】
编写程序实现进程的管道通信。
用系统调用pipe()建立一管道,二个子进程P1和P2分别向管道各写一句话:
Child1issendingamessage!
Child2issendingamessage!
父进程从管道中读出二个来自子进程的信息并显示(要求先接收P1,后P2)。
参考程序
#include
#include
intpid1,pid2;
main()
{
intfd[2];
charoutpipe[100],inpipe[100];
pipe(fd); /*创建一个管道*/
while((pid1=fork())==-1);
if(pid1==0)
{
lockf(fd[1],1,0);
sprintf(outpipe,"child1processissendingmessage!
");
/*把串放入数组outpipe中*/
write(fd[1],outpipe,50); /*向管道写长为50字节的串*/
sleep(5); /*自我阻塞5秒*/
lockf(fd[1],0,0);
exit(0);
}
else
{
while((pid2=fork())==-1);
if(pid2==0)
{ lockf(fd[1],1,0); /*互斥*/
sprintf(outpipe,"child2processissendingmessage!
");
write(fd[1],outpipe,50);
sleep(5);
lockf(fd[1],0,0);
exit(0);
}
else
{ wait(NULL); /*同步*/
read(fd[0],inpipe,50); /*从管道中读长为50字节的串*/
printf("%s/n",inpipe);
wait(NULL);
read(fd[0],inpipe,50);
printf("%s/n",inpipe);
exit(0);
}
}
}
运行结果,延迟5秒后显示
child1processissendingmessage!
再延迟5秒
child2processissendingmessage!
5.2有名管道通信实验
【实验目的】
1、掌握有名管道的创建和读写方式
2、熟悉UNIX/LINUX支持的有名管道通信方式
【实验内容】
1.创建有名管道
2.本进程执行循环等待数据被写入到管道中并读有名管道
3.打开有名管道并写数据到名管道
参考代码:
//read_fifo.c
#include
#include
#include
#include
#include
#include
#include
#defineBUFFER_SIZE1024
intmain(intargc,char**argv)
{
intfd;
if(argc<2)
{
fprintf(stdout,"Usage:
%s\n",argv[0]);
exit
(1);
}
//intopen(constchar*path,intoflag,...);
if((fd=open(argv[1],O_RDONLY))<0)
{
fprintf(stderr,"openfifo%sforreadingfailed:
%s\n",argv[1],strerror(errno));
exit
(1);
}
fprintf(stdout,"openfifo%sforreadingsuccessed.\n",argv[0]);
charbuffer[BUFFER_SIZE];
ssize_tn;
while
(1)
{
again:
//ssize_tread(intfd,void*buf,size_tcount);
if((n=read(fd,buffer,BUFFER_SIZE))<0)
{
if(errno==EINTR)