会话与终端的关联
新会话关联控制终端的方法 (设备打开前处于空闲状态)
1、 关闭标准输入输出和标准错误输出
2、 将 stdin 关联到终端设备 : STDIN_FILENO -> 0
3、 将 stdout 关联到终端设备 : STDOUT_FILENO -> 1
4、 将 stderr 关联到终端设备 : STDERR_FILENO -> 2
一些相关推论
- 新会话关联控制终端后,会话中的所有进程生命周期与控制终端相关
- 只有会话首进程能过够关联控制终端(会话中的其它进行不行)
进程的标准输入输出与标准错误输出可以进行重定向
- 由描述符 0, 1, 2 决定重定向的目标位置(按顺序打开)
- 控制终端与进程的标准输入输出以及标准错误输出无直接关系
一个大胆的想法
if (pid == 0) {
setsid();
sleep(3);
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
i += open(argv[1], O_RDONLY); // argv[1] 指向终端设备路径; 按顺序打开终端设备,使其描述符对应位 0,1,2
i += open(argv[1], O_RDONLY);
i += open(argv[1], O_RDONLY);
printf("child = %d, ppid = %d, gid = %d, sid = %d\n", getpid(), getppid(), getpgrp(), getsid(getpid()));
printf("i = %d\n", i);
}
会话与终端深度实验
master.c
#define _XOPEN_SOURCE 600
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
char rx = 0;
int c = 0;
int master = 0;
master = posix_openpt(O_RDWR);
if (master > 0) {
grantpt(master);
unlockpt(master);
printf("Slave: %s\n", ptsname(master));
while ((c = read(master, &rx, 1)) == 1) {
if (rx != '\r') {
printf("%c", rx);
}
}
close(master);
}
else {
printf("create pty error...\n");
}
return 0;
}
session.c
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int pid = 0;
int i = 0;
if ((pid = fork()) > 0) {
printf("parent = %d, ppid = %d, pgid = %d, sid = %d\n", getpid(), getppid(), getpgrp(), getsid(getpid()));
printf("new: %d\n", pid);
exit(0);
} else if (pid == 0) {
setsid();
sleep(60);
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
i += open(argv[1], O_RDONLY); // 0 -> STDIN
i += open(argv[1], O_WRONLY); // 1 -> STDOUT
i += open(argv[1], O_RDWR); // 2 -> STDERR
printf("child = %d, ppid = %d, pgid = %d, sid = %d\n", getpid(), getppid(), getpgrp(), getsid(getpid()));
printf("i = %d\n", i);
} else {
printf("fork error...\n");
}
sleep(240);
return 0;
}
守护进程要点分析
什么是守护进程 (Daemon)?
守护进程是系统中执行任务的后台进行
- 不与任何终端相关联(不接收终端相关的信号)
- 生命周期长,一旦启动,正常情况下不会停止(直到系统退出)
- Linux 大多数服务器使用守护进程实现(守护进程名以后缀 d 结尾)
守护进程的创建步骤
1、 通过 fork() 创建新进程,成功后,父进程退出
2、 子进程通过 setsid() 创建新会话
3、 子进程通过 fork() 创建孙进程[成为守护进程](肯定不是会话首进程) 【无法与终端关联】
4、 孙进程修改模式 umask() , 改变目录为 "/" 【避免删除文件的影响】
5、 关闭标准输入输出和标准错误输出 【不能打印输出】
6、 重定向标准输入输出和标准错误输出 ("/dev/null")
守护进程关键点分析
- 父进程创建子进程是为了创建新会话
- 自进程创建孙进程是为了避免产生控制进程
- 孙进程不是会话首进程,因此不会关联终端
- 重定向操作可以避开奇怪的进程输出行为
守护进程深度分析
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc, char* argv[])
{
int pid = 0;
int i = 0;
if( (pid = fork()) > 0 ) // 1
{
printf("parrent = %d, ppid = %d, pgid = %d, sid = %d\n", getpid(), getppid(), getpgrp(), getsid(getpid()));
printf("child: %d\n", pid);
exit(0);
}
else if( pid == 0 )
{
setsid(); // 2
if( (pid = fork()) > 0 ) // 3
{
printf("child = %d, ppid = %d, pgid = %d, sid = %d\n", getpid(), getppid(), getpgrp(), getsid(getpid()));
printf("groundson: %d\n", pid);
exit(0);
}
if( pid == 0 )
{
// 4
umask(0);
chdir("/");
// 5
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
// 6
i += open("/dev/null", O_RDONLY); // 0 --> STDIN
i += open("/home/harmony/Documents/d.log", O_WRONLY); // 1 --> STDOUT
i += open("/dev/null", O_RDWR); // 2 --> STDERR
printf("child = %d, ppid = %d, pgid = %d, sid = %d\n", getpid(), getppid(), getpgrp(), getsid(getpid()));
while( 1 )
{
// do something
printf("i = %d\n", i++);
sleep(1);
fflush(stdout);
}
}
}
else
{
printf("fork error...\n");
}
return 0;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。