一、 进程关系
1、会话
承载进程组 打开一个终端就是创建会话,在同一个终端里面打开的多个进程在同一个会话中会话、getsid、setsid函数
getsid:返回调用进程的会话ID
setsid:创建一个会话,并以自己的ID设置进程组ID,同时也是新会话的ID。
#include <sys/types.h>
#include <unistd.h>
pid_t getsid(pid_t pid);
pid_t setsid(void);
2、进程组
承载进程 getpgid(2) / getpgrp(2)
#include <sys/types.h>
#include <unistd.h>
pid_t getpgid(pid_t pid);
pid_t getpgrp(void);
pid_t getpgrp(pid_t pid);
获取当前进程的进程组ID ,如果成功,setpgid()和setpgrp()返回0。出现错误时,返回-1,并适当地设置errno。
3、进程
运行的一个程序
二、日志
1、与syslog创建连接---》syslog写日志,我们只是提交
`void openlog(const char *ident, int option, int faci)`openlog(),系统信息记录
2、提交日志
`void syslog(int level, const char *format, ...)` syslog() 函数简单解析
3、断开连接
`void closelog()`
三、守护进程
1、守护进程的创建
进程组组长,会话组长,脱离控制终端
PID == PGID == SID
构建守护进程:
1. umask(0) 给出最大的创建权限程序中umask(0)的作用
2. chdir("/") 用于改变当前工作目录
3. fork() 父进程终止
4. 子进程(非组长进程)调用setsid()
5. 0, 1, 2重定向到"/dev/null"
6. 关闭不必要的文件描述符
2、练习
练习中包含了日志和守护进程两个部分
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <syslog.h>
#define BUFSIZE 64
#define PATH "/tmp/out"
/*
守护进程的创建
*/
const char *currentTm();
static int daemon_init();
int main(void)
{
const char *tm;
FILE *fp;
// 创建链接
openlog("mydaemon", LOG_PID | LOG_PERROR, LOG_DAEMON);
if (-1 == daemon_init()) {
// fprintf(stderr, "daemon_init() failed");
syslog(LOG_ERR, "daemon_init() failed");
exit(1);
}
fp = fopen(PATH, "w");
if (NULL == fp) {
// perror("fopen()");
syslog(LOG_ERR, "fopen():%s", strerror(errno));
exit(1);
}
while (1) {
tm = currentTm();
fputs(tm, fp);
fputs("\n", fp);
fflush(NULL);
syslog(LOG_INFO, "%s write into %s", tm, PATH);
sleep(1);
}
closelog();
fclose(fp);
return 0;
}
/*
获得当前时间字符串
*/
const char *currentTm()
{
time_t tm;
struct tm *tmp;
static char buf[BUFSIZE] = {};
time(&tm);
tmp = localtime(&tm);
strftime(buf, BUFSIZE, "%Y/%m/%d %H:%M:%S", tmp);
return buf;
}
static int daemon_init()
{
pid_t pid;
int fd;
umask(0);
chdir("/");
pid = fork();
if (-1 == pid) {
perror("fork()");
return -1;
}
if (pid > 0) {
// parent
_exit(0);
}
// child--->非组长
if (-1 == setsid()) {
perror("setsid()");
return -1;
}
// PID == PGID == SID 脱离控制终端
fd = open("/dev/null", O_RDWR);
if (-1 == fd) {
perror("open()");
return -1;
}
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
if (fd > 2)
close(fd);
return 0;
}
四、收尸
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *wstatus);
pid_t waitpid(pid_t pid, int *wstatus, int options);
pid < -1 pid绝对值这个进程组组内任意一个子进程收尸
pid == -1 相当于wait
pid == 0 与父进程在同一个进程组内的任意一个子进程收尸
pid > 0 指定子进程收尸
五、终止处理程序
#include <stdlib.h>
int atexit(void (*function)(void));
1、何时调用:
1. main return
2. exit(2)
2、调用规则
注册的反序调用
练习:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
static void exit_handler1(void)
{
printf("%s is called\n", __FUNCTION__);
}
static void exit_handler2(void)
{
printf("%s is called\n", __FUNCTION__);
}
int main(void)
{
pid_t pid;
int n = 100;
// 注册进程的终止处理程序:主要工作,释放,关闭文件....
atexit(exit_handler1);
atexit(exit_handler2);
printf("hello world");
// create
pid = fork();
if (-1 == pid) {
perror("fork()");
exit(EXIT_FAILURE); // 1
}
// 两进程同时执行
if (0 == pid) {
// child
sleep(1);
printf("child pid:%d, ppid:%d, pgid:%d, sid:%d\n", \
getpid(), getppid(), getpgrp(), getsid(0));
printf("child n:%d\n", n);
n++;
printf("child ++ n:%d\n", n);
exit(0);
}
// 把子进程独立
setpgid(pid, pid);
// 进程收尸
wait(NULL);
// parent
printf("parent pid:%d, ppid:%d, pgid:%d, sid:%d\n", \
getpid(), getppid(), getpgrp(), getsid(0));
printf("parent n:%d\n", n);
return 0;
}