这次更新第8章习题。

参考资料:

csapp.h使用说明

将csapp.h复制到/usr/include:

sudo cp csapp.h /usr/include/
sudo cp csapp.c /usr/include/

编译命令需要加上-lpthread:

gcc -o forkprob1 forkprob1.c -lpthread

8.9

进程对 并发地?
AB
AC
AD
BC
BD
CD

8.10

  • A. 调用一次,返回两次。
    • fork
  • B. 调用一次,从不返回。
    • execve, longjmp
  • C. 调用一次,返回一次或者多次。
    • setjmp

8.11

进程图:

- main, i = 0, Fork
	- child1, i = 1, Fork
		- child11, i = 2, hello
		- child1, i = 2, hello
	- main, i = 1, Fork
		- main, i = 2, hello
		- child2, i = 2, hello

所以会输出4个hello输出行。

8.12

进程图:

- main, doit, Fork
	- child1, Fork
		- child1, printf, printf
		- child11, printf, printf
	- main, Fork
		- main, printf, printf
		- child2, printf, printf

所以会输出8个hello输出行。

验证:

$ gcc -o forkprob4 forkprob4.c -lpthread
$ ./forkprob4
hello
hello
hello
hello
hello
hello
hello
hello

8.13

子进程结果:

  • 2

父进程结果:

  • 4
  • 3

父进程先结束:

4
3
2

子进程先结束:

2
4
3

或者:

4
2
3

8.14

进程图:

- main, doit, Fork
  - child1, return, printf, exit(0)
  - main, Fork
  	  - main, printf, exit(0)
  	  - child2, printf, exit(0)

所以会输出3个hello输出行。

验证:

$ gcc -o forkprob5 forkprob5.c -lpthread
$ ./forkprob5
hello
hello
hello

8.15

进程图:

- main, doit, Fork
  - child1, return, printf, exit(0)
  - main, Fork
  	  - main, printf, return, printf, exit(0)
  	  - child2, printf, return, printf, exit(0)

所以会输出5个hello输出行。

验证:

$ gcc -o forkprob6 forkprob6.c -lpthread
$ ./forkprob6
hello
hello
hello
hello
hello

8.16

子进程完成后退出,所以输出结果为:

counter = 2

验证:

$ gcc -o forkprob7 forkprob7.c -lpthread
$ ./forkprob7
counter = 2

8.17

8.4在520页,只要给出满足条件的拓扑排序即可:

Hello, 1, Bye, 0, 1, Bye
Hello, 1, 0, Bye, 1, Bye
Hello, 0, 1, Bye, 1, Bye

8.18

atexit参考资料:

atexit()最后调用。

进程图:

- main, Fork
	- child1, Fork
		- child11, printf("0"), printf("2")
		- child1, printf("1"), printf("2")
	- main, Fork
		- main, printf("1")
		- child2, printf("0")

注意到1,0在2之前,所以A, C, E可能出现;

验证:

$ gcc -o forkprob2 forkprob2.c -lpthread
$ ./forkprob2
112002

8.19

记行数为$a_n$,那么递推关系为

注意到

所以

8.20

参考资料:

csh中命令

setenv COLUMNS 40

等价于bash中命令

export COLUMNS=40

代码:

#include "csapp.h"

int main(int argc, char *argv[], char *env[]){
    printf("\n");
    printf("Environment variables:\n");
    for (int i=0; env[i] != NULL; i++){
        printf("    envp[%2d]: %s\n", i, env[i]);
    }
	
    execve("/bin/ls", argv, env);
    exit(0);
}

编译运行:

$ gcc -o myls myls.c -lpthread
$ ./myls

备注:默认的ls会重置COLUMNS,所以运行时不起作用。

8.21

参考资料:

由于waitpid等待子进程结束,所以可能的输出结果为:

a b c
b a c

8.22

参考资料:

代码:

mysystem.c

#include "csapp.h"

int mysystem(char *command){
    pid_t pid;
    if ((pid = Fork()) == 0){
        char *argv[4] = {"", "-c", command, NULL};
        if(execve("/bin/sh", argv, environ) < 0){
            return 0;
        }
    }
    /* Parent waits for foreground job to terminate */
    int status;
    if (waitpid(pid, &status, 0) > 0){
        // 正常终止
        if (WIFEXITED(status)){
            // 返回状态
            return WEXITSTATUS(status);
        }

        // 由未捕获的信号终止
        if (WIFSIGNALED(status)){
            // 返回信号编号
            return WTERMSIG(status);
        }
    }

    return status;
}

int main(int argc, char *argv[], char *env[]){
    int code = mysystem(argv[1]);
    printf("code is %d\n", code);

    exit(0);
}

test.c:

#include "csapp.h"

int main(){
    printf("This is test program!\n");
    
    exit(4);
}

测试:

$ gcc -o mysystem mysystem.c -lpthread
$ gcc -o test test.c -lpthread
$ ./mysystem ./test
This is test program!
code is 4

8.23

阻塞,部分信号被忽略,没有解决信号不会排队的问题。

8.24

参考资料:

8.18程序位于519。

代码:

/* $begin waitpid1 */
#include "csapp.h"
#define N 2

int main() 
{
    int status, i;
    pid_t pid;

    /* Parent creates N children */
    for (i = 0; i < N; i++)                       //line:ecf:waitpid1:for
	if ((pid = Fork()) == 0)  /* Child */     //line:ecf:waitpid1:fork
	    exit(100+i);                          //line:ecf:waitpid1:exit

    /* Parent reaps N children in no particular order */
    while ((pid = waitpid(-1, &status, 0)) > 0) { //line:ecf:waitpid1:waitpid
        if (WIFEXITED(status)) {                    //line:ecf:waitpid1:wifexited
            printf("child %d terminated normally with exit status=%d\n",
            pid, WEXITSTATUS(status));     //line:ecf:waitpid1:wexitstatus
        }else if(WIFSIGNALED(status)){
            // 返回信号编号
            char buf[100];
            sprintf(buf, "child %d terminated by signal %d: Segmentation fault\n", pid, WEXITSTATUS(status));
            psignal(SIGSEGV, buf);
        }else{
            printf("child %d terminated abnormally\n", pid);
        }
    }

    /* The only normal termination is if there are no more children */
    if (errno != ECHILD)                          //line:ecf:waitpid1:errno
	unix_error("waitpid error");

    exit(0);
}
/* $end waitpid1 */

8.25

代码:

#include "csapp.h"
#define SIZE 100

sigjmp_buf env;

void handler(int sig){
    siglongjmp(env, 1);
}

char *tfgets(char *s, int size, FILE *stream){
    if (!sigsetjmp(env, 1)){
        alarm(5);
        Signal(SIGALRM, handler);
        char* res = fgets(s, size, stream);

        return res;
    }else{
        return NULL;
    }
}

int main(){
    char buf[SIZE];
    char* res = tfgets(buf, SIZE, stdin);

    if (res == NULL){
        printf("NULL\n");
    }else{
        printf("%s\n", res);
    }

    return 0;
}

测试:

$ gcc -o tfgets tfgets.c -lpthread

$ ./tfgets 
123
123

$ ./tfgets 
NULL