进程API-操作系统实验OSTEP

本文详细介绍了操作系统中的fork函数,包括其工作原理、返回值以及可能出现的错误。接着讨论了wait系统调用,用于父进程等待子进程结束,并解释了其参数和功能。此外,还探讨了exec系列函数,用于执行新的程序,并替换当前进程映像。最后,提到了实验项目,包括创建进程树、控制进程执行顺序和实现输入重定向。
摘要由CSDN通过智能技术生成

OSTEP操作系统导论

目录

相关介绍

1、fork函数介绍

2、系统调用wait()

int wait(int *status)函数

3、系统调用exec()

4、exec系列

一、实验项目名称

二、实验目的

三、实验步骤


以下为操作系统课程实验对应教程。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;
}

 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值