背景说明:后台子系统都是运行在pc上的linux系统有多个子系统,有一个子系统负责统一启停其他子系统,这里把这个子系统称为olddriver。olddriver子系统在每个子系统的所有主机上都有一个用户及相应的客户端进程。平时各子系统各自为阵,自行在主机shell命令行进行启停。图示如下:
上线前,在olddriver上把所有的子系统部署,配置做完,上线当晚使用olddriver启动子系统A,发现启不来。进一步追查是IPC无法创建。由于输出日志有限,不能进一步定位。
当时我猜想是不是因为olddriver client使用exec启动子系统A的进程时缺乏创建IPC的权限,于是想起一个解决办法“设置用户ID”。
进程的权限是和进程的有效用户ID相关,通常情况下exec函数执行时将父进程的有效用户ID拷贝过来设置子进程的有效用户ID,
但是如果子进程的可执行文件设置了设置用户ID时(chmod +s),exec将会使用设置用户ID来作为子进程的有效用户ID
所以不管你是哪个用户下的什么进程通过exec来执行这个可执行文件时,产生的子进程都相当于是在可执行文件归属用户下启动一样,就不会存在权限问题。我把这个想法给相关领导说了一下,没有被采用,因为领导考虑上线时间控制更加重要,所以像平时一样在shell命令行直接启动了。
有没有被坑的感觉,大家!还没完...
系统顺利上线了,之后的某一天,负责子系统A的同学获得了一次gdb的机会,还是由olddriver来启动的场景。于是终于定位到了问题。
代码居然使用了errno来作为流程控制伪代码大概是这样
init(); //这里errno会变为2 fprintf(stdout,"xxxx",...); if(errno==2) { createIPC(); }
gdb的时候发现在init()后,errno==2 但是fprintf()以后errno变成了25
errno.25 is: Inappropriate ioctl for device
我在/proc/该进程的进程号/目录下执行了 ls -l命令,发现标准输出连接到了/dev/null。
问题定位了,olddriver在启动子进程的时候把标准输出重定向了,所以fprintf()报错设置了errno,导致流程跳过了IPC的创建;由于平时在shell命令行启动时,并没有重定向标准输出,所以没有发现这个问题。
所以,遇到问题想办法精确定位问题才是正确之选,猜测不靠谱啊。