Linux 关于fork函数和sleep函数以及通信管道的一些思考

在学进程通信的时候接触到了下面这段代码

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

int main()
{
    
    
    //create pipe
    int fd[2]={
    
    0,0};
    if(pipe(fd)!=0){
    
    
        //create false
        perror("pipe");
        exit(1);
    }
    // pipe create success
    pid_t id=fork();
    if(id==0){
    
    
        //child -->write fd[1]
        printf("Child\n");
        sleep(2);
        const char* msg="Hello,leap\n";
        close(fd[0]);
        int count=3;
        while(count--){
    
    
            ssize_t size=write(fd[1],msg,strlen(msg));
            printf("size:%d\n",size);
            //if(count--){
    
    
            //  sleep(1);
            //}
            sleep(1);
            printf("child is writing...\n");
        }
        close(fd[1]);
        exit(0);
    }
    else{
    
    
        //father -->read fd[0]
        printf("Father\n");
        sleep(2);
        close(fd[1]);
        char buf[1024];
        int count=3;
        while(1){
    
    
            ssize_t Len=read(fd[0],buf,1024);
            //printf("Len::%d\n",Len);
            printf("Father is reading...\n");
            if(Len>0){
    
    
                //read success
                buf[Len]='\0';
                printf("child say:%s",buf);
            }
            else if(Len==0){
    
    
                //read end of file
                printf("Read the end of pipe\n");
                break;
            }
            else{
    
    
                perror("read");
                exit(1);
            }
        }
        close(fd[0]);
        int status=0;
        pid_t _pid=waitpid(id,&status,0);
        if(_pid==id){
    
    
            printf("Wait success for child\n");
            printf("Exit code:%d,Exit signal:%d\n",(status>>8)&0xff,status&0xff);
        }
        else{
    
    
            perror("wait");
        }
        exit(0);
    }
    return 0;
}
————————————————
版权声明:本文为CSDN博主「_stark」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/bit_clearoff/article/details/55105816

这段代码里有不少的sleep函数,以前使用sleep函数都是为了扩大一下程序运行的时间来对比各种算法的时间复杂度。这是第一次在OS里面接触到sleep函数,这里的sleep函数显然是针对进程来进行的。意识到以前一直忽略了一个问题,就是fork函数创建的子进程与父进程执行的顺序究竟是怎样的.在网上寻找了一下,没有找到答案。于是写了以下的程序进行测试:

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

int main(){
    
    
	pid_t t = fork(),count=100;
	while(count--){
    
    
		if(t == 0){
    
    
			printf("son\n");
		}
		else{
    
    
			printf("father\n");
		}
	}

	return 0;
}

运行的结果是前半段是是father,中间是father和son交替出现,后面是son。
那么猜测的结果应该是他们是并发进行的,只不过son进程进入执行状态的时间会稍微慢一点。所以才会出现中间交替出现的情况。
那么上面那段代码里的sleep函数可以这么理解,首先是父进程先执行,执行到第一个sleep函数后挂起,进入到子进程之后又有一个sleep(2),理论上来说和父亲休眠的相同的时间后应该是父亲先进入执行状态,结果运行的结果却是下面这样:
在这里插入图片描述

可以发现的是先进入到了子进程处,此处产生了很大的疑惑,在网上找寻不到答案也不知道如何正确描述问题,目前只能强行认为是此时运行的子进程的优先级比较高,在多个进程sleep同样的时间的时候优先运行当前正在运行的进程。然后执行子进程的下一个sleep函数,这时才转移到父进程。
上面只是本人不负责任的推理。关于这段代码的疑惑还存在几个重要的问题待解决:

  1. 这个sleep函数到底是如何运行的?为何父进程先挂起了2ms子进程后挂起2ms但最后还是子进程先执行?
  2. 多进程之间的执行顺序又是如何?是和那个测试程序一样多进程之间一个进程读取一行代码吗?

还有一个更神奇的问题时当这段代码执行多次之后,运行顺序将会变得混乱起来。有时reading会在size上面,有时size又会在reading上面,但是进程之间的通信却没有被打扰,父进程依然能正常的接受到子进程的消息。也就是说正在执行的子进程还是优先于父进程执行的。

目前的OS学习刚刚起步,希望这些问题在之后的学习中能得到解决。

update:
事实证明在写程序之前应该先去阅读一下原理。上述的其实是一个很简单的问题,就是管道对于读写端控制的问题。为了让问题更清晰,我们来看下面这段代码。

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

int main(){
    
    
    int fd[2] = {
    
    0,0};
    if(pipe(fd) != 0)
        exit(-1);
    pid_t id = fork();
    if(id == -1)
        exit(-1);
    if(id == 0){
    
     // son
        close(fd[0]); // 关闭读端,父子进程之间的管道端都是独立的,这个只是单向关闭了子进程的读端。
        for(int i = 1;i <= 3; ++ i){
    
    
            printf("I'm writing....\n");
            char msg[20] = "This is x time write"; // 使用char*定义的数组无法通过下标更改数组的值。
            msg[8] = i + '0';
            write(fd[1],msg,strlen(msg));
            sleep(30); // 等待父亲读取。
        }
        close(fd[1]);
        exit(0);
    }
    else{
    
     // father
        sleep(2);
        close(fd[1]);
        char msg[1024];
        int cnt = 0;
        while(1){
    
    
            printf("%d\n",++cnt);
            ssize_t length = read(fd[0],msg,1024); // 
            if(length < 0)
                exit(-1);
            else if(length == 0){
    
    
                printf("done!");
                break;
            }
            else{
    
    
                msg[length] = '\0';
                printf("father:%s\n",msg);
                sleep(2);
            }
        }
        close(fd[0]);
    }


    return 0;
}

这是代码的运行结果:

在这里插入图片描述

在这个程序里我将sleep函数的时间设定到了30s,有一个客观事实是父子进程确实是并发进行的。那么为什么程序没有在sleep的时间中因为父进程读不到数据而结束?其实很简单,pipe创建的管道为我们提供了阻塞机制。当某个进程要在管道中读取数据时,如果此时管道是空的但又还有写端未关闭时,系统会使用堵塞机制堵塞掉read语句,直到管道中有数据可以读取的时候,read语句才会正确执行。相同的是,如果写端写入数据的时候管道满了,write语句同样会被挂起。

猜你喜欢

转载自blog.csdn.net/weixin_45590210/article/details/115191473