8.9
进程对
|
是否并发
|
AB
|
No
|
AC
|
Yes
|
AD
|
Yes
|
BC
|
Yes
|
BC |
Yes |
CD |
Yes |
8.10
A. 调用一次,返回两次: fork
B. 调用一次,从不返回: execve, longjmp
C. 调用一次,返回一次或者多次: setjmp
4行
8行
->x=2
->x=4->x=3
满足这种拓扑即可。
主进程只打印一行。
主进程的直接子进程会打印一行,子进程的子进程又打印一行。
所以是3行。
这里的子进程不是exit,而是return,说明两个子进程都要到回到main函数去打印那里的hello。所以是5行。
输出counter = 2,因为全局变量也是复制的,而不是共享的。
满足
Hello--->1->Bye
\ \
\--->0---->2-->Bye
这种拓扑即可。
Hello
1
Bye
0
2
Bye
|
Hello
1
0
Bye
2
Bye
|
Hello 0 1 Bye 2 Bye |
画一下进程图就可以知道。
所以ACE是可能的。
总共会输出2^n行。
其实这道题不需要你在环境变量里抓取name,只要用execve。
int main(int argc, char* args[])
{
execve("/bin/ls", args, environ); //没有错误处理,注意环境变量
return 0;
}
abc或者bac。c肯定在a和b之后。
不太清楚/bin/sh -c command中,-c以及command的意义。
所以把-c和command都当做/bin/sh的参数吧。
但是如果command有空格怎么办?
int mysystem(char *command)
{
int status;
char *argv[4];
char *a0 = "sh";
char *a1 = "-c";
if( fork()==0 ) /*子进程*/
{
argv[0] = a0;
argv[1] = a1;
argv[2] = command;
argv[3] = NULL;
execve("/bin/sh", args, environ);
return -1; //执行异常
}
else{ /*父进程*/
if( wait(&status) > 0)
{
if(WIFEXITED(status) != 0)
return WEXITSTATUS(status);
else return status;
}
else return -1; //wait异常
}
}
一个可能的原因是,在第一个信号发给父进程之后,父进程进入handler,并且阻塞了SIGUSR2,第二个信号依然可以发送,然而,之后的3个信号便会被抛弃了。因为是连续发送,所以很可能是没等上下文切换,这5个信号就同时发送了。所以只有2个信号被接收。
#include "csapp.h"
#define N 2
int main()
{
int status, i;
pid_t pid;
char errorInfo[128];
/* Parent creates N children */
for(i=0;i if ((pid = Fork()) == 0) /* Child */
exit(100+i);
/* Parent reaps N children in no particular order */
while ((pid = waitpid(-1, &status, 0)) > 0) {
if (WIFEXITED(status))
printf("child %d terminated normally with exit status=%d\n",
pid, WEXITSTATUS(status));
else if(WIFSIGNALED(status))
{
//为什么只在写只读文本时才报错?下面的程序对所有异常退出都会提示
printf("child %d terminated by signal %d: ",
pid, WTERMSIG(status) );
psignal(WTERMSIG(status), errorInfo); //psignal会打印sig的信息
}
}
/* The only normal termination is if there are no more children */
if (errno != ECHILD)
unix_error("waitpid error");
exit(0);
}
fgets的定义如下:
char *fgets(char *buf, int bufsize, FILE *stream);
参数:
*buf: 字符型指针,指向用来存储所得数据的地址。
bufsize: 整型数据,指明buf指向的字符数组的大小。
*stream: 文件结构体指针,将要读取的文件流。
这个应该是用alarm发送信号给自己,然后在信号处理程序里面做文章。
显然,在tfgets里一开始需要调用fgets。然而,因为五秒时间到了,fgets还没有返回,所以我们必须在处理程序里直接跳转到某个地方进行tfgets的NULL返回。这就需要用到非本地跳转。
#include
#include
#include
#include
#include
#include
#include
sigjmp_buf env;
void tfgets_handler(int sig)
{
signal(SIGALRM, SIG_DFL);
siglongjmp(env, 1);
}
char *tfgets(char *buf, int bufsize, FILE *stream)
{
static const int TimeLimitSecs = 5;
signal(SIGALRM, tfgets_handler)
alarm(TimeLimitSecs);
int rc = sigsetjmp(env, 1);
if(rc == 0) return fgets(buf, bufsize, stream);
else return NULL; //alarm,time out
}
注意这里如果用非sig版本,程序将不会正确运行。(尝试了只会time out一次)。
sig版本是可以供信号处理程序使用的版本。
暂时没有做。感觉加上JID的话,得自己写程序来管理每个job。
1)需要定义一个struct结构体数组,JID是1,那么就是数组的第一个。
结构体中有pid和状态。状态分空,运行,停止等等。
难点是JID和pid的互相查找,有点像hash。
如果不用hash,就是枚举数组寻找pid。所以,结构体数组大小要先设定(最多运行的jobs数目),不能太大,否则会增加复杂度。
2)可以写一个addjob和deletejob函数,以pid为参数。
当然,对于后台运行的进程,要注意竞争。
3)需要写handler函数,当子进程运行完毕或终止,需要用handler函数来回收。
还需要写一些handler来实现发送消息的功能,比如ctrl-z和ctrl-c。
4)jobs就是列出数组中状态不为空的进程。
可以写一个searchJID(pid_t pid)函数,根据某个pid来查询JID,当然,这个是可以优化的,当前可以只写枚举。
5)fg和bg倒是比较简单,给子进程发送那些消息,让那些消息重新execve。这个好像也可以用非本地跳转来实现。