免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 10629 | 回复: 12
打印 上一主题 下一主题

[函数] 对fput,fwrite系列函数 线程安全性的疑问? [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2006-09-25 15:33 |只看该作者 |倒序浏览
我在win,linux和solaris下面分别测试,开多个线程向同一个文件中puts数据

在windos下,每次fputs数据长度超过100k后,文件中的数据就会出现交错。而每次fputs数据小于10k时,文件内容便是正常的。

而在linux和solaris下,每次fputs的数据长度分别为1k,100k,1000k,文件中的数据都不会出现交错。

请教各位,fputs和fwrite用在多线程时,是否需要认为再加锁保护。它们是不是某种意义的线程安全函数?

论坛徽章:
0
2 [报告]
发表于 2006-09-25 15:41 |只看该作者
不是线程安全的
文件越大,lseek需要的时间越大,线程之间的竞争就越明显

论坛徽章:
0
3 [报告]
发表于 2006-09-25 15:53 |只看该作者
看NETBSD的源代码,fputs, fwrite操作的时候都自动对文件加锁了,应该是线程安全的。

没有在ansi里找到相关定义。不太清楚各个系统、编译器之间是不是一样的。

论坛徽章:
0
4 [报告]
发表于 2006-09-25 15:59 |只看该作者
puts(3S)                                                           puts(3S)

NAME
      puts(), fputs() - put a string on a stream

SYNOPSIS
      #include <stdio.h>

      int puts(const char *s);

      int fputs(const char *s, FILE *stream);

    Obsolescent Interfaces
      int puts_unlocked(const char *s);

      int fputs_unlocked(const char *s, FILE *stream);

DESCRIPTION
      puts() writes the null-terminated string pointed to by s, followed by
      a new-line character, to the standard output stream stdout.

      fputs() writes the null-terminated string pointed to by s to the named
      output stream, but does not append a new-line character.

      Neither function writes the terminating null character.

    Obsolescent Interfaces
      puts_unlocked() and fputs_unlocked() put a string on a stream.

RETURN VALUE
      Upon successful completion, these routines return a non-negative
      number.  Otherwise they return EOF, set the error indicator for the
      stream, and set errno to indicate the error.

ERRORS
      These routines fail if, either the stream is unbuffered or stream's
      buffer needed to be flushed causing an underlying write() call to be
      invoked, and:

           [EAGAIN]       The O_NONBLOCK flag is set for the file descriptor
                          underlying stream and the process would be delayed
                          in the write operation.

           [EBADF]        The file descriptor underlying stream is not a
                          valid file descriptor open for writing.

           [EFBIG]        An attempt was made to write to a file that
                          exceeds the process's file size limit or the
                          maximum file size (see ulimit(2)).

           [EINTR]        A signal was caught during the write() system
                          call.

Hewlett-Packard Company            - 1 -   HP-UX 11i Version 2: August 2003

puts(3S)                                                           puts(3S)

           [EIO]          The process is in a background process group and
                          is attempting to write to its controlling
                          terminal, TOSTOP is set, the process is neither
                          ignoring nor blocking the SIGTTOU signal, and the
                          process group of the process is orphaned.

           [ENOSPC]       There was no free space remaining on the device
                          containing the file.

           [EPIPE]        An attempt is made to write to a pipe or FIFO that
                          is not open for reading by any process.  A SIGPIPE
                          signal is also sent to the process.

      Additional errno values may be set by the underlying write() function
      (see write(2)).

WARNINGS
      puts_unlocked() and fputs_unlocked() are obsolescent interfaces
      supported only for compatibility with existing DCE applications. New
      multithreaded applications should use puts() and fputs().
????
这个是否意味着是线程安全?

NOTES
      puts() and puts_unlocked() append a new-line character; fputs() and
      fputs_unlocked() do not.

SEE ALSO
      ferror(3S), flockfile(3S), fopen(3S), fread(3S), printf(3S), putc(3S),
      orientation(5), thread_safety(5).

STANDARDS CONFORMANCE
      puts(): AES, SVID2, SVID3, XPG2, XPG3, XPG4, FIPS 151-2, POSIX.1, ANSI
      C

      fputs(): AES, SVID2, SVID3, XPG2, XPG3, XPG4, FIPS 151-2, POSIX.1,
      ANSI C

Hewlett-Packard Company            - 2 -   HP-UX 11i Version 2: August 2003

论坛徽章:
0
5 [报告]
发表于 2006-09-25 16:19 |只看该作者
FreeBSD 源代码,有锁,线程安全

#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)fputs.c        8.1 (Berkeley) 6/4/93";
#endif /* LIBC_SCCS and not lint */
#include <sys/cdefs.h>
__FBSDID("$FreeBSD: src/lib/libc/stdio/fputs.c,v 1.11 2002/10/12 16:13:37 mike Exp $");

#include "namespace.h"
#include <stdio.h>
#include <string.h>
#include "un-namespace.h"
#include "fvwrite.h"
#include "libc_private.h"
#include "local.h"

/*
* Write the given string to the given file.
*/
int
fputs(s, fp)
        const char * __restrict s;
        FILE * __restrict fp;
{
        int retval;
        struct __suio uio;
        struct __siov iov;

        iov.iov_base = (void *)s;
        iov.iov_len = uio.uio_resid = strlen(s);
        uio.uio_iov = &iov;
        uio.uio_iovcnt = 1;
        FLOCKFILE(fp);
        ORIENT(fp, -1);
        retval = __sfvwrite(fp, &uio);
        FUNLOCKFILE(fp);
        return (retval);
}


size_t
fwrite(buf, size, count, fp)
        const void * __restrict buf;
        size_t size, count;
        FILE * __restrict fp;
{
        size_t n;
        struct __suio uio;
        struct __siov iov;

        iov.iov_base = (void *)buf;
        uio.uio_resid = iov.iov_len = n = count * size;
        uio.uio_iov = &iov;
        uio.uio_iovcnt = 1;

        FLOCKFILE(fp);
        ORIENT(fp, -1);
        /*
         * The usual case is success (__sfvwrite returns 0);
         * skip the divide if this happens, since divides are
         * generally slow and since this occurs whenever size==0.
         */
        if (__sfvwrite(fp, &uio) != 0)
            count = (n - uio.uio_resid) / size;
        FUNLOCKFILE(fp);
        return (count);
}

论坛徽章:
0
6 [报告]
发表于 2006-09-25 16:23 |只看该作者
Solaris源代码:

/*
* Copyright 1995 Sun Microsystems, Inc.  All rights reserved.
* Use is subject to license terms.
*/

/*      Copyright (c) 1984 AT&T */
/*        All Rights Reserved   */

#pragma ident        "@(#)fputs.c        1.20        05/09/30 SMI"


/*LINTLIBRARY*/
/*
* This version writes directly to the buffer rather than looping on putc.
* Ptr args aren't checked for NULL because the program would be a
* catastrophic mess anyway.  Better to abort than just to return NULL.
*/
#include <stdio.h>
#include "stdiom.h"
#include <errno.h>
#include <memory.h>

static char        *memnulccpy(char *, char *, int, int);

int
fputs(char *ptr, FILE *iop)
{
        int ndone = 0, n;
        unsigned char *cptr, *bufend;
        char *p;
        char c;

        if (_WRTCHK(iop)) {
                iop->_flag |= _IOERR;
#ifdef POSIX
                errno = EBADF;
#endif        /* POSIX */
                return (EOF);
        }
        bufend = iop->_base + iop->_bufsiz;

        if ((iop->_flag & _IONBF) == 0)  {
                if (iop->_flag & _IOLBF) {
                        for ( ; ; ptr += n) {
                                while ((n = bufend - (cptr = iop->_ptr)) <= 0)  
                                        /* full buf */
                                        if (_xflsbuf(iop) == EOF)
                                                return(EOF);
                                if ((p = memnulccpy((char *) cptr, ptr, '\n', n)) != NULL) {
                                        /*
                                         * Copy terminated either because we
                                         * saw a newline or we saw a NUL (end
                                         * of string).
                                         */
                                        c = *(p - 1);        /* last character moved */
                                        if (c == '\0')
                                                p--;        /* didn't write '\0' */
                                        n = p - (char *) cptr;
                                }
                                iop->_cnt -= n;
                                iop->_ptr += n;
                                _BUFSYNC(iop);
                                ndone += n;
                                if (p != NULL) {
                                        /*
                                         * We found either a newline or a NUL.
                                         * If we found a newline, flush the
                                         * buffer.
                                         * If we found a NUL, we're done.
                                         */
                                        if (c == '\n') {
                                                if (_xflsbuf(iop) == EOF)
                                                        return(EOF);
                                        } else {
                                                /* done */
                                                return(ndone);
                                        }
                                       }
                        }
                } else {
                        for ( ; ; ptr += n) {
                                while ((n = bufend - (cptr = iop->_ptr)) <= 0)  
                                        /* full buf */
                                        if (_xflsbuf(iop) == EOF)
                                                return(EOF);
                                if ((p = memccpy((char *) cptr, ptr, '\0', n)) != NULL)
                                        n = (p - (char *) cptr) - 1;
                                iop->_cnt -= n;
                                iop->_ptr += n;
                                _BUFSYNC(iop);
                                ndone += n;
                                if (p != NULL)  {
                                        /* done */
                                               return(ndone);
                                       }
                        }
                }
        }  else  {
                /* write out to an unbuffered file */
                return (write(iop->_file, ptr, strlen(ptr)));
        }
}

/*
* Copy s2 to s1, stopping if character c or a NUL is copied.
* Copy no more than n bytes.
* Return a pointer to the byte after character c or NUL in the copy,
* or NULL if c or NUL is not found in the first n bytes.
*/
static char *
memnulccpy(char *s1, char *s2, int c, int n)
{
        int cmoved;

        while (--n >= 0) {
                cmoved = *s2++;
                if ((*s1++ = cmoved) == '\0' || cmoved == c)
                        return (s1);
        }
        return (0);
}

论坛徽章:
0
7 [报告]
发表于 2006-09-25 16:24 |只看该作者
Solaris fwrite源代码:

/*
* Copyright 1989 Sun Microsystems, Inc.  All rights reserved.
* Use is subject to license terms.
*/

/*      Copyright (c) 1984 AT&T */
/*        All Rights Reserved   */

#pragma ident        "@(#)fwrite.c        1.10        05/06/08 SMI"  /* from S5R2 3.6 */

/*LINTLIBRARY*/
/*
* This version writes directly to the buffer rather than looping on putc.
* Ptr args aren't checked for NULL because the program would be a
* catastrophic mess anyway.  Better to abort than just to return NULL.
*
* This version does buffered writes larger than BUFSIZ directly, when
* the buffer is empty.
*/
#include <stdio.h>
#include "stdiom.h"

#define MIN(x, y)       (x < y ? x : y)

extern char *memcpy();

int
fwrite(ptr, size, count, iop)
char *ptr;
int size, count;
register FILE *iop;
{
        register unsigned nleft;
        register int n;
        register unsigned char *cptr, *bufend;
        register unsigned char *prev_ptr;

        if (size <= 0 || count <= 0 || _WRTCHK(iop))
                return (0);

        bufend = iop->_base + iop->_bufsiz;
        nleft = count*size;

        /* if the file is unbuffered, or if the iop->ptr = iop->base, and there
           is > BUFSZ chars to write, we can do a direct write */
        prev_ptr = iop->_ptr;
        if (iop->_base >= iop->_ptr)  {        /*this covers the unbuffered case, too*/
                if (((iop->_flag & _IONBF) != 0) || (nleft >= BUFSIZ))  {
                        if ((n=write(fileno(iop),ptr,nleft)) != nleft)
                            {
                                iop->_flag |= _IOERR;
                                n = (n >= 0) ? n : 0;
                        }
                        return n/size;
                }
        }
        /* Put characters in the buffer */
        /* note that the meaning of n when just starting this loop is
           irrelevant.  It is defined in the loop */
        for (; ; ptr += n) {
                while ((n = bufend - (cptr = iop->_ptr)) <= 0)  /* full buf */
                        if (_xflsbuf(iop) == EOF)
                                return (count - (nleft + size - 1)/size);
                n = MIN(nleft, n);
                (void) memcpy((char *) cptr, ptr, n);
                iop->_cnt -= n;
                iop->_ptr += n;
                _BUFSYNC(iop);
                /* done; flush if linebuffered with a newline */
                if ((nleft -= n) == 0)  {
                        if (iop->_flag & (_IOLBF | _IONBF)) {
                                       if ((iop->_flag & _IONBF) || (memchr(prev_ptr,
                                        '\n',iop->_ptr - prev_ptr) != NULL))  {
                                             (void) _xflsbuf(iop);
                                }
                        }
                        return (count);
                }
        }
}

论坛徽章:
0
8 [报告]
发表于 2006-09-25 17:03 |只看该作者
多谢风云使者,你真有办法啊,呵呵

下面这段话,似乎真的是说fputs是线程安全的,不过有点隐讳呀。。。
puts_unlocked() and fputs_unlocked() are obsolescent interfaces
      supported only for compatibility with existing DCE applications. New
      multithreaded applications should use puts() and fputs().


solaris那段代码看不太懂,似乎是没有加锁的机制呀

论坛徽章:
0
9 [报告]
发表于 2006-09-25 17:45 |只看该作者
请问wuqing一下,这个函数的源码你是在那个文件里面找到的,我咋没找到呀?

我在linux 环境,这个函数源码是linux的源码里有,还是gcc的源码里面有阿?

论坛徽章:
0
10 [报告]
发表于 2006-09-25 17:48 |只看该作者
Linux下需要安装内核的源代码就可以了,在 /usr/src/kernel2.4/libc 下?因为这个是libc提供的标准函数嘛
我的机器上安装了FreeBSD6.0, Solaris10X86,WindowsXP, Redhat Linux AS4.0(这个没有安装源代码),所以我可以很迅速地找到源代码,但是因为从系统管理员想向程序员方面转,现在还遗留很多比较懒散的习惯,嗬嗬,慢慢改吧

一起努力学习,互相鞭策
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP