第5章 插叙:进程API

Homework(Simulation):

        This simulation homework focuses on fork.py, a simple process creation simulator that shows how processes are related in a single “familial” tree. Read the relevant README for details about how to run the simulator.

Questions:

1. Run ./fork.py -s 10 and see which actions are taken. Can you predict what the process tree looks like at each step? Use the -c flag to check your answers. Try some different random seeds (-s) or add more actions (-a) to get the hang of it.

python fork.py -s 10 -c

python fork.py -s 10 -A a+b,b+c,c-,a+d -c

2. One control the simulator gives you is the fork percentage, controlled by the -f flag. The higher it is, the more likely the next action is a fork; the lower it is, the more likely the action is an exit. Run the simulator with a large number of actions (e.g., -a 100) and vary the fork percentage from 0.1 to 0.9. What do you think the resulting final process trees will look like as the percentage changes? Check your answer with -c.

python fork.py -s 10 -f 0.1 -a 10 -c

python fork.py -s 10 -f 0.9 -a 10 -c

3. Now, switch the output by using the -t flag (e.g., run ./fork.py -t). Given a set of process trees, can you tell which actions were taken?

python3 fork.py -t -c

4. One interesting thing to note is what happens when a child exits; what happens to its children in the process tree? To study this, let’s create a specific example: ./fork.py -A a+b,b+c,c+d,c+e,c-. This example has process ’a’ create ’b’, which in turn creates ’c’, which then creates ’d’ and ’e’. However, then, ’c’ exits. What do you think the process tree should like after the exit? What if you use the -R flag? Learn more about what happens to orphaned processes on your own to add more context.

没有-R:d,e成为a的子进程

python fork.py -A a+b,b+c,c+d,c+e,c- -c

有-R:d,e成为b的子进程

python fork.py -A a+b,b+c,c+d,c+e,c- -R -c

5. One last flag to explore is the -F flag, which skips intermediate steps and only asks to fill in the final process tree. Run ./fork.py -F and see if you can write down the final tree by looking at the series of actions generated. Use different random seeds to try this a few times.

python fork.py -F -c

6. Finally, use both -t and -F together. This shows the final process tree, but then asks you to fill in the actions that took place. By looking at the tree, can you determine the exact actions that took place? In which cases can you tell? In which can’t you tell? Try some different random seeds to delve into this question

python fork.py -t -F -c

Homework(Code):

        In this homework, you are to gain some familiarity with the process management APIs about which you just read. Don’t worry – it’s even more fun than it sounds! You’ll in general be much better off if you find as much time as you can to write some code, so why not start now?

Questions:

1. Write a program that calls fork(). Before calling fork(), have the main process access a variable (e.g., x) and set its value to something (e.g., 100). What value is the variable in the child process? What happens to the variable when both the child and parent change the value of x?

说明父进程和子进程拥有相同但是独立的地址空间

父进程和子进程对x所做的任何改变都是独立的

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

int main(void)
{
	int x = 1;

	printf("x is: %d\n", x);

	x = 100;

	int rc = fork();

	if (rc == 0)
	{
		printf("Hello, i am child (pid: %d)\n", getpid());
		printf("old x is %d\n", x);
		x++;
		printf("now x is %d\n", x);
	}
	else
	{
		printf("Hello, i am parent of %d (pid: %d)\n", rc, getpid());
		printf("old x is %d\n", x);
		x--;
		printf("now x is %d\n", x);
	}

	return 0;
}

2. Write a program that opens a file (with the open() system call) and then calls fork() to create a new process. Can both the child and parent access the file descriptor returned by open()? What happens when they are writing to the file concurrently, i.e., at the same time?

子进程和父进程共享文件,父进程打开了文件a.txt而子进程继承了这个文件,因此也可以写入

假设父进程先运行的话,子进程的写入不会覆盖父进程

子进程先运行,父进程的写入同样也不会覆盖子进程

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

int main(void)
{
	int fd = open("a.txt", O_RDWR, 2);
	int rc = fork();

	if (rc == 0)
	{
		printf("Hello! i am child (pid: %d)\n", getpid());
		char* str1 = "de";
		write(fd, str1, 2);
	}
	else
	{
		printf("Hello! i am parent of %d (pid: %d)\n", rc, getpid());
		char* str2 = "fgh";
		write(fd, str2, 3);
	}

	return 0;
}

3. Write another program using fork(). The child process should print “hello”; the parent process should print “goodbye”. You should try to ensure that the child process always prints first; can you do this without calling wait() in the parent?

