2012-07-16 99 views
3

鉴于这种代码:在父子之间发送消息 - 为什么没有死锁?

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

int main(void) 
{ 
     int  fd[2], nbytes; 
     pid_t childpid; 
     char string[] = "Hello, world! I'm the son and this my message!\n"; 
     char readbuffer[80]; 

     pipe(fd); // piping fd[0] & fd[1] 

     if((childpid = fork()) == -1) // here we create a SON process 
     { 
       perror("fork"); 
       exit(1); 
     } 

     if(childpid == 0) // child process 
     { 
       /* Child process closes up input side of pipe */ 
       close(fd[0]);  // closing the READ end from reading , after that the SON would write into fd[1] 

       /* Send "string" through the output side of pipe */ 
       write(fd[1], string, (strlen(string)+1)); 
       printf("Verification : Message was sent successfully by the SON!\n"); 
       exit(0); 
     } 
     else // father process 
     { 
       /* Parent process closes up output side of pipe */ 
       close(fd[1]); 

       /* Read in a string from the pipe */ 
       nbytes = read(fd[0], readbuffer, sizeof(readbuffer)); 
       printf("I'm the father and I received that string: %s", readbuffer); 
     } 

     return(0); 
} 

输出是:

I'm the father and I received that string: Hello, world! I'm the son and this my message! 
Verification : Message was sent successfully by the SON! 

我想了解的管道,和几件事情我也不清楚:

  1. 如果儿子在那一行write(fd[1], string, (strlen(string)+1));发送他的消息,之后我们有printf验证邮件已发送,为什么我会得到验证(例如。 Verification : Message was sent successfully by the SON! 父亲收到儿子的消息?是不是它假设是第一个验证从儿子和只有字符串?

  2. 如果父亲试图从管道读取,并且儿子想写入管道,在这里的某个地方隐藏(我认为)是死锁,不是吗?为什么我没有陷入僵局?

由于

回答

2

1)从子过程中的消息出现之后是因为写入到一个管道可以被阻塞,直到有在缓冲足够的空间(从here)其原因是:

如果进程尝试写入完整管道(请参阅下面的 ),然后写入(2)块,直到从管道 中读取足够的数据以允许写入完成。

换句话说,子进程等待父进程在write()调用中读取消息。 2)如果子进程没有向管道写任何东西,那么是的,父进程将被阻塞(它不会像这样死锁)。

+0

尽管Linux下的管道缓冲区是16kB,这对于保存一个48字节的消息应该足够了(所以在这个例子中应该确实是_never_block)。我认为这实际上只是一个调度/并发artefact。 – Damon 2012-07-16 12:14:39

+0

@达蒙是的,这似乎很可能。 – trojanfoe 2012-07-16 12:15:34

2

由于I/O缓冲,不能保证输出将按打印顺序显示。

+0

+1我只是想说在这种情况下这是不正确的,但是在再次阅读之后......也许如果你对它进行了轻微的改写,它就是_quite_正确的。管道上的“写入”过程中没有缓冲。但是,像所有C标准lib I/O一样,'printf'肯定是缓冲区。由于'printf'已经在'write'之后执行,但是缓冲所增加的不确定性并不影响它们的顺序。尽管如此,另一个线程也使用'printf',所以人们可以说,你是对的(或者至少没有错)。但是它可能同样(或更多)可能只是一种调度制品而已。 – Damon 2012-07-19 09:42:03

1

当孩子写入管道时,内核会将父级更改为运行状态。 似乎在孩子打印文本之前(可能在孩子从write呼叫返回之前),调度程序将父母切换为正在运行。

所以行

printf("I'm the father and I received that string: %s", readbuffer); 

是行前执行:

printf("Verification : Message was sent successfully by the SON!\n"); 

您可以通过使用strace -f命令进行验证。

1
if(childpid == 0) // child process 
    { 
      write(fd[1], string, (strlen(string)+1)); #C1 
      printf("Verification : Message was sent successfully by the SON!\n"); #C2 
      exit(0); 
    } 
    else // father process 
    { 
      /* Read in a string from the pipe */ 
      nbytes = read(fd[0], readbuffer, sizeof(readbuffer)); #F1 
      printf("I'm the father and I received that string: %s", readbuffer); F2 
    } 

在上述情况下,我们无法分辨自己是否C1或F1将首先发生。这取决于内核调度,不应该依赖。但是两个C1 & F1是相关的,如果管道处于bock模式。相关= 都必须发生,否则会出现死锁。以下情况会导致死锁。

  1. 孩子没有执行C1,但做了其他的事情,如等待输入等&不返回,那么父母将在F1中死锁。
  2. 父母不执行F1,而是做其他事情如等待输入等&不返回,则子将在C1中死锁。

如果孩子/父母退出,那么你会得到一个破管/ sig-pipe我认为。

3

你的第一个问题:

1. wasn't it suppose to be first the verification from the son and only then the string ?

答:当你有多个进程,然后运行这些程序的指令执行顺序是不确定的。它取决于调度程序在何时执行哪个进程。因此,从程序的输出中,我们可以看到指令执行顺序如下:

CHILD-PROCESS: write(fd [1],string,(strlen(string)+1)); \\在此指令之后,此过程被暂停

PARENT-PROCESS: nbytes = read(fd [0],readbuffer,sizeof(readbuffer)); (“我是父亲,我收到了字符串:%s”,readbuffer);

CHILD-PROCESS: printf(“验证:消息由SON成功发送!\ n”);

这个序列可能与其他时间不同。

你secod问题:

2. Why am I not getting a deadlock ?

答:在这种情况下,父进程只有几个街区等在管道上一些输入。但孩子可以写,不必等待。所以没有死锁的机会。

+0

谢谢,当存在死锁时,你能举个例子吗? – ron 2012-07-16 12:27:13

相关问题