fork执行有关的逻辑代码:注意fork子进程返回值0
((fork()&&fork())||fork()&&fork()); 7个进程
可以用printf("每个实际用户ID的最大进程数=%d\n", sysconf(_SC_CHILD_MAX));
fork失败的可能性:
a) 系统中进程太多
b) 缺省情况 最大pid :32767
c) 每个用户都有一个允许的开启总数:printf("每个实际用户ID的最大进程数=%d\n", sysconf(_SC_CHILD_MAX));
普通进程:a)进程有对应终端,终端退出, 进程也退出
b)终端被占住了, 你输入各种命令这个终端都没反应
守护进程:一种长期运行进程,在后台运行, 不和任何终端关联
a)生存周期长, 操作系统启动时, 启动, 操作系统关闭时, 关闭
b)守护进程和终端无关联, 控制终端退出 不会导致守护进程退出
c)后台运行,不占终端
d)linux本身有很多守护进程在运行
ps -efj
ppid=0的是内核进程, 跟随系统一起启动
带中括号的都是内核守护进程
老祖init, 也是系统守护进程,辅助他负责启动各运行层次的系统服务
cmd队列不带中括号的为普通守护进程,多数都是进程组组长
共同点总结:大多数守护进程都是以超级用户特权运行
守护进程大多数没有终端
内核守护进程以无控制终端启动
普通守护进程可能是守护进程调用了setsid的结果(无控制终端)
守护进程编写规则:
a)调用umask(0):是一个函数, 用来限制一些文件的权限
b)fork()一个子进程出来, 然后父进程退出, 为了把终端空出来,干其他事
//fork的目的是想成功调用setsid来建立新会话, 子进程有单独的sid,而且子进程也成为了一个新的进程组 //的组长, 同时,子进程不关联任何终端了。
c)守护进程虽然可以通过终端启动, 但是不挂钩,也可以通过系统初始化脚本启动, 守护进程在后台运行, 不应该从键盘上接受东西,也不应该把东西输入到屏幕,所以要把守护进程的标准输入输出重定向到空设备。
以下为重定向代码:dup2:复制文件描述符 像指针复制一样, 把左边给右边
//int fd;
//fd = open("/dev/null", 0_RDWR);
// dup2(fd, STDIN_FILENO);
// dup2(fd, STDOUT_FILENO);
// if(fd>STDERR_FILENO)
// close(fd); //等价于fd=null
-----------------------------------------------
一些概念:
文件描述符:是一个正数,用来表示一个文件。当你打开一个存在的文件,操作系统都会返回一个文件描述符 后续对这个文件的操作的一些函数, 都会用到这个文件描述符作为参数
0:一般表示标准输入(键盘) 对于的符号位STDIN_FILENO
1:标准输出【屏幕】 STDOUT_FILENO
2:标准错误【屏幕】STDERR_FILENO
类Unix操作系统默认 从STDIN_FILENO读数据,向STDOUT_FILENO写数据, 向STDERR_FILENO写错 误
类Unix有个说法,一切皆文件,它把0,1,2都看成文件,即像看待文件,操作文件一样,看待 0,1,2,操作 0,1,2
同时,你程序一旦运行起来, 这三个文件自动打开, 0,1,2会自动打开,自动指向对应设备
例:write(STDOUT_FILENO, "aaabbb", 6);
输出重定向:
输出重定向:我标准输出文件描述符不指向屏幕,例如指向(重定向)一个文件
重定向在命令行中 用>即可
例如:ls -la > myfileout 本该在屏幕上显示的内容被写到了myfileout里去了
输入重定向:一般用< 例如:cat单独使用 即输出你用键盘输入的
cat < myfileout 即为重定向
综合:cat < myfilein > myoutfile
空设备: /dev/null (也是一个文件, 设备文件):丢弃一切写入的数据
守护进程实现范例:
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<sys/stat.h>
#include<fcntl.h>
int ngx_daemon()
{
int fd;
switch(fork())
{
case -1:
//创建子进程失败, 可以写日志。。。
return -1;
case 0:
//子进程 直接break
break;
default:
//父进程直接退出
exit(0);
}
//只有子进程可以走到这里
if(setsid() == -1)//脱离终端 终端关闭, 将跟此进程无关
{
//记录错误的日志。。。
return -1;
}
umask(0); //设置为0, 不要让他限制文件的权限
fd = open("/dev/null", O_RDWR); //打开空设备, 一读写方式打开
if(fd == -1)
{
//记录错误日志
return -1;
}
if(dup2(fd, STDIN_FILENO) == -1)
{
//记录错误日志
return -1;
}
if(dup2(fd, STDOUT_FILENO) == -1) //先关闭STDOUT_FILENO 在复制
{
//记录错误日志
return -1;
}
if(fd > STDERR_FILENO)
{
if(close(fd) == -1)
{
//释放这个文件描述符, 不然他会一直占着资源
return -1;
}
}
return 1;
}
//创建守护进程, 成功返回1
int main()
{
if(ngx_daemon() !=1)
{
//创建失败,可以写入日志
return 1;
}
else
{
for(;;)
{
sleep(1);
printf("休息一秒, 进程id=%d\n", getpid());//这里打印也没有,因为输出已经重定向
}
}
return 0;
}
守护进程不会受到的信号:内核发给你, 另外的进程发给你的
守护进程不会收到来自内核的SIGHUP信号
很多守护进程把这个信号作为通知信号,表示配置文件已经发生改动,守护进程应该重新读入其配置文件:sudo ./nginx -s reload
守护进程也不会受到来自内核的SIGINT(ctrl+c), SIGWINCH(终端窗口大小改变信号)信号
守护进程和后台进程的区别:
守护进程和终端不挂钩,后台可以输出,和终端挂钩
守护进程关闭终端不受影响, 后台进程会退出