vfork() :与 fork() 一样创建一个子进程,但保证子进程先运行,只有子进程调用 exec 或 exit 后,                 父进程才会被调用

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

int main(void)
{
	int rc = vfork();

	if (rc == 0)
	{
		printf("Hello! i am child (pid: %d)\n", getpid());
		exit(0);
	}
	else
	{
		printf("Goodbye! i am parent of %d (pid: %d)\n", rc, getpid());
	}

	return 0;
}

4. Write a program that calls fork() and then calls some form of exec() to run the program /bin/ls. See if you can try all of the variants of exec(), including (on Linux) execl(), execle(), execlp(), execv(), execvp(), and execvpe(). Why do you think there are so many variants of the same basic call?

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

int main(void)
{
	printf("hello world (pid:%d)\n",(int)getpid());
	int rc = fork();
	if(rc < 0)
	{
		fprintf(stderr,"fork failed\n");
		exit(1);
	}else if(rc == 0)
	{
		printf("hello, I am child (pid:%d)\n",(int)getpid());
		char *path = "/bin/ls";
		char* myargs[3] = {"ls","-l",NULL};
		char* env[] = {"PATH=/bin:/bin/usr",NULL};
		execl("/bin/ls","ls","-l",NULL);
		execv(path,myargs);
		execlp("ls","ls","-l",NULL);
		execvp(myargs[0],myargs);
		execle("/bin/ls","ls","-l",NULL,env);
		execvpe(myargs[0],myargs,env);
		printf("This shouldn't print out\n");
	}else
	{
		int rc_wait = wait(NULL);
		printf("hello, I am parent of %d (rc_wait:%d) (pid:%d)\n",
						rc,rc_wait,(int)getpid());
	}
		
	exit(0);
}

5. Now write a program that uses wait() to wait for the child process to finish in the parent. What does wait() return? What happens if you use wait() in the child?

父进程调用wait()

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>

int main(void)
{
	int rc = fork();

	if (rc == 0)
	{
		printf("Hello! i am child (pid: %d)\n", getpid());
	}
	else
	{
		int wc = wait(NULL);
		printf("Hello! i am parent of %d (wc: %d) (pid: %d)\n", rc, wc, getpid());
	}

	return 0;
}

 子进程调用wait(),wait()会返回-1,并且设置errno为ECHILD

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<errno.h>

int main(void)
{
	int rc = fork();

	if (rc == 0)
	{
		int wc = wait(NULL);
		printf("Hello! i am child (wc: %d) (pid: %d)\n", wc, getpid());
	}
	else
	{
		printf("Hello! i am parent of %d (pid: %d)\n", rc, getpid());
	}

	if (errno == ECHILD)
	{
		printf("No child!\n");
	}

	return 0;
}

6. Write a slight modification of the previous program, this time using waitpid() instead of wait(). When would waitpid() be useful?

wait()等价于waitpid(-1, &status, 0)

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<errno.h>

int main(void)
{
	int status;
	int rc = fork();
	int wc = waitpid(-1, &status, 0);

	if (rc == 0)
	{
		printf("Hello! i am child (wc: %d) (pid: %d)\n", wc, getpid());
	}
	else
	{
		printf("Hello! i am parent of %d (wc: %d) (pid: %d)\n", wc, rc, getpid());
	}

	if (errno == ECHILD)
	{
		printf("No child!\n");
	}

	return 0;
}

7. Write a program that creates a child process, and then in the child closes standard output (STDOUT FILENO). What happens if the child calls printf() to print some output after closing the descriptor?

在子进程中关闭标准输出,不会影响到父进程的输出

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

int main(void)
{
	int rc = vfork();  //让子进程先运行,看看对父进程有什么影响

	if (rc == 0)
	{
		printf("Hello! i am child (pid: %d)\n", getpid());
		close(STDOUT_FILENO);
		printf("What happend?\n");
		exit(0);
	}
	else
	{
		printf("Hello! i am parent of %d (pid: %d)\n", rc, getpid());
	}

	return 0;
}

8. Write a program that creates two children, and connects the standard output of one to the standard input of the other, using the pipe() system call.

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

int main(void)
{
	int p[2];
	int pid1;
	int pid2;

	pipe(p);

	pid1 = fork();

	if(pid1 == 0)
	{
		close(p[0]);
     	write(p[1],"hello\n",6);
		exit(0);
	}	

	pid2 = fork();
	
	int buf[10];
	if(pid2 == 0)
	{
		close(p[1]);
		read(p[0],buf,6);
		printf("%s",buf);
		exit(0);
	}
	
	wait(NULL);
	wait(NULL);

	exit(0);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值