《Operating Systems:Three Easy Pieces》第五章课后习题解答

过程有点曲折。写最后题的时候发现前面的这章内容其实没有全懂。果然实践的时候才能暴露不足阿。而且最后一题被网上的一份题解坑的不惨。不过最后也是学到了新东西。写博客的时候发现共享文件夹莫名其妙坏了。修了1小时也没修好。然后转用之前一个软件XTFP调了一会儿。

1.编写一个调用 fork()的程序。在调用 fork()之前,让主进程访问一个变量(例如 x) 并将其值设置为某个值(例如 100)。子进程中的变量有什么值?当子进程和父进程都改变 x 的值时,变量会发生什么?

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

int main(int argc,char *argv[]){
	int x=100;
	int rc=fork();
	if(rc<0){
		printf("error");
		exit(1);
	}
	else if(rc==0){///child
	   x=-1;	
	   printf("child's x=%d\n",x);
	}
	else{//father
	   x=10; 
	   printf("father's x=%d\n",x);
	}
return 0;
}

返回结果两者都是100.修改的话就是各自变成各自的x。

子进程在一定程度上拷贝了父进程。之后存的就是私有内存。

2.编写一个打开文件的程序(使用 open()系统调用),然后调用 fork()创建一个新进程。 子进程和父进程都可以访问 open()返回的文件描述符吗?当它们并发(即同时)写入文件时, 会发生什么?

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<string.h>
int main(int argc,char *argv[]){
 	int fd=open("./issue2.output", O_CREAT|O_WRONLY|O_TRUNC,S_IRWXU);	
	int rc=fork();
	if(rc<0){
	     printf("error");
	     exit(1);
	}
	else if(rc==0){///child
	      printf("child's %d \n",fd);
	      char buf[] ="I am child\n";
	      write(fd,buf,strlen(buf));
	}
	else{//father
              printf("father's %d \n",fd);
	      char buf[]="I am father\n";
	      write(fd,buf,strlen(buf));
	}
return 0;
}											

都可以。我这边显示的是先父亲再儿子。

3.使用 fork()编写操一个程序。子进程应打印“hello”,父进程应打印“goodbye”。你 应该尝试确保子进程始终先打印。你能否不在父进程调用 wait()而做到这一点呢?

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<string.h>
#include<sys/wait.h>
int main(int argc,char *argv[]){
	int rc=fork();
	if(rc<0){
	     printf("error");
	     exit(1);
	}
	else if(rc==0){///child
	      printf("hello\n");
	    
	}
	else{//father
	     // wait(NULL);
  	      sleep(1);
	      printf("goodbye\n");
	}
return 0;
}															

我是尝试用sleep()

4.编写一个调用 fork()的程序,然后调用某种形式的 exec()来运行程序/bin/ls。看看是 否可以尝试 exec()的所有变体,包括 execl()、execle()、execlp()、execv()、execvp()和 execvP()。进程 API 为什么同样的基本调用会有这么多变种?

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<string.h>
#include<sys/wait.h>
int main(int argc,char *argv[]){
	int rc=fork();
	if(rc<0){
	     printf("error");
	     exit(1);
	}
	else if(rc==0){///child
	    printf("This is child\n");
	    execl("/bin/ls","ls","-l",NULL);
	    
	}
	else{//father
	    waitpid(rc,NULL,0);
   	    char* v[]={"ls","-l",NULL};
	    printf("This is father\n");
	    execve("/bin/ls",v,NULL);
	    execv("/bin/ls",v);
	    execvp("/bin/ls",v);
	}
return 0;
}					

函数的用法参数列表可以在RTFM中找。

所有的exec变体都可以运行程序/bin/ls。execve()是基础的系统调用,其他的变种都是在这个基础上包装的库函数。因为要应对各种不同的需求,所以才衍生出了这么多的形式。

比如“l”表示参数以列表的形式表示,“v”表示参数以数组的形式表示,“p”表示在PATH中搜索执行文件,“e”表示可附加环境参数。

5.现在编写一个程序,在父进程中使用 wait(),等待子进程完成。wait()返回什么?如 果你在子进程中使用 wait()会发生什么

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<string.h>
#include<sys/wait.h>
int main(int argc,char *argv[]){
	int rc=fork();
	if(rc<0){
	     printf("error");
	     exit(1);
	}
	else if(rc==0){///child
	     int res=wait(NULL);
  	     printf("child %d %d \n",res,(int)getpid()); 
	}
	else{//father
///	     int res= wait(NULL);
///	     printf("%d %d\n",res,(int)getpid());
	     printf("father %d\n",(int)getpid());       
	}
return 0;
}					

父进程等待子进程完成,返回子进程的pid

子进程用wait等待返回的是-1.

说明了wait对应的实际上是父子关系。(man wait可以找到)[这在最后一题很重要,虽然那一份题解输出是对的,但是实际上是错的。我本地输出和朋友输出两份代码输出的是不一样的】

All of these system calls are used to wait for state changes in a child of the calling process, and obtain information about the child whose state has changed. A state change is considered to be: the child terminated; the child was stopped by a signal; or the child was resumed by a signal. In the case of a terminated child, performing a wait allows the system to release the resources associated with the child; if a wait is not performed, then the terminated child remains in a "zombie" state (see NOTES below).

If a child has already changed state, then these calls return immediately. Otherwise they block until either a child changes state or a signal handler interrupts the call (assuming that system calls are not automatically restarted using the SA_RESTART flag of sigaction(2)). In the remainder of this page, a child whose state has changed and which has not yet been waited upon by one of these system calls is termed waitable.
 

6.对前一个程序稍作修改,这一使用 waitpid()而不是 wait()。什么时候 waitpid()会 有用?

#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<string.h>
#include<sys/wait.h>
int main(int argc,char *argv[]){
        int rc=fork();
        if(rc<0){
             printf("error");
             exit(1);
        }
        else if(rc==0){///child
           ///  int res=waitpid(NULL);
             printf("child %d \n",(int)getpid());
        }
        else{//father
             int res= waitpid(-1,NULL,0);
             printf("father %d %d\n",(int)getpid(),res);       
        }
return 0;
}                                                               
								

由pid的参数决定,waitpid( pid_t, int* status,int options)

通常后两者取NULL,0

pid_t取要等待结束的pid

7.编写一个创建子进程的程序,然后在子进程中关闭标准输出(STDOUT_FILENO)。 如果子进程在关闭描述符后调用 printf()打印输出,会发生什么?

#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<string.h>
#include<sys/wait.h>
int main(int argc,char *argv[]){
        int rc=fork();
        if(rc<0){
             printf("error");
             exit(1);
        }
        else if(rc==0){///child
             close(STDOUT_FILENO); 
             printf("child  %d \n",(int)getpid());
        }
        else{//father
///          int res= wait(NULL);
///          printf("%d %d\n",res,(int)getpid());
             printf("father %d\n",(int)getpid());       
        }
return 0;
}                                                               
																						

我的测试是只会输出父程序的printf到屏幕上

8.编写一个程序,创建两个子进程,并使用 pipe()系统调用,将一个子进程的标准输 出连接到操一个子进程的标准输入。

#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<string.h>
#include<sys/wait.h>
int main(int argc,char *argv[]){
	int result=-1;
	int fd[2]={0,0};
	result=pipe(fd);
	if(0!=result){
		printf("fail to create pipe\n");
		return 0;
	}
	int p1=fork();
	if(-1==p1){
		printf("fail to fork1\n");
		exit(-1);
	}
	else if(0==p1){
		close(fd[0]); //close read
		char* str="Hello";
		write(fd[1],str,strlen(str));
		close(fd[1]); //close write
		printf("I am P1. I has sent! \n");
		return 0;
	}
	waitpid(p1,NULL,0);
	int p2=fork();
/*	if(0!=p2){
		printf("fail to fork2 %d \n",p2);
		///father also touch (more infomation --P29)
	}*/
	if(-1==p2){
		printf("fail to fork2 \n");
	}
	else if(0==p2){
		int errno=0;
		waitpid(p1,NULL,0); //wati until pid1 ok
	//	perror("wait error?: ");
		printf("Now it's p2\n");
		close(fd[1]);
		char buf[1024];
		int len=read(fd[0],buf,1024);
		if(len>0){
		    buf[len]='\0';
		    printf("I am P2.I has revecived %s\n",buf);
		}
		printf("???\n");
		return 0;
	}

	waitpid(p1,NULL,0);
	waitpid(p2,NULL,0);

	printf("I am father,exerything is over!\n");
	
	
return 0;
}                                                               
								

这个题让我理清了之前没有懂的一些东西。

1.waitpid()父子进程之间的,兄弟进程之间是无效的。所以你用perpor测waitpid的返回值会返回无子进程。所以我的当时本地输出是"fuck"先出来的。

2.对于fork,两个进程会同时从这里返回,但是主进程返回儿子进程的pid,儿子进程返回0

3.对于pipe的读写,及时关闭不用的一端,写完也及时关。因为不保证原子操作。其他进程可能也会用到导致意外错误

4.之前gdb调试都是反汇编,一下子不知道对于程序怎么处理。主要还是一个原始代码怎么在gdb中显示出来。

 GDB 可以打印出所调试程序的源代码,当然,在程序编译时一定要加上-g的参数,把源程序信息编译到执行文件中。不然就看不到源程序了。当程序停下来以后, GDB会报告程序停在了那个文件的第几行上。你可以用list命令来打印程序的源代码。

gcc -c issue8.c -g
gcc -o issue88.out issue8.o   (中间的是filename,自己任意)

gdb issue88.out (gdb一个可执行文件)

输入l就可以看到代码行

关于list的用法

一般来说在list后面可以跟以下这们的参数:
<linenum>   行号。
<+offset>   当前行号的正偏移量。
<-offset>   当前行号的负偏移量。
<filename:linenum>  哪个文件的哪一行。
<function>  函数名。显示函数名为function的函数的源程序。
<filename:function> 哪个文件中的哪个函数。
<*address>  程序运行时的语句在内存中的地址。
list <first>, <last> 显示从first行到last行之间的源代码。

一般是打印当前行的上5行和下5行,如果显示函数是是上2行下8行,默认是10行,当然,你也可以定制显示的范围,使用下面命令可以设置一次显示源程序的行数。

set listsize <count>
设置一次显示源代码的行数。



  • 10
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值