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

论坛 期权论坛 脚本     
已经匿名di用户   2022-2-7 16:33   2171   0

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()--------内存分配表

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:81
帖子:4969
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP