linux write 函数 是否是 线程安全的?

http://bbs.chinaunix.net/thread-4187636-1-1.html

我做了两个实验:

第一个实验,创建一个本地文件,然后用5个线程对这个文件进行写入,结果前面的写入内容被后面的写入内容覆盖;对write函数加锁之后结果就正常了,就似乎验证了write函数是非线程安全的。

第二个实验,创建一个客户端的TCP socket,然后用5个线程对这个socket进行写入;服务器端把内容读取出来并打印,发现打印结果与客户端发送内容一致,没有出现异常,似乎说明write TCP socket是线程安全的。

我的问题是:
如果write不是线程安全的,为什么写TCP socket却正常,是否因为系统为socket操作加锁了?

实验代码如下

  1. #include        <unistd.h>
  2. #include        <errno.h>
  3. #include        <pthread.h>

  4. #include        <sys/socket.h>
  5. #include        <netinet/in.h>
  6. #include        <sys/types.h>
  7. #include        <sys/select.h>
  8. #include        <sys/stat.h>
  9. #include        <arpa/inet.h>
  10. #include        <fcntl.h>

  11. #include        <stdio.h>
  12. #include        <stdlib.h>
  13. #include        <string.h>

  14. #define BUFF_SIZE 1024

  15. pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

  16. struct ThreadArg
  17. {
  18.         int id;
  19.         int fd;
  20. };

  21. void* 
  22. proc(void* arg)
  23. {
  24.         struct ThreadArg* p_arg = (struct ThreadArg*) arg;

  25.         char msg[BUFF_SIZE];
  26.         int  n_msg;
  27.         n_msg = snprintf(msg, BUFF_SIZE, "thread_%d\n", p_arg->id);

  28.         int i;
  29.         for (i = 0; i < 5; ++ i)
  30.         {
  31.                 //pthread_mutex_lock(& mutex);
  32.                 if (write(p_arg->fd, msg, n_msg) < 0)
  33.                         perror("thread %d write fail");
  34.                 //pthread_mutex_unlock(& mutex);
  35.         }
  36. }

  37. int 
  38. open_socket(char* ip)
  39. {
  40.         int                                 connfd;
  41.         struct sockaddr_in         serv_addr;

  42.         if ( (connfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
  43.                 return -1;

  44.         memset(&serv_addr, 0, sizeof(serv_addr));
  45.         serv_addr.sin_family = AF_INET;
  46.         serv_addr.sin_port         = htons(9999);
  47.         inet_pton(AF_INET, ip, &serv_addr.sin_addr);

  48.         if ( connect(connfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1)
  49.                 return -1;

  50.         return connfd;
  51. }

  52. int 
  53. open_file(char* file_name)
  54. {
  55.         return open(file_name, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
  56. }

  57. int 
  58. main(int argc, char** argv)
  59. {
  60.         int  fd;

  61.         pthread_t tids[5];
  62.         struct ThreadArg targ[5];

  63.         //if ( (fd = open_socket("127.0.0.1")) < 0) // 实验二
  64.         //        exit(1);

  65.         if ( (fd = open_file("data")) < 0) // 实验一
  66.                 exit(1);

  67.         /* start child threads */
  68.         int i;
  69.         for (i = 0; i < 5; ++ i)
  70.         {
  71.                 targ[i].id = i;
  72.                 targ[i].fd = fd;
  73.                 pthread_create(tids+i, NULL, proc, targ+i);
  74.         }

  75.         for (i = 0; i < 5; ++ i)
  76.                 pthread_join(tids[i], NULL);

  77.         close(fd);
  78.         exit(0);
  79. }
复制代码
实验二 需要的服务器程序代码如下:
  1. #include        <stdio.h>
  2. #include        <stdlib.h>
  3. #include        <string.h>

  4. #include        <sys/types.h>
  5. #include        <sys/socket.h>
  6. #include        <netinet/in.h>
  7. #include        <sys/epoll.h>
  8. #include        <fcntl.h>

  9. #include        <errno.h>

  10. const int MAX_EVENTS = 1024;
  11. const int BUFF_SIZE = 1024;

  12. void err_quit(const char* msg) {
  13.         printf("%s, error code = %d\n", msg, errno);
  14.         exit(1);
  15. }

  16. void err_sys(const char* msg) {
  17.         printf("%s, error code = %d\n", msg, errno);
  18. }

  19. int create_and_bind(int port_no) {
  20.         int listen_fd;
  21.         struct sockaddr_in serv_addr;

  22.         if ( (listen_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
  23.                 return -1;

  24.         memset(&serv_addr, 0, sizeof(serv_addr));
  25.         serv_addr.sin_family      = AF_INET;
  26.         serv_addr.sin_addr.s_addr = INADDR_ANY;
  27.         serv_addr.sin_port        = htons(port_no);

  28.         if ( bind(listen_fd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) < 0)
  29.                 return -1;

  30.         return listen_fd;
  31. }

  32. int communicate(const int fd)
  33. {
  34.         int  n_msg = 9;
  35.         char msg[BUFF_SIZE];

  36.         int count = 0;
  37.         int n_read;
  38.         while ( (n_read = read(fd, msg, n_msg)) > 0)
  39.         {
  40.                 msg[n_msg] = 0;
  41.                 printf("%s", msg);
  42.                 ++ count;
  43.         }

  44.         printf("msg number = %d\n", count);

  45.         if (n_read < 0)
  46.                 return -1;
  47.         return 0;
  48. }

  49. int main(int argc, char** argv) {

  50.         int listen_fd;
  51.         int conn_fd;

  52.         /* create and bind listening socket */
  53.         listen_fd = create_and_bind(9999);
  54.         if (listen_fd < 0)
  55.                 err_quit("create and bind listening socket failed!");

  56.         /* listening */
  57.         listen(listen_fd, 100);

  58.         while (1) {
  59.                 if ( (conn_fd = accept(listen_fd, NULL, NULL)) < 0)
  60.                         err_sys("accept connection socket failed!");
  61.                 else 
  62.                         if (communicate(conn_fd) < 0)
  63.                                 perror("read socket fail");
  64.                         else
  65.                                 close(conn_fd);
  66.         }

  67.         close(listen_fd);
  68.         exit(0);
  69. }
系统调用都是线程安全的。 write是原子的, 系统一定要保证这个语义, 不然这系统没法用了,但是下面这三个都不是线程安全的。

printf()----stdout()

malloc-------内存分配表

free()--------内存分配表

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值