LINUX实现父子进程轮流修改文件的值

Posted 项海龙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LINUX实现父子进程轮流修改文件的值相关的知识,希望对你有一定的参考价值。

本例子是基于信号的同步机制实现父子进程轮流修改文件中的值。

tatic volatile sig_atomic_t sigflag;
static sigset_t newmask,oldmask,zeromask;

static void sig_usr(int signo)
{
    sigflag=1;
}

void TELL_WAIT(void)
{
    if(signal(SIGUSR1,sig_usr)==SIG_ERR)
        perror("signal error");
    if(signal(SIGUSR2,sig_usr)==SIG_ERR)
        perror("signal error");
    sigemptyset(&zeromask);
    sigemptyset(&newmask);
    sigaddset(&newmask,SIGUSR1);
    sigaddset(&newmask,SIGUSR2);

    if(sigprocmask(SIG_BLOCK,&newmask,&oldmask)<0)
        perror("SIG_BLOCK error");
}

void TELL_PARENT(pid_t pid)
{
    kill(pid,SIGUSR2);
}

void WAIT_PARENT(void)
{
    while(sigflag==0)
        sigsuspend(&zeromask);
    sigflag=0;

    if(sigprocmask(SIG_SETMASK,&oldmask,NULL))
        perror("SIG_SETMASK error");
}

void TELL_CHILD(pid_t pid)
{
    kill(pid,SIGUSR1);
}

void WAIT_CHILD(void)
{
    while(sigflag==0)
        sigsuspend(&zeromask);

    sigflag=0;

    if(sigprocmask(SIG_SETMASK,&oldmask,NULL))
        perror("SIG_SETMASK error");
}

以上是父子进程通过信号同步的代码实现

开始我是这样写的,先上代码

int main()
{
    int fd;
    pid_t pid;
    char buf[3];
    int number=0;
    int len;

    buf[0]=0;
    buf[1]=\0;
    if((fd=open("file.txt",O_RDWR|O_CREAT|O_TRUNC|O_SYNC))<0)
        perror("open file error");
    if(write(fd,buf,2)<0)
        perror("write error");
    TELL_WAIT();
    if((pid=fork())<0)
    {    
        perror("fork error");
    }else if(pid == 0)
    {
        for(;;)
        {
            WAIT_PARENT();
            
            lseek(fd,SEEK_SET,0);
            if((len=read(fd,buf,10))<0)
                perror("read error");
            
            number=atoi(buf);
            printf("child number %d\n",number);
            number++;
            memset(buf,0,sizeof(buf));
            sprintf(buf,"%d",number);
            buf[strlen(buf)]=\0;
            if(open("file.txt",O_RDWR|O_CREAT|O_TRUNC|O_SYNC)<0)
                perror("open errpr");
//            lseek(fd,SEEK_SET,0);
            if(write(fd,buf,2)<0)
                perror("write error");
            sleep(2);
            TELL_PARENT(getppid());
                                        
        }    
    }else
    {
        for(;;)
        {
            lseek(fd,SEEK_SET,0);
            if((len=read(fd,buf,10))<0)
                perror("read error");
            
            number=atoi(buf);    
            printf("parent number : %d\n",number);
            number++;
            memset(buf,0,sizeof(buf));
            sprintf(buf,"%d",number);
            
            if(open("file.txt",O_WRONLY|O_CREAT|O_TRUNC)<0)
//                perror("open errpr");
//            lseek(fd,SEEK_SET,0);
            if(write(fd,buf,2)<0)
                perror("write error");
            sleep(2);
            TELL_CHILD(pid);
            WAIT_CHILD();        
        }
    }
    exit(0);
}

首先创建一个文件,往文件中写入0字符。之后父进程中先读取文件中的字符,将文件清空,字符转化为整形后加一后写入文件。子进程和父进程做相同操作。但是运行结果是这样的

parent number : 0
child number 0
parent number : 1
child number 1
parent number : 2
child number 2
parent number : 3

子进程读出的数据并不是父进程写入的数据,似乎父子进程对文件的操作是独立的一样,这不符合父子进程共享同一个文件表项的规则。我反复看了好久,

 

于是我在父进程清空文件前加了延迟,查看了文件内容,发现正常写入。在清空后进行写入操作后查看文件发现文件内容为空。

sleep(4);
if(open("file.txt",O_WRONLY|O_CREAT|O_TRUNC)<0)

于是调试了一下查看了一下清空前buf的值,发现没有问题

68                sprintf(buf,"%d",number);
(gdb) s
69                sleep(3);
(gdb) p buf
$2 = "1\000"
(gdb) 

之后运行到第二个睡眠函数时,再查看文件内容,发现文件为空。

那么我猜测问题出在我通过open函数来将文件清空。(这里我也不知道为什么会这样,希望有人能指导一下)

 

那么我换了一种方法,通过每次写数据直接覆盖原来的数据,而不是将文件长度截断为0,来实现

else if(pid == 0)
    {
        for(;;)
        {
            WAIT_PARENT();
            
            lseek(fd,SEEK_SET,0);
            if((len=read(fd,buf,10))<0)
                perror("read error");
            
            number=atoi(buf);
            printf("child number %d\n",number);
            number++;
            memset(buf,0,sizeof(buf));
            sprintf(buf,"%d",number);
            buf[strlen(buf)]=\0;
//            if(open("file.txt",O_RDWR|O_CREAT|O_TRUNC|O_SYNC)<0)
//                perror("open errpr");
            lseek(fd,SEEK_SET,0);
            if(write(fd,buf,2)<0)
                perror("write error");
            sleep(2);
            TELL_PARENT(getppid());
                                        
        }    
    }else
    {
        for(;;)
        {
            lseek(fd,SEEK_SET,0);
            if((len=read(fd,buf,10))<0)
                perror("read error");
            
            number=atoi(buf);    
            printf("parent number : %d\n",number);
            number++;
            memset(buf,0,sizeof(buf));
            sprintf(buf,"%d",number);
            
//            if(open("file.txt",O_WRONLY|O_CREAT|O_TRUNC)<0)
//                perror("open errpr");
            lseek(fd,SEEK_SET,0);
            if(write(fd,buf,2)<0)
                perror("write error");
            sleep(2);
            TELL_CHILD(pid);
            WAIT_CHILD();        
        }
    }

 

parent number : 0
child number 1
parent number : 2
child number 3
parent number : 4
child number 5

 



以上是关于LINUX实现父子进程轮流修改文件的值的主要内容,如果未能解决你的问题,请参考以下文章

如何修改 Linux 中的进程名

Linux pipe 源代码分析

Linux进程控制

Linux fork操作之后发生了什么?

Linux 编程之进程篇:task_struct进程创建和退出

Linux 编程之进程篇:task_struct进程创建和退出