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\n");
    
    while (1)
    {
        ret = read(fd, buf, MAX_READ_BUFF_SIZE - 1);
        if (ret > 0)
        {
            buf[ret] = 0;
            printf("read:%d bytes\n", ret);
            printf("%s\n", buf);
        }
    }

    return NULL;
}


void* write_globalmem(void* args)
{
    int fd = *((int*)args);
    int ret;

    printf("run a write thread\n");
    
    while (1)
    {
        ret = write(fd, "0123456789\n", 11);
        if ( ret < 0 )
        {
            printf("write error\n");
        }
        else
        {
            printf("write %d bytes\n", 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\n");
        }
    }
    


    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 + 文件指针,实际的文件描述结构是同一份。所以,参考多线程的解决方法,要分别在各种的进程里面做打开文件的操作。

posted @ 2020-04-01 17:29  thammer  阅读(350)  评论(0编辑  收藏  举报