第5章 插叙:进程API homework详解
实验环境
笔者采用的是Ubuntu22.04的虚拟机。
本次作业原则上只需自己编码,不需要依靠实验材料,不过作者也提供了两个py程序可供参考,文件名为cpu-api。
实验内容
1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| #include<stdio.h> #include<stdlib.h> #include<unistd.h>
int main(int argc, char *argv[]) { int x = 100; printf("%d (pid:%d)\n", x, (int) getpid()); int rc = fork(); if (rc < 0) { fprintf(stderr, "fork failed\n"); exit(1); } else if (rc == 0) { printf("%d (pid:%d)\n", x, (int) getpid()); x = 1000; } else { printf("%d (pid:%d)\n", x, (int) getpid()); x = 500; } printf("%d\n", x); return 0; }
|
由此可见,在fork子进程之后修改主进程变量不会影响到子进程,要把它们理解成两个独立的进程,当然子进程的变量值全部继承于fork子进程之前的主进程。
2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<fcntl.h> #include<string.h> #include<sys/wait.h>
int main() { int fd = open("temp.txt", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU); int rc = fork(); if (rc < 0) { close(fd); fprintf(stderr, "fork failed"); exit(1); } else if (rc == 0) { char *s = "child write something!\n"; write(fd, s, strlen(s)); } else { char *s = "parent write something\n"; write(fd, s, strlen(s)); wait(NULL); close(fd); } return 0; }
|
两个进程都能对打开的文件进行修改。
3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include<stdio.h> #include<stdlib.h> #include<unistd.h>
int main(int argc, char *argv[]) { int rc = vfork(); if (rc < 0) { fprintf(stderr, "fork failed\n"); exit(1); } else if (rc == 0) printf("hello\n"); else printf("goodbye\n"); return 0; }
|
使用vfork创建子进程,可以优先执行子进程。
4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| #include<stdio.h> #include<unistd.h> #include<stdlib.h>
int flag = 0; const int MAX = 6; int main() { char * s = "/bin/ls"; char * ss = "ls"; char * s2 = "/"; char * sv[] = { ss, s2, NULL }; for(flag = 0; flag < MAX; ++flag) { int rc = fork(); if (rc < 0) { fprintf(stderr, "fork failed"); exit(1); } else if (rc == 0) { switch(flag) { case 0: execl(s, ss, s2, NULL); break; case 1: execle(s, ss, s2, NULL); break; case 2: execlp(s, s, s2, NULL); break; case 3: execv(s, sv); break; case 4: execvp(ss, sv); break; case 5: execvpe(ss, sv); break; default: break; } } else wait(NULL); } return 0; }
|
依次调用了6个函数,可见都能实现最终效果。
5
第一个程序,在父进程中使用wait
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include<stdio.h> #include<stdlib.h> #include<unistd.h>
int main(int argc, char *argv[]) { int rc = fork(); if (rc < 0) { fprintf(stderr, "fork failed\n"); exit(1); } else if (rc == 0) printf("I am child!(pid: %d)\n", (int)getpid()); else { int wc = wait(NULL); printf("I am parent!(wc: %d pid: %d)\n", wc, (int)getpid()); } return 0; }
|
可以看到wait的返回值恰好是子进程的pid
第二个程序,在子进程中使用wait
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include<stdio.h> #include<stdlib.h> #include<unistd.h>
int main(int argc, char *argv[]) { int rc = fork(); if (rc < 0) { fprintf(stderr, "fork failed\n"); exit(1); } else if (rc == 0) { int wc = wait(NULL); printf("I am child!(wc: %d pid: %d)\n", wc, (int)getpid()); } else printf("I am parent!(pid: %d)\n", (int)getpid()); return 0; }
|
可以看到运行结果依然是先运行主进程,再运行子进程,值得注意的是,wait返回值为-1。
6
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include<stdio.h> #include<stdlib.h> #include<unistd.h>
int main(int argc, char *argv[]) { int rc = fork(); if (rc < 0) { fprintf(stderr, "fork failed\n"); exit(1); } else if (rc == 0) printf("I am child!(pid: %d)\n", (int)getpid()); else { int wc = waitpid(NULL); printf("I am parent!(wc: %d pid: %d)\n", wc, (int)getpid()); } return 0; }
|
waitpid相比于wait的区别在于:
1 waitpid使我们可以等待指定的进程
2 waitpid提供了一个无阻塞的wait
3 waitpid支持工作控制
7
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include<stdio.h> #include<unistd.h> #include<stdlib.h>
int main() { int rc = fork(); if (rc < 0) { fprintf(stderr, "fork failed"); exit(1); } else if (rc == 0) { close(STDOUT_FILENO); printf("output child\n"); } else printf("output parent\n"); return 0; }
|
子进程不会影响到主进程。
8
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| #include<stdio.h> #include<unistd.h> #include<stdlib.h>
int main() { int pi[2]; int p = pipe(pi); if(p < 0) { fprintf(stderr, "pipe failed"); exit(1); } int i = 0; int rc[2]; char buf[256]; for(i = 0; i < 2; ++i) { rc[i] = fork(); if (rc[i] < 0) { fprintf(stderr, "fork failed"); exit(1); } else if (rc[i] == 0) { switch(i) { case 0: dup2(pi[1], STDOUT_FILENO); puts("child input"); break; case 1: dup2(pi[0], STDIN_FILENO); gets(buf); printf("child0 out (%s) in the child1\n", buf); return 2; } break; } } waitpid(rc[0], NULL, 0); waitpid(rc[1], NULL, 0); return 0; }
|
pipe创建一个管道,将两个进程连接到一起。
int wc = wait(NULL);
printf(“I am parent!(wc: %d pid: %d)\n”, wc, (int)getpid());
}
return 0;
}
// 运行结果
// I am child!(pid: 27378)
// I am parent!(wc: 27378 pid: 27377)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| 可以看到wait的返回值恰好是子进程的pid
第二个程序,在子进程中使用wait
```c #include<stdio.h> #include<stdlib.h> #include<unistd.h>
int main(int argc, char *argv[]) { int rc = fork(); if (rc < 0) { fprintf(stderr, "fork failed\n"); exit(1); } else if (rc == 0) { int wc = wait(NULL); printf("I am child!(wc: %d pid: %d)\n", wc, (int)getpid()); } else printf("I am parent!(pid: %d)\n", (int)getpid()); return 0; }
|
可以看到运行结果依然是先运行主进程,再运行子进程,值得注意的是,wait返回值为-1。