zoukankan      html  css  js  c++  java
  • linux 进程-线程文件操作注意点

    为了测试globalmem在不带互斥保护下,多个地方进行IO操作,会引发竞态的问题。写了如下一个测试程序:

    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    
    #define MAX_THREAD_NUM  2
    #define MAX_READ_BUFF_SIZE    1024
    
    void* read_globalmem(void* args)
    {
        int fd = *((int*)args);
        int ret;
        char buf[MAX_READ_BUFF_SIZE];
    
        printf("run a read thread
    ");
        
        while (1)
        {
            ret = read(fd, buf, MAX_READ_BUFF_SIZE - 1);
            if (ret > 0)
            {
                buf[ret] = 0;
                printf("read:%d bytes
    ", ret);
                printf("%s
    ", buf);
            }
        }
    
        return NULL;
    }
    
    
    void* write_globalmem(void* args)
    {
        int fd = *((int*)args);
        int ret;
    
        printf("run a write thread
    ");
        
        while (1)
        {
            ret = write(fd, "0123456789
    ", 11);
            if ( ret < 0 )
            {
                printf("write error
    ");
            }
            else
            {
                printf("write %d bytes
    ", ret);
            }
        }
    
        return NULL;
    }
    
    
    int main(int argc, char** argv)
    {
        int fd, i, ret;
        pthread_t tid[MAX_THREAD_NUM];
        void *(* thread_fun) (void *);
        char buf[MAX_READ_BUFF_SIZE];
    
        fd = open("/dev/globalmem", O_RDWR);
        if ( fd < 0 )
        {
            perror("open");
            return -1;
        }
        
    
        for ( i = 0; i < MAX_THREAD_NUM; ++i )
        {
            if ( i % 2 )
            {
                thread_fun = read_globalmem;
            }
            else
            {
                thread_fun = write_globalmem;
            }
            
            ret = pthread_create(&tid[i], NULL, thread_fun, &fd);
            if ( ret < 0 )
            {
                tid[i] = 0;
                printf("create read thread failed
    ");
            }
        }
        
    
    
        for ( i = 0; i < MAX_THREAD_NUM; ++i )
        {
            if ( 0 != tid[i] )
            {
                pthread_join(tid[i], NULL);
            }
        }
        
        
        close(fd);
        
        return 0;
    }
    

    大概的操作就是开两个线程分别对文件/dev/globalmem进行读写操作。/dev/globalmem缓冲区4K大小。

    而实际在globalmem驱动里面的打印是:

    [114273.958892] read 1023 bytes(s) from 0
    [114273.959057] read 1023 bytes(s) from 1023
    [114273.959257] read 1023 bytes(s) from 2046
    [114273.959404] read 1023 bytes(s) from 3069
    [114273.959414] written 4 bytes(s) from 4092
    

    可以看到write操作的offset是在read操作基础上进行的,很明显这是因为两个线程共用了一个文件表结构,实际情况如下图:

    两个线程共用了一个文件表,导致文件偏移量相互的影响各种的访问。所以应该改为在读写线程中分别打开文件,这样整个进程拥有两个fd表。

    还一种情况是如果这里改为两个父子进程呢?

    	fd = open("/dev/globalmem", O_RDWR);
    
    	pid = fork();
    	if ( pid == 0 )
    	{
    		while(1)
    		{
    			read();
    		}
    	}
    	else if ( pid > 0 )
    	{
    		while(1)
    		{
    			write();
    		}
    	}
    	
    	close(fd);
    

    会发现依然会出现文件偏移量相互干扰的情况,下图反应了fork调用后,父子进程与文件表,文件inode,vnode之间的关系(参考自APUE):

    可以看到fork对父进程打开文件的拷贝只是拷贝了fd + 文件指针,实际的文件描述结构是同一份。所以,参考多线程的解决方法,要分别在各种的进程里面做打开文件的操作。

  • 相关阅读:
    Celery异步框架
    彻底理解cookie,session,token
    消息队列
    pip源、搭建虚拟环境、git
    全文检索
    redis高级
    redis基础
    基本数据结构和算法(python代码实现算法)
    MySQL数据库高级
    MySQL数据库进阶
  • 原文地址:https://www.cnblogs.com/thammer/p/12614065.html
Copyright © 2011-2022 走看看