strcpy 一个二进程文件的字节会怎样?

本文探讨了二进制文件的内部结构及复制原理,强调了在复制过程中使用strcpy可能导致的信息丢失问题,并推荐使用memcpy来确保完整复制。通过实例代码展示了正确与错误的复制方式。

展示一下二进制文件内部是什么:
本质上就是一个个字符。通过一定的文件格式,标准(我觉得就像一些程序)写入到文件中,我们可以通过系统软件去解读它(就像序列化和反序列化)

  • 可执行文件ELF格式(至于ELF究竟是什么,去翻《程序员的自我修养》吧,书上极其详细,而我觉得我这部分看过来却只是一知半解)
    a.out
    图片:pdf
    pdf

视频:mp4

mp4
int fd=open("a.txt",O_RDONLY);
int fd2=open("b.txt",O_WRONLY|O_APPEND);
read(fd,buf,BUFSIZE);
strcpy(buf2,buf);
write(fd2,buf2,BUFSIZE);
如果我们用这样一段代码去复制二进制文件,得到的文件是错误的。
但原因是什么呢?

我收获到的经验是如果想要用buf去复制整个文件,在复制的过程中万万要小心。
这些乱码啊是可能包含’\0’这个空字符的,这意味着我们如果使用strcpy这种通过判断是否到’\0’去复制字符串的函数,是有可能(基本上就会)丢失掉文件一部分的信息。使用memcpy,它是一个字节一个字节复制。就可以避免信息丢失。

<think>我们正在处理一个关于进程间传递文件描述符的请求。用户希望看到示例代码。根据之前提供的引用,我们知道有几种管理文件描述符的方法(如select,poll,epoll),并且提到了unixsocket可用于进程间通信(包括传递文件描述符)。特别引用[2]提到了一种使用unixsocket的示例(绑定本地文件),但用户需要的是传递文件描述符的示例。在Unix/Linux系统中,传递文件描述符通常通过UNIX域套接字(AF_UNIX)使用sendmsg和recvmsg系统调用来完成。具体步骤包括:1.创建一对unix域套接字(socketpair)或一个服务器套接字和一个客户端套接字(绑定路径)。2.在发送进程,打开一个文件(或套接字等)获得文件描述符,然后通过sendmsg发送控制消息(包含SCM_RIGHTS类型的辅助数据)来传递文件描述符。3.在接收进程,通过recvmsg接收控制消息,从中提取文件描述符。我们将编写一个示例程序,展示如何通过socketpair在两个进程(父子进程)之间传递文件描述符。这里我们以传递一个文件描述符为例,比如传递一个打开的文件描述符。注意:由于这里需要编写代码,我们使用C语言来演示,因为系统调用通常用C。步骤:1.使用socketpair创建一对相连的unix域套接字。2.创建子进程。3.在父进程中:-打开一个文件(例如,一个临时文件)获得文件描述符。-通过sendmsg将这个文件描述符发送到子进程。-等待子进程读取该文件并验证。4.在子进程中:-通过recvmsg接收文件描述符。-使用该文件描述符读取文件内容并打印。我们将编写相应的代码,并添加必要的注释。注意:由于文件描述符在发送后,接收进程会得到一个新的文件描述符(可能数值不同,但指向同一个文件表项),因此可以像自己打开的文件一样使用。下面提供C语言的示例代码:</think> 下面我将详细介绍在Linux环境下通过UNIX域套接字(UNIX Domain Socket)在不同进程间传递文件描述符的实现方法和示例代码: ### 文件描述符传递原理 在Linux系统中,文件描述符传递是通过**UNIX域套接字**使用`SCM_RIGHTS`机制实现的: 1. 发送方进程通过`sendmsg()`系统调用发送包含描述符的控制消息 2. 接收方通过`recvmsg()`接收消息并解压出新的文件描述符 3. 新的文件描述符指向相同的底层文件对象[^1] ### 示例代码 (C语言实现) ```c #include <sys/socket.h> #include <sys/un.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #define SOCK_PATH "/tmp/fdpassing" int main() { int sockfd, fd_to_send; struct sockaddr_un addr; // 创建UNIX域套接字 if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { perror("socket"); exit(1); } // 绑定到临时地址 memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strcpy(addr.sun_path, SOCK_PATH); unlink(SOCK_PATH); // 移除可能存在的旧文件 if (bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) { perror("bind"); exit(1); } listen(sockfd, 5); // 创建示例文件描述符 (示例中为日志文件) fd_to_send = open("/var/log/system.log", O_RDONLY); if (fd_to_send < 0) { perror("open"); exit(1); } // 等待接收进程连接 printf("等待接收进程连接...\n"); int client_fd = accept(sockfd, NULL, NULL); if (client_fd < 0) { perror("accept"); exit(1); } // 发送文件描述符 struct msghdr msg = {0}; char iobuf[1] = {'\0'}; // 必须发送至少1字节数据 struct iovec io = { .iov_base = iobuf, .iov_len = sizeof(iobuf) }; msg.msg_iov = &io; msg.msg_iovlen = 1; char cmsgbuf[CMSG_SPACE(sizeof(int))]; msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf); struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(sizeof(int)); *(int*)CMSG_DATA(cmsg) = fd_to_send; if (sendmsg(client_fd, &msg, 0) < 0) { perror("sendmsg"); exit(1); } printf("文件描述符%d已发送\n", fd_to_send); close(client_fd); close(sockfd); return 0; } ``` 接收方进程代码: ```c // 接收端需要连接发送端 int main() { int sockfd, recv_fd; struct sockaddr_un addr; sockfd = socket(AF_UNIX, SOCK_STREAM, 0); memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strcpy(addr.sun_path, SOCK_PATH); if (connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) { perror("connect"); exit(1); } // 接收文件描述符 struct msghdr msg = {0}; char iobuf[1]; struct iovec io = { .iov_base = iobuf, .iov_len = sizeof(iobuf) }; msg.msg_iov = &io; msg.msg_iovlen = 1; char cmsgbuf[CMSG_SPACE(sizeof(int))]; msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf); if (recvmsg(sockfd, &msg, 0) < 0) { perror("recvmsg"); exit(1); } // 提取文件描述符 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { recv_fd = *(int*)CMSG_DATA(cmsg); printf("接收到文件描述符%d\n", recv_fd); // 验证描述符有效性 char buf[1024]; ssize_t n = read(recv_fd, buf, sizeof(buf)); if (n > 0) { printf("成功读取数据: %.*s\n", (int)n, buf); } close(recv_fd); } close(sockfd); return 0; } ``` ### 关键注意点 1. **最小数据要求**:即使不需要数据,也必须传递至少1字节有效载荷 2. **权限要求**:传递方和接收方需要有访问底层文件的权限 3. **内核支持**:需要内核支持`SCM_RIGHTS`机制 4. **描述符类型**:可传递各类文件描述符(普通文件、套接字、管道等) 5. **多描述符传递**:一次可传递多个描述符(修改`cmsg_len`和缓冲区大小) 进程间描述符传递避免了冗余数据拷贝,在需要共享I/O资源的场景(如web服务器工作进程共享连接套接字)具有重要作用[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值