OSTEP操作系统导论
目录
以下为操作系统课程实验对应教程。OSTEP
相关介绍
1、fork函数介绍
- fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。
- fork只拷贝下一个要执行的代码到新的进程。
- fork仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:
1)在父进程中,fork返回新创建子进程的进程ID;
2)在子进程中,fork返回0;
3)如果出现错误,fork返回一个负值;
- fork出错可能有两种原因:
1)当前的进程数已经达到了系统规定的上限,这时errno的值被设置为EAGAIN。
2)系统内存不足,这时errno的值被设置为ENOMEM。
- 创建新进程成功后,系统中出现两个基本完全相同的进程,这两个进程执行没有固定的先后顺序,哪个进程先执行要看系统的进程调度策略。
- 每个进程都有一个独特(互不相同)的进程标识符(process ID),可以通过getpid()函数获得,还有一个记录父进程pid的变量,可以通过getppid()函数获得变量的值。
创建一个新进程
新进程有自己的地址空间、寄存器和PC.
p1.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[]){
printf("hello world (pid:%d)\n", (int) getpid());
int rc = fork();
if (rc < 0) { // fork failed; exit
fprintf(stderr, "fork failed\n");
exit(1);
} else if (rc == 0) { // child (new process)
printf("hello, I am child (pid:%d)\n", (int) getpid());
} else { // parent goes down this path (main)
printf("hello, I am parent of %d (pid:%d)\n",
rc, (int) getpid());
}
return 0;
}
fork复制父进程的代码,这里count的值会是多少?(fork.c)
#include <unistd.h>
#include <stdio.h>
int main ()
{
pid_t fpid;
int count=0;
fpid=fork();
if (fpid < 0)
printf("error in fork!");
else if (fpid == 0) {
printf("i am the child process, my process id is %d/n",getpid());
count++;
}
else {
printf("i am the parent process, my process id is %d/n",getpid());
count++;
}
printf("统计结果是: %d/n",count);
return 0;
}
总共创建了多少进程(fork1.c)
#include <unistd.h>
#include <stdio.h>
int main(void)
{
int i=0;
printf("i son/pa ppid pid fpid/n");
//ppid指当前进程的父进程pid
//pid指当前进程的pid,
//fpid指fork返回给当前进程的值
for(i=0;i<2;i++){
pid_t fpid=fork();
if(fpid==0)
printf("%d child %4d %4d %4d/n",i,getppid(),getpid(),fpid);
else
printf("%d parent %4d %4d %4d/n",i,getppid(),getpid(),fpid);
}
return 0;
}
生成7个进程
输出多少个son,多少个father(fork2.c)
#include <unistd.h>
#include <stdio.h>
int main(void)
{
int i=0;
for(i=0;i<3;i++){
pid_t fpid=fork();
if(fpid==0)
printf("son/n");
else
printf("father/n");
}
return 0;
}
生成多少进程?
#include <stdio.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
fork();
fork() && fork() || fork();
fork();
return 0;
}
20个进程
2、系统调用wait()
该系统调用直到子进程运行完成才返回
p2.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char *argv[]){
printf("hello world (pid:%d)\n", (int) getpid());
int rc = fork();
if (rc < 0) { // fork failed; exit
fprintf(stderr, "fork failed\n");
exit(1);
} else if (rc == 0) { // child (new process)
printf("hello, I am child (pid:%d)\n", (int) getpid());
} else { // parent goes down this path (main)
int wc = wait(NULL);
printf("hello, I am parent of %d (wc:%d) (pid:%d)\n",
rc, wc, (int) getpid());
}
return 0;
}
int wait(int *status)函数
功能:父进程一旦调用了wait就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。
注:当父进程忘了用wait()函数等待已终止的子进程时,子进程就会进入一种无父进程的状态,此时子进程就是僵尸进程.
如果先终止父进程,子进程将继续正常进行,只是它将由init进程(PID 1)继承,当子进程终止时,init进程捕获这个状态.
如果调用进程没有子进程,调用就会失败,此时wait返回-1,同时errno被置为ECHILD。
wait()要与fork()配套出现,如果在使用fork()之前调用wait(),wait()的返回值则为-1,正常情况下wait()的返回值为子进程的PID.
参数status用来保存被收集进程退出时的一些状态
但如果不关心此进程死掉的原因,就可以设定这个参数为NULL,pid = wait(NULL);如果成功,wait会返回被收集的子进程的进程ID
3、系统调用exec()
运行一个不同于调用程序的程序
p3.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
int main(int argc, char *argv[]){
printf("hello world (pid:%d)\n", (int) getpid());
int rc = fork();
if (rc < 0) { // fork failed; exit
fprintf(stderr, "fork failed\n");
exit(1);
} else if (rc == 0) { // child (new process)
printf("hello, I am child (pid:%d)\n", (int) getpid());
char *myargs[3];
myargs[0] = strdup("wc"); // program: "wc" (word count)
myargs[1] = strdup("p3.c"); // argument: file to count
myargs[2] = NULL; // marks end of array
execvp(myargs[0], myargs); // runs word count
printf("this shouldn’t print out");
} else { // parent goes down this path (main)
int wc = wait(NULL);
printf("hello, I am parent of %d (wc:%d) (pid:%d)\n",
rc, wc, (int) getpid());
}
return 0;
}
exec替换进程映像
用exec函数可以把当前进程替换为一个新进程,且新进程与原进程有相同的PID。 exec名下是由多个关联函数组成的一个完整系列,包括execl、execlp、execle、execv、execvp
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
参数:
path参数表示你要启动程序的名称包括路径名
arg参数表示启动程序所带的参数,一般第一个参数为要执行命令名,不是带路径且arg必须以NULL结束
返回值:成功返回0,失败返回-1
4、exec系列
带l 的exec函数:execl,execlp,execle,表示后边的参数以可变参数的形式给出且都以一个空指针结束。
execl("/bin/ls","ls","-l",NULL);
带 p 的exec函数:execlp,execvp,表示第一个参数path不用输入完整路径,只有给出命令名即可,它会在环境变量PATH当中查找命令
不带 l 的exec函数:execv,execvp表示命令所需的参数以char *arg[]形式给出且arg最后一个元素必须是NULL
char *argv[] = {"ls","-l",NULL};
ret = execvp("ls",argv);
增加重定向
p4.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/wait.h>
int main(int argc, char *argv[]){
int rc = fork();
if (rc < 0) { // fork failed; exit
fprintf(stderr, "fork failed\n");
exit(1);
} else if (rc == 0) { // child: redirect standard output to a file
close(STDOUT_FILENO);
open("./p4.output", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU);
// now exec "wc"...
char *myargs[3];
myargs[0] = strdup("wc"); // program: "wc" (word count)
myargs[1] = strdup("p4.c"); // argument: file to count
myargs[2] = NULL; // marks end of array
execvp(myargs[0], myargs); // runs word count
} else { // parent goes down this path (main)
int wc = wait(NULL);
}
return 0;
}
一、实验项目名称
进程API
二、实验目的
1、利用fork()生成如下图所示的进程树。
2、使用fork()编写一个程序,子进程打印“hello”,父进程打印“goodbye”,使用wait()确保子进程先打印。
3、实现一个输入重定向程序。
三、实验步骤
1、需要通过if语句判断,在父进程中生成三个子进程。在一个子进程里通过if判断生成两个子进程。Centos中避免出现孤儿进程,在进程a和b后添加sleep(1)。下面加粗部分为添加代码。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<stdbool.h>
#include<sys/types.h>
int main(int argc, char *argv) {
int pida,pidb,pidc,pidd,pide;
pida=fork();
if(pida<0){
printf("pida failed!");
exit(-1);
}
else if(pida==0)
{
printf("a %d %d\n",getpid(),getppid());
return 0;
}
else{
pidb=fork();
if(pidb<0){
printf("pidb failed!");
exit(-1);
}else if(pidb==0)
{
printf("b %d %d\n",getpid(),getppid());
pidd = fork();
if(pidd < 0){
printf("pidd failed!");
exit(-1);
}else if(pidd==0){
printf("d %d %d\n", getpid(), getppid());
return 0;
}else{
pide = fork();
if(pide < 0){
printf("pide failed!");
exit(-1);
}else if(pide == 0){
printf("e %d %d\n", getpid(), getppid());
return 0;
}
}
sleep(1);
return 0;
}
else{
pidc=fork();
if(pidc<0){
printf("pidc failed!");
exit(-1);
}
else if(pidc==0)
{
printf("c %d %d\n",getpid(),getppid());
return 0;
}
sleep(1);
}
}
return 0;
}
2、
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
int main(int argc, char *argv) {
int pid = fork();
if(pid < 0){
printf("pid failed!");
exit(-1);
}else if(pid == 0){
printf("Hello from child pid ( %d )\n",getpid());
return 0;
}else {
int wc = wait(NULL);
printf("Goodbye from parent pid ( %d )\n", getpid());
}
return 0;
3、
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<string.h>
#include<stdlib.h>
int main(int argc, char *argv) {
int pid = fork();
if(pid < 0){
printf("pid failed!");
exit(-1);
}else if(pid == 0){
close(STDOUT_FILENO);
open("./test.txt",O_CREAT | O_WRONLY | O_TRUNC,S_IRWXU);
char *testargs[3];
testargs[0] = strdup("ls");
testargs[1] = strdup("-l");
testargs[2] = NULL;
execvp(testargs[0], testargs);
}else {
int wc = wait(NULL);
}
return 0;
}