创建守护进程时,去除进程关联的终端问题

zhiys 2011-08-04 10:31:23
最近在阅读 apue , 关于进程的终端设备一直没有搞明白。为了创建一个守护进程,我首先要fork ,然后终止father , 在child 执行 setsid ,创建一个new session, 然后再次fork , 再次终止father , 这时的child 属于new session中的一员 , 并且不是这个new session 的首进程,而new session 的创建会断开原始关联的终端,所以最终的 child 没有终端。(以上方式来自 apue 书)。
我按照书描述写了如下:
int main()
{
int fork_id ;
switch(fork_id=fork())
{
case -1:{ printf("fork failed"); return(-1); }
case 0:{ printf("child"); setsid();
fork_id=fork();
if(fork_id==0) { printf("child without /dev/tty"); }
else { printf("father"); exit(2); }
break ; }
default:{ printf("father"); break ; }
}
return(1);
}
按照我的理解,执行到 setsid();后进程就应已经断开了终端,其后执行到 if(fork_id==0) 时如果是子进程,则执行 printf("child without /dev/tty");
时,已经是session 的非首进程,肯定是没有终端关联的。
我的问题:既然终端都没有了,那么child 进程的标准输出printf("child without /dev/tty");
肯定不会再显示到我的屏幕上了,可是实际程序却是依然输入到我的屏幕,这是为什么?
...全文
246 6 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
6 条回复
切换为时间正序
请发表友善的回复…
发表回复
0153 2011-08-04
  • 打赏
  • 举报
回复
第二子进程要做的事情还很多,lz这样写并没有与控制终端分离。
以下是我写的一个子程序(供main调用)给你参考:
//把进程初始化为Daemon,返回0或1说明main需要结束,返回0x80表示可继续.
int initDaemon(const char* lpcszMyName)
{
int i,pid,hFile;
char szFilename[108];
//第一次fork
pid = fork();
if (pid > 0) {
//父进程到此结束.
return 0;
}
if (pid < 0) {
//fork失败,退出.
return 1;
}

////////////////////////////////////
//只有第一子进程可以走到这里继续执行.

//第一子进程成为新的会话组长和进程组长.
setsid();

//第二次fork
pid = fork();
if (pid > 0) {
//第一子进程到此结束.
return 0;
}
if (pid < 0) {
//fork失败,退出.
return 1;
}

////////////////////////////////////
//只有第二子进程可以走到这里继续执行.

//与控制终端分离.
for (i = 0; i < NOFILE; i++) {
//关闭打开的文件描述符.
close(i);
}

//改变工作目录到/tmp防止无法卸载文件系统.
chdir("/tmp");

//重定向标准输出.
sprintf(szFilename, "/tmp/%s.out", lpcszMyName);
hFile = open(szFilename, O_WRONLY|O_CREAT|O_TRUNC, 0644);
if (hFile != -1) {
dup2(hFile, 1);//1号句柄代表标准输出设备.
close(hFile);
}
//重定向标准错误.
sprintf(szFilename, "/tmp/%s.err", lpcszMyName);
hFile = open(szFilename, O_WRONLY|O_CREAT|O_TRUNC, 0644);
if (hFile != -1) {
dup2(hFile, 2);//2号句柄代表标准错误设备.
close(hFile);
}

//重设文件创建掩码.
umask(0);

//处理SIGCHLD信号.
signal(SIGCHLD, SIG_IGN);
return 0x80;
}
就想叫yoko 2011-08-04
  • 打赏
  • 举报
回复
MARK一下
zhiys 2011-08-04
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 0153 的回复:]
第二子进程要做的事情还很多,lz这样写并没有与控制终端分离。
以下是我写的一个子程序(供main调用)给你参考:

C/C++ code

//把进程初始化为Daemon,返回0或1说明main需要结束,返回0x80表示可继续.
int initDaemon(const char* lpcszMyName)
{
int i,pid,hFile;
char szFi……
[/Quote]
感谢你的详细讲解, 很经典,学习了,我确实省略很多步骤,我的第2个子进程没有关闭 0,1,2 但我如在第2个子进程中放置 sleep(60); 这样执行后,我在shell 中 ps -axj|grep "程序名" 后会看到这个sleep进程的TTY 列会显示一个 "?" 符号,而其他正常进程都显示 pts/1 等类似终端名称,好像说明,这第2子进程的终端已经不存在了,而且在第2个子进程中,我执行 open 或 fopen 读写方式打开 /dev/tty 时会失败,(在apue 中没有提供如何检查一个进程有没有控制终端的明确方法)我只能根据上述情况判定,这个子进程的控制终端已经不存在了,但只要是我没有在父进程,或第一子进程中,或第2子进程中 关闭0,1,2 , 我第2子进程的输出都会显示到屏幕。 我该如何准确确定我的第2子进程没有控制终端关联呢?
ytfire 2011-08-04
  • 打赏
  • 举报
回复
编写守护进程的步骤应该是:
1.创建子进程,父进程退出;(子进程在形式上与控制终端脱离)
2.在子进程中创建新会话;(完全脱离所有其他进程)
3.改变当前目录的根目录;(防止目录所在文件系统被卸载)
4.重设文件权限掩码;(修改权限掩码umask为0,可以增加守护进程的灵活性)
5.关闭文件描述符。(关闭用不到的文件)


第5部的关闭文件描述符,是因为新建的子进程会继承所有已打开的文件,创建新会话后,守护进程已脱离任何控制终端,故应该关闭用不到的文件。
nickowen 2011-08-04
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 q191201771 的回复:]
哦, 可能是
for (i = 0; i < NOFILE; i++) {
//关闭打开的文件描述符.
close(i);
}
这步的影响
关闭了 0 1 2 就没有输入输出了

猜测而已
希望高手出现
[/Quote]

就是这样的.虽然第1个子进程成为了新的进程和会话组长,没有终端关联.
但它却可以重新开打控制终端.
只有使进程不再成为会话组长才能禁止进程重新打开控制终端
就想叫yoko 2011-08-04
  • 打赏
  • 举报
回复
哦, 可能是
for (i = 0; i < NOFILE; i++) {
//关闭打开的文件描述符.
close(i);
}
这步的影响
关闭了 0 1 2 就没有输入输出了

猜测而已
希望高手出现

70,024

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