写入数据小于指定长度的管道行为及子进程printf追加buf问题解析
1. 当调用write向管道写入数据时,若指定的写入长度大于输入数据的实际大小,会发生什么?
write函数会严格按照指定长度,从输入缓冲区的起始地址读取对应字节数写入管道,完全不会校验缓冲区的有效数据边界。如果指定长度超过缓冲区实际有效数据的大小,write会继续读取缓冲区之后的栈内存内容(这些是未初始化的垃圾数据,内容无规律),直到凑够指定长度为止。
这种行为属于内存越界访问,是C语言中的未定义行为,可能导致程序读取敏感数据、崩溃,或是像本例一样向管道写入无意义的垃圾数据。
2. 程序中子进程printf输出异常追加的原因
结合代码和输出结果,异常的核心原因如下:
(1)父进程写入了越界的垃圾数据
父进程执行write(pipefds[1], "SMAQ", 16)时,要求写入16字节,但字符串"SMAQ"仅占4字节有效数据。write会从"SMAQ"所在内存地址开始,额外读取12字节的栈内存内容(这些是父进程栈中的局部变量残留、栈帧信息等垃圾数据),一并写入管道。从输出里的p、i、p、e、f、d、s、[、1、]等字符可以看出,这些就是被写入管道的垃圾数据。
(2)子进程的输出逻辑暴露了垃圾数据
子进程每次调用read(pipefds[0], buf, 1)读取1字节到buf[0],再用printf("%s", buf)输出。由于初始化时memset(buf, 0, 30)把buf全部置为0,所以每次读取后buf[0]是读到的字符,buf[1]及之后都是'\0',%s会正常输出单个字符后终止。输出里的异常字符,本质就是父进程写入管道的垃圾数据被正常读取并打印出来了。
补充:子进程未关闭管道的写端
pipefds[1],但这不会导致本次输出异常,只是会让子进程在读完16字节后,read调用因管道仍有写端打开而阻塞,无法执行到后续的CCHILD: EXITING!输出。
内容的提问来源于stack exchange,提问作者Kp Vignessh

