🧨🧨🧨🧨🧨🧨🧨🧨大年初一篇🧨🧨🧨🧨🧨🧨🧨🧨

🎉❤️🎉❤️🎉❤️🎉❤️🎉❤️祝各位程序员们马年大吉 万事如意!❤️🎉❤️🎉❤️🎉❤️🎉❤️🎉
目录
[开启core dump](#开启core dump)
1.情景回顾
我在OS53.【Linux】System V 共享内存(2)文章写了一个"共享内存+管道"的实现代码,运行结果没问题,但是我写的最初版本是有问题的,如下:
header.hpp写入:
cpp
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <stdlib.h>
#define SIZE 1024
#define PROJ_ID 0x1344
key_t get_key()
{
char buffer[SIZE];
char* pathname=getcwd(buffer,sizeof(buffer));
if (pathname==nullptr)
{
perror("getcwd failed");
exit(1);
}
return ftok(pathname,PROJ_ID);
}
//使用IPC_EXCL来确保获得新的共享内存
int get_new_shared_memory()
{
key_t key=get_key();
//申请新的共享内存
int shmid=shmget(key,4096,IPC_CREAT|IPC_EXCL);
return shmid;
}
int get_old_shared_memory()
{
key_t key=get_key();
//获取旧的共享内存
int shmid=shmget(key,4097,IPC_CREAT|0666);
return shmid;
}
shm1.cpp写入:
cpp
#include <stdio.h>
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "header.hpp"
int main()
{
//为了减小代码行数,系统调用的错误执行结果都没有判断
mkfifo("./tmp_fifo",0664);
int fd=open("./tmp_fifo",O_RDONLY);
int shmid=get_new_shared_memory();
char* start_addr=(char*)shmat(shmid,nullptr,0);
for (;;)
{
char ch;
ssize_t n=read(fd,&ch,1);
if (n==0||n<0)
break;
std::cout<<start_addr<<std::endl;
}
shmdt(start_addr);
shmctl(shmid,IPC_RMID,nullptr);
unlink("./tmp_fifo");
return 0;
}
shm2.cpp写入:
cpp
#include <stdio.h>
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "header.hpp"
int main()
{
//为了减小代码行数,系统调用的错误执行结果都没有判断
int fd=open("./tmp_fifo",O_WRONLY);
int shmid=get_old_shared_memory();
char* start_addr=(char*)shmat(shmid,nullptr,0);
for (;;)
{
std::cin>>start_addr;
write(fd,"",1);
}
shmdt(start_addr);
return 0;
}
★提示: 此bug非常隐蔽,不仔细分析很难查出问题,非常锻炼调试能力!
2.排查
事故现场
未运行shm1.out和shm2.out时,操作系统中没有任何共享内存:

编译以上代码,然后先运行shm1.out,再运行shm2.out:

shm1.out崩溃后,Ctrl+C退出shm2.out后,查看共享内存:

开启core dump
发现报了"Segmentation fault",为了能进一步查明问题,可以先启用Linux的core dump功能:
cpp
ulimit -c unlimited #让core dump文件大小无限制
在有些云服务器上,虽然设置了core dump文件大小无限制,但是仍然没有在崩溃进程运行的目录下生成core dump文件,使用以下命令:
cpp
sudo bash -c 'echo "core.%p" > /proc/sys/kernel/core_pattern'
注: 该命令是临时修改的,重启会失效
这样让操作系统生成核心转储core dump然后开启gdb进行事后调试,能较为容易定位问题
打开core dump功能后,生成了core.40405文件:

使用gdb进行事后调试
从上面的输出的结果来看,shm1.out先运行,其打开tmp_fifo管道的读端,由于写端未打开,故shm1.out阻塞,再运行shm2.out,其打开tmp_fifo管道的写端,接着shm2.out向共享内存中写入用户的输入,为什么shm1.out会段错误呢?
使用gdb进行事后调试:

给的报错有点奇怪,没太明白:
cpp
Program terminated with signal SIGSEGV, Segmentation fault.
#0 __strlen_evex () at ../sysdeps/x86_64/multiarch/strlen-evex.S:450
看看堆栈信息:
bash
bt
没看出什么问题:

再看看堆栈详细信息:
bash
bt full

注意到:
++start_addr = 0xffffffffffffffff <error: Cannot access memory at address 0xffffffffffffffff>++
虚拟地址为0xffffffffffffffff,为64位虚拟地址的最高端,说明shmat的返回值有问题,man手册查看:

shmat要么返回有效地址,要么返回-1,-1在64位下表示为0xffffffffffffffff ,说明shmat未能成功挂接共享内存
再仔细看看函数的调用顺序:

按照栈先进后出的原则,调用顺序为:
main()-->cout-->__strlen_evex()
shm1.cpp中使用了std::cout<<start_addr<<std::endl,为std::cout<<(char*)0xffffffffffffffff<<std::endl肯定不行,所以报"Segmentation fault"
由于start_addr为0xffffffffffffffff,那么shmid肯定为-1,那么int shmid=get_new_shared_memory()中shmid得到-1,++反推原因: get_old_shared_memory()先执行,导致get_new_shared_memory()无法分配新的内存(因为加了IPC_EXCL标志位),所以shm1.out的shmid为-1!!!++
为了验证我的想法,改动shm2.cpp:
cpp
#include <stdio.h>
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "header.hpp"
int main()
{
//为了减小代码行数,系统调用的错误执行结果都没有判断
int fd=open("./tmp_fifo",O_WRONLY);
int shmid=get_old_shared_memory();
char* start_addr=(char*)shmat(shmid,nullptr,0);
printf("start_addr=0x%p",start_addr);
for (;;)
{
std::cin>>start_addr;
write(fd,"",1);
}
shmdt(start_addr);
return 0;
}
运行结果:确实是我上述所说的那样

★★★错误关键点: shm1.out先运行,读端打开,但阻塞在open系统调用,下面的get_new_shared_memory()没有机会执行
bash
int fd=open("./tmp_fifo",O_RDONLY);// <--阻塞在此处
//暂时无法运行下面的函数
int shmid=get_new_shared_memory();
char* start_addr=(char*)shmat(shmid,nullptr,0);
启动shm2.out后,写端打开,open系统调用执行完后,shm2.out继续执行,get_old_shared_memory()返回有效的地址
cpp
int fd=open("./tmp_fifo",O_WRONLY); //<--接着写端打开后,管道的读写端都打开,不阻塞
//正常执行以下内容
int shmid=get_old_shared_memory();
char* start_addr=(char*)shmat(shmid,nullptr,0);
for (;;)
{
std::cin>>start_addr;
write(fd,"",1);
}
cin执行结束后,向管道写入数据
shm1.out执行get_new_shared_memory(),但发现共享内存已经分配过了,而且使用了IPC_EXCL,那么get_new_shared_memory()返回-1,即上方的0xffffffffffffffff,
cpp
int get_new_shared_memory()
{
key_t key=get_key();
//申请新的共享内存
int shmid=shmget(key,4096,IPC_CREAT|IPC_EXCL);
return shmid;
}
管道的读写端都打开时,管道中又有数据,read不阻塞,执行到std::cout<<start_addr<<std::endl;引发错误
cpp
int shmid=get_new_shared_memory();
char* start_addr=(char*)shmat(shmid,nullptr,0);
for (;;)
{
char ch;
ssize_t n=read(fd,&ch,1);
if (n==0||n<0)
break;
std::cout<<start_addr<<std::endl;
}
3.修改
让get_new_shared_memory()先于get_old_shared_memory()执行即可
shm1.cpp写入:
cpp
#include <stdio.h>
#include <iostream>
#include "header.hpp"
int main()
{
//为了减小代码行数,系统调用的错误执行结果都没有判断
int shmid=get_new_shared_memory();
char* start_addr=(char*)shmat(shmid,nullptr,0);
for (;;)
{
sleep(1);
std::cout<<start_addr;
fflush(stdout);
}
shmdt(start_addr);
shmctl(shmid,IPC_RMID,nullptr);
return 0;
}
shm2.cpp写入:
cpp
#include <stdio.h>
#include <iostream>
#include "header.hpp"
int main()
{
//为了减小代码行数,系统调用的错误执行结果都没有判断
int shmid=get_old_shared_memory();
char* start_addr=(char*)shmat(shmid,nullptr,0);
for (;;)
{
std::cin>>start_addr;
}
shmdt(start_addr);
return 0;
}
运行结果: