兰州大学操作系统实验五进程管理题目和答案.docx
- 文档编号:7040968
- 上传时间:2023-01-16
- 格式:DOCX
- 页数:37
- 大小:958.93KB
兰州大学操作系统实验五进程管理题目和答案.docx
《兰州大学操作系统实验五进程管理题目和答案.docx》由会员分享,可在线阅读,更多相关《兰州大学操作系统实验五进程管理题目和答案.docx(37页珍藏版)》请在冰豆网上搜索。
兰州大学操作系统实验五进程管理题目和答案
实验五
实验五
实验名称:
进程管理
实验目的:
1.进一步学习进程的属性
2.学习进程管理的系统调用
3.掌握使用系统调用获取进程的属性、创建进程、实现进程控制等
4.掌握进程管理的基本原理
实验时间pcb
6学时
预备知识:
1.进程属性
1.1getpid(取得进程ID)
表头文件#include
定义函数pid_tgetpid(void);
函数说明getpid()用来取得目前进程的进程ID,许多程序利用取到的此值来建立临时文件,以避免临时文件相同带来的问题。
返回值目前进程的进程ID
范例
1.2getppid(取得父进程的进程ID)
表头文件#include
定义函数pid_tgetppid(void);
函数说明getppid()用来取得目前进程的父进程ID。
返回值目前进程的父进程ID。
1.3getegid(取得有效的组ID)/etc/shadow存放用户口令信息,主人是root
表头文件#include
#include
定义函数gid_tgetegid(void);ls-l/usr/bin/passwd
函数说明getegid()用来取得执行目前进程有效(effective)组ID。
有效的组ID用来决定进程执行时组的权限。
返回值返回有效的组ID。
Group组
1.4geteuid(取得有效的用户ID)
表头文件#include
#include
定义函数uid_tgeteuid(void)
函数说明geteuid()用来取得执行目前进程有效的用户ID。
有效的用户ID用来决定进程执行的权限,借由此改变此值,进程可以获得额外的权限。
倘若执行文件的setID位已被设置,该文件执行时,其进程的euid值便会设成该文件所有者的uid。
返回值返回有效的用户ID。
1.5getgid(取得真实的组ID)
表头文件#include
#include
定义函数gid_tgetgid(void);
函数说明getgid()用来取得执行目前进程的组ID。
返回值返回组ID
1.6getuid(取得真实的用户ID)
表头文件#include
#include
定义函数uid_tgetuid(void);
函数说明getuid()用来取得执行目前进程的用户ID。
返回值用户ID
1.7times(取得进程相关的时间)断异常
表头文件#include
定义函数clock_ttimes(structtms*buf);
函数说明取得进程运行相关的时间。
参数说明
/*sys/times.h*/
structtms{
clock_ttms_utime;/*进程花在执行用户模式代码上的时间*/<=几十毫秒
clock_ttms_stime;/*进程花在执行内核代码上的时间*/<=几十毫秒
clock_ttms_cutime;/*子进程花在执行用户模式代码上的时间*/
clock_ttms_cstime;/*子进程花在执行内核代码上的时间*/
}
返回值自系统自举后经过的时钟嘀嗒数。
注意时钟嘀嗒数time转换为用户可读的方式,即多少秒,需通过如下方式:
(float)time/sysconf(_SC_CLK_TCK);sysconf:
宏代表一常量
2.进程创建
2.1system(执行shell命令)通过执行命令产生一个进程
表头文件#include
定义函数intsystem(constchar*string);mansh-c(不覆盖现有的文件“>”。
)
函数说明system()会调用fork()产生子进程,由子进程来调用/bin/sh-cstring来执行参数string字符串所代表的命令,此命令执行完后随即返回原调用的进程。
返回值如果system()在调用/bin/sh时失败则返回127,其他失败原因返回-1。
若参数string为空指针(NULL),则返回非零值。
2.2fork(建立一个新的进程)创建一个新的与父进程一模一样的子进程
表头文件#include
定义函数pid_tfork(void);
函数说明fork()会产生一个新的子进程,其子进程会复制父进程的数据与堆栈空间,并继承父进程的用户代码,组代码,环境变量、已打开的文件代码、工作目录和资源限制等。
Linux使用copy-on-write(COW)技术,只有当其中一进程试图修改欲复制的空间时才会做真正的复制动作,由于这些继承的信息是复制而来,并非指相同的内存空间,因此子进程对这些变量的修改和父进程并不会同步。
此外,子进程不会继承父进程的文件锁定和未处理的信号。
注意Linux不保证子进程会比父进程先执行或晚执行,因此编写程序时要留意死锁或竞争条件的发生。
返回值如果fork()成功则在父进程会返回新建立的子进程代码(PID),而在新建立的子进程中则返回0。
如果fork失败则直接返回-1,失败原因存于errno中。
错误代码EAGAIN内存不足。
ENOMEM内存不足,无法配置核心所需的数据结构空间。
exec函数簇
表头文件#include
2.3.1execl(执行文件)
定义函数intexecl(constchar*path,constchar*arg,....);
函数说明execl()用来执行参数path字符串所代表的文件路径,接下来的参数代表执行该文件时传递过去的argv(0)、argv[1]……,最后一个参数必须用空指针(NULL)作结束。
返回值如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中。
范例#include
main()
{
printf(“BEFORE”);
execl(“/bin/ls”,”ls”,”-al”,”/etc/passwd”,(char*)0);
printf(“AFTER”);//这里的东西被前面覆盖了,被回收了。
}
2.3.2execlp(从PATH环境变量中查找文件并执行)
定义函数intexeclp(constchar*file,constchar*arg,……);
函数说明execlp()会从PATH环境变量所指的目录中查找符合参数file的文件名,找到后便执行该文件,然后将第二个以后的参数当做该文件的argv[0]、argv[1]……,最后一个参数必须用空指针(NULL)作结束。
返回值如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中。
错误代码参考execve()。
范例/*执行ls-al/etc/passwdexeclp()会依PATH变量中的/bin找到/bin/ls*/
#include
main()
{
execlp(“ls”,”ls”,”-al”,”/etc/passwd”,(char*)0);
}
2.3.3execv(执行文件)
定义函数intexecv(constchar*path,char*constargv[]);
函数说明execv()用来执行参数path字符串所代表的文件路径,与execl()不同的地方在于execve()只需两个参数,第二个参数利用数组指针来传递给执行文件。
返回值如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中。
范例/*执行/bin/ls-al/etc/passwd*/
#include
main()
{
char*argv[]={“ls”,”-al”,”/etc/passwd”,(char*)0};
execv(“/bin/ls”,argv);
}
2.3.4execve(执行文件)
定义函数intexecve(constchar*filename,char*constargv[],char*constenvp[]);
函数说明execve()用来执行参数filename字符串所代表的文件路径,第二个参数系利用数组指针来传递给执行文件,最后一个参数则为传递给执行文件的新环境变量数组。
返回值如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中。
错误代码EACCES
1.欲执行的文件不具有用户可执行的权限。
2.欲执行的文件所属的文件系统是以noexec方式挂上。
3.欲执行的文件或script翻译器非一般文件。
EPERM
1.进程处于被追踪模式,执行者并不具有root权限,欲执行的文件具有SUID或SGID位。
2.欲执行的文件所属的文件系统是以nosuid方式挂上,欲执行的文件具有SUID或SGID位元,但执行者并不具有root权限。
E2BIG参数数组过大
ENOEXEC无法判断欲执行文件的执行文件格式,有可能是格式错误或无法在此平台执行。
EFAULT参数filename所指的字符串地址超出可存取空间范围。
ENAMETOOLONG参数filename所指的字符串太长。
ENOENT参数filename字符串所指定的文件不存在。
ENOMEM核心内存不足
ENOTDIR参数filename字符串所包含的目录路径并非有效目录
EACCES参数filename字符串所包含的目录路径无法存取,权限不足
ELOOP过多的符号连接
ETXTBUSY欲执行的文件已被其他进程打开而且正把数据写入该文件中
EIOI/O存取错误
ENFILE已达到系统所允许的打开文件总数。
EMFILE已达到系统所允许单一进程所能打开的文件总数。
EINVAL欲执行文件的ELF执行格式不只一个PT_INTERP节区
EISDIRELF翻译器为一目录
ELIBBADELF翻译器有问题。
范例#include
main()
{
char*argv[]={“ls”,”-al”,”/etc/passwd”,(char*)0};
char*envp[]={“PATH=/bin”,0}
execve(“/bin/ls”,argv,envp);
}
2.3.5execvp(执行文件)
定义函数intexecvp(constchar*file,char*constargv[]);
函数说明execvp()会从PATH环境变量所指的目录中查找符合参数file的文件名,找到后便执行该文件,然后将第二个参数argv传给该欲执行的文件。
返回值如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中。
范例/*请与execlp()范例对照*/
#include
main()
{
char*argv[]={“ls”,”-al”,”/etc/passwd”,0};
execvp(“ls”,argv);
}
3.进程等待find查找文件group查找字符串
3.1wait(等待子进程中断或结束)
表头文件#include
#include
定义函数pid_twait(int*status);
函数说明wait()会暂时停止目前进程的执行,直到有信号来到或子进程结束。
如果在调用wait()时子进程已经结束,则wait()会立即返回子进程结束状态值。
子进程的结束状态值会由参数status返回,而子进程的进程ID也会一快返回。
如果不在意结束状态值,则参数status可以设成NULL。
子进程的结束状态值请参考waitpid()。
返回值如果执行成功则返回子进程ID(PID),如果有错误发生则返回-1。
失败原因存于errno中。
附加说明
范例#include
#include
#include
#include
main()
{
pid_tpid;
intstatus,i;
if(fork()==0){//子进程代码
printf(“Thisisthechildprocess.pid=%d\n”,getpid());
exit(5);
}else{//父进程代码
sleep
(1);
printf(“Thisistheparentprocess,waitforchild...\n”;
pid=wait(&status);//等待
i=WEXITSTATUS(status);//唤醒遗言放在status中
printf(“child’spid=%d.exitstatus=^d\n”,pid,i);
}
}
3.2waitpid(等待子进程中断或结束)
表头文件#include
#include
定义函数pid_twaitpid(pid_tpid,int*status,intoptions);
函数说明waitpid()会暂时停止目前进程的执行,直到有信号来到或子进程结束。
如果在调用wait()时子进程已经结束,则wait()会立即返回子进程结束状态值。
子进程的结束状态值会由参数status返回,而子进程的进程ID也会一快返回。
如果不在意结束状态值,则参数status可以设成NULL。
参数pid为欲等待的子进程ID,其他数值意义如下:
pid<-1等待进程组ID为pid绝对值的任何子进程。
pid=-1等待任何子进程,相当于wait()。
pid=0等待进程组ID与目前进程相同的任何子进程。
pid>0等待任何子进程ID为pid的子进程。
参数option可以为0或下面的OR组合:
WNOHANG如果没有任何已经结束的子进程则马上返回,不予以等待。
WUNTRACED如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会。
子进程的结束状态返回后存于status,底下有几个宏可判别结束情况
WIFEXITED(status)如果子进程正常结束则为非0值。
WEXITSTATUS(status)取得子进程exit()返回的结束代码,一般会先用WIFEXITED来判断是否正常结束才能使用此宏。
WIFSIGNALED(status)如果子进程是因为信号而结束则此宏值为真
WTERMSIG(status)取得子进程因信号而中止的信号代码,一般会先用WIFSIGNALED来判断后才使用此宏。
WIFSTOPPED(status)如果子进程处于暂停执行情况则此宏值为真。
一般只有使用WUNTRACED时才会有此情况。
WSTOPSIG(status)取得引发子进程暂停的信号代码,一般会先用WIFSTOPPED来判断后才使用此宏。
返回值如果执行成功则返回子进程ID(PID),如果有错误发生则返回-1。
失败原因存于errno中。
4.信号
4.1kill(传送信号给指定的进程)
表头文件#include
#include
定义函数intkill(pid_tpid,intsig);
函数说明kill()可以用来送参数sig指定的信号给参数pid指定的进程。
参数pid有几种情况:
pid>0将信号传给进程ID为pid的进程。
pid=0将信号传给和目前进程相同进程组的所有进程
pid=-1将信号广播传送给系统内所有的进程
pid<0将信号传给进程组ID为pid绝对值的所有进程
返回值执行成功则返回0,如果有错误则返回-1。
错误代码EINVAL参数sig不合法
ESRCH参数pid所指定的进程或进程组不存在
EPERM权限不够无法传送信号给指定进程
范例#include
#include
#include
#include
main()
{
pid_tpid;
intstatus;
if(!
(pid=fork())){
printf(“HiIamchildprocess!
\n”);
sleep(10);
return;
}
else{
printf(“sendsignaltochildprocess(%d)\n”,pid);
sleep
(1);
kill(pid,SIGABRT);
wait(&status);
if(WIFSIGNALED(status))
printf(“chileprocessreceivesignal%d\n”,WTERMSIG(status));
}
}
4.2signal(设置信号处理方式)
表头文件#include
定义函数void(*signal(intsignum,void(*handler)(int)));
函数说明signal()会依参数signum指定的信号编号来设置该信号的处理函数。
当指定的信号到达时就会跳转到参数handler指定的函数执行。
如果参数handler不是函数指针,则必须是下列两个常数之一:
SIG_IGN忽略参数signum指定的信号。
SIG_DFL将参数signum指定的信号重设为核心预设的信号处理方式。
返回值返回先前的信号处理函数指针,如果有错误则返回SIG_ERR(-1)。
附加说明在信号发生跳转到自定的handler处理函数执行后,系统会自动将此处理函数换回原来系统预设的处理方式,如果要改变此操作请改用sigaction()。
5.进程调度
5.1getpriority(取得程序进程执行优先权)
表头文件#include
#include
定义函数intgetpriority(intwhich,intwho);
函数说明getpriority()可用来取得进程、进程组和用户的进程执行优先权。
参数which有三种数值,参数who则依which值有不同定义
whichwho代表的意义
PRIO_PROCESSwho为进程ID
PRIO_PGRPwho为进程的组ID
PRIO_USERwho为用户ID
此函数返回的数值介于-20至20之间,代表进程执行优先权,数值越低代表有较高的优先次序,执行会较频繁。
返回值返回进程执行优先权,如有错误发生返回值则为-1且错误原因存于errno。
附加说明由于返回值有可能是-1,因此要同时检查errno是否存有错误原因。
最好在调用次函数前先清除errno变量。
错误代码ESRCH参数which或who可能有错,而找不到符合的进程。
EINVAL参数which值错误。
5.2setpriority(设置程序进程执行优先权)
表头文件#include
#include
定义函数intsetpriority(intwhich,intwho,intprio);
函数说明setpriority()可用来设置进程、进程组和用户的进程执行优先权。
参数which有三种数值,参数who则依which值有不同定义
参数prio介于-20至20之间。
代表进程执行优先权,数值越低代表有较高的优先次序,执行会较频繁。
此优先权默认是0,而只有超级用户(root)允许降低此值。
返回值执行成功则返回0,如果有错误发生返回值则为-1,错误原因存于errno。
ESRCH参数which或who可能有错,而找不到符合的进程
EINVAL参数which值错误。
EPERM权限不够,无法完成设置
EACCES一般用户无法降低优先权
5.3nice(改变进程优先顺序)
表头文件#include
定义函数intnice(intinc);
函数说明nice()用来改变进程的进程执行优先顺序。
参数inc数值越大则优先顺序排在越后面,即表示进程执行会越慢。
只有超级用户才能使用负的inc值,代表优先顺序排在前面,进程执行会较快。
返回值如果执行成功则返回0,否则返回-1,失败原因存于errno中。
错误代码EPERM一般用户企图转用负的参数inc值改变进程优先顺序。
6.其他
6.1atexit(设置程序正常结束前调用的函数)
表头文件#include
定义函数intatexit(void(*function)(void));
函数说明atexit()用来设置一个程序正常结束前调用的函数。
当程序通过调用exit()或从main中返回时,参数function所指定的函数会先被调用,然后才真正由exit()结束程序。
返回值如果执行成功则返回0,否则返回-1,失败原因存于errno中。
范例#include
voidmy_exit(void)
{
printf(“beforeexit()!
\n”);
}
main()
{
atexit(my_exit);
exit(0);
}
6.2exit(正常结束进程)
表头文件#include
定义函数voidexit(intstatus);
函数说明exit()用来正常终结目前进程的执行,并把参数status返回给父进程,而进程所有的缓冲区数据会自动写回并关闭未关闭的文件。
实验要求:
1.编写一个程序,打印进程的如下信息:
进程标识符,父进程标识符,真实用户ID,有效用户ID,真实用户组ID,有效用户组ID。
并分析真实用户ID和有效用户ID的区别。
2.阅读如下程序:
/*processusingtime*/
#include
#include
#include
#include
#include
voidtime_print(char*,clock_t);
intmain(void)
{
clock_tstart,end;
structtmst_start,t_end;
start=times(&t_start);
system(“grepthe/usr/doc/*/*>/dev/null2>/dev/nu
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 兰州大学 操作系统 实验 进程 管理 题目 答案