进程的信号处理(signal),守护进程编写,进程组和会话

主要探讨四个主题:信号处理、守护进程、进程组和会话。这些都是Linux/Unix进程管理中的重要概念。下面将分别深入探讨它们的工作原理和机制。

一、信号处理(Signal)
1.1 信号的基本概念
信号是Linux系统中用于进程间通信的一种机制,它是一种异步的通知,用来通知进程发生了某个事件。信号可以由内核、其他进程或进程自身发送。

常见信号列表:
信号编号 信号名 默认动作 说明
1 SIGHUP 终止 终端挂起或控制进程终止
2 SIGINT 终止 来自键盘的中断(Ctrl+C)
3 SIGQUIT 终止+核心转储 来自键盘的退出(Ctrl+\)
9 SIGKILL 终止 强制终止信号,不可捕获或忽略
10 SIGUSR1 终止 用户自定义信号1
12 SIGUSR2 终止 用户自定义信号2
14 SIGALRM 终止 由alarm函数设置的定时器超时
15 SIGTERM 终止 终止信号,可被捕获
17 SIGCHLD 忽略 子进程状态改变
20 SIGTSTP 停止 来自键盘的停止(Ctrl+Z)
1.2 信号的处理机制
当信号发生时,内核会在进程的进程表项中设置一个标志位,这个过程称为“递送”。信号产生和递送之间的时间间隔内,信号是“未决”的。

进程可以选择以下三种方式处理信号:

忽略信号:除了SIGKILL和SIGSTOP,其他信号都可以被忽略。

捕捉信号:为信号指定一个处理函数,当信号发生时,执行该处理函数。

执行默认动作:大多数信号的默认动作是终止进程。

1.3 信号处理函数
使用sigaction函数可以设置信号处理方式。示例:

c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void signal_handler(int signo) {
if (signo == SIGINT) {
printf(“Received SIGINT\n”);
} else if (signo == SIGTERM) {
printf(“Received SIGTERM\n”);
}
}

int main() {
struct sigaction sa;

sa.sa_handler = signal_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;

// 注册信号处理函数
if (sigaction(SIGINT, &sa, NULL) == -1) {
    perror("sigaction");
    return 1;
}

if (sigaction(SIGTERM, &sa, NULL) == -1) {
    perror("sigaction");
    return 1;
}

// 无限循环,等待信号
while (1) {
    pause();
}

return 0;

}
1.4 信号的阻塞与未决
进程可以阻塞信号,被阻塞的信号将保持在未决状态,直到进程解除阻塞。使用sigprocmask可以设置进程的信号屏蔽字。

c
#include <signal.h>

int main() {
sigset_t newset, oldset;

// 初始化信号集
sigemptyset(&newset);
sigaddset(&newset, SIGINT);  // 阻塞SIGINT

// 设置信号屏蔽字
sigprocmask(SIG_BLOCK, &newset, &oldset);

// 在这段代码中,SIGINT被阻塞
// 即使按下Ctrl+C,也不会中断

// 恢复原来的信号屏蔽字
sigprocmask(SIG_SETMASK, &oldset, NULL);

return 0;

}
二、守护进程(Daemon)
2.1 守护进程的特点
守护进程是运行在后台的一种特殊进程,它独立于控制终端,通常周期性地执行某种任务或等待处理某些事件。守护进程通常在系统启动时开始运行,直到系统关闭。

守护进程的特点:

在后台运行

脱离控制终端,避免被终端信号打断

独立于用户会话,通常以root权限运行

通常用来提供服务

2.2 编写守护进程的步骤
创建子进程,父进程退出:使子进程成为孤儿进程,被init进程收养。

在子进程中创建新会话:调用setsid,使子进程成为会话组长,脱离终端。

改变当前工作目录:通常改为根目录,避免占用可卸载的文件系统。

重设文件权限掩码:umask(0),确保守护进程创建文件时有足够的权限。

关闭不需要的文件描述符:释放从父进程继承的打开文件。

处理信号:通常忽略某些信号,如SIGHUP。

2.3 守护进程示例
c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

void daemonize() {
pid_t pid;

// 1. 创建子进程,父进程退出
pid = fork();
if (pid < 0) {
    perror("fork");
    exit(1);
} else if (pid > 0) {
    exit(0);  // 父进程退出
}

// 2. 在子进程中创建新会话
if (setsid() < 0) {
    perror("setsid");
    exit(1);
}

// 3. 改变当前工作目录为根目录
if (chdir("/") < 0) {
    perror("chdir");
    exit(1);
}

// 4. 重设文件权限掩码
umask(0);

// 5. 关闭标准输入、输出、错误输出
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);

// 6. 处理信号
signal(SIGCHLD, SIG_IGN);
signal(SIGHUP, SIG_IGN);

}

int main() {
daemonize();

// 守护进程的具体工作
while (1) {
    // 在这里执行守护进程的任务
    // 例如,每隔一段时间执行某个操作
    sleep(10);
}

return 0;

}
三、进程组和会话
3.1 进程组(Process Group)
进程组是一个或多个进程的集合,每个进程组有一个唯一的进程组ID。进程组中的进程通常与同一个作业(job)相关联,可以接收相同的信号。

每个进程属于一个进程组。

进程组ID等于组长的进程ID。

组长进程可以创建新的进程组,并将其他进程加入该组。

3.2 会话(Session)
会话是一个或多个进程组的集合,通常由一次登录形成一个会话。每个会话有一个唯一的会话ID,并且有一个会话首进程(session leader),即创建会话的进程。

每个会话可以有一个控制终端(controlling terminal)。

与控制终端建立连接的会话首进程称为控制进程(controlling process)。

一个会话中的进程组可以分为一个前台进程组和多个后台进程组。

3.3 相关函数
setsid():创建一个新会话,并成为会话首进程和进程组长,同时脱离控制终端。

setpgid(pid_t pid, pid_t pgid):将指定进程加入指定进程组,或创建新的进程组。

getpgrp():返回当前进程的进程组ID。

getsid(pid_t pid):返回指定进程的会话ID。

3.4 示例:创建新会话和进程组
c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main() {
pid_t pid;

// 创建子进程
pid = fork();
if (pid < 0) {
    perror("fork");
    return 1;
} else if (pid == 0) {
    // 子进程中创建新会话
    if (setsid() < 0) {
        perror("setsid");
        return 1;
    }
    
    printf("Child: PID=%d, PGID=%d, SID=%d\n", 
           getpid(), getpgrp(), getsid(0));
    
    // 子进程的工作
    sleep(10);
} else {
    // 父进程
    printf("Parent: PID=%d, PGID=%d, SID=%d\n", 
           getpid(), getpgrp(), getsid(0));
    
    sleep(5);
}

return 0;

}
四、深入探讨工作原理和机制
4.1 信号处理的底层机制
当信号被发送给进程时,内核会在进程的进程控制块(PCB)中设置一个标志位。当进程从内核态返回到用户态时,内核会检查进程的未决信号,并调用相应的信号处理函数。信号处理函数在用户态执行,但内核为其设置了特殊的栈帧,使得信号处理函数返回后,进程可以继续原来的执行。

4.2 守护进程与系统服务
现代Linux系统使用systemd来管理守护进程。systemd提供了更强大的服务管理功能,包括依赖关系、并行启动、按需启动等。编写systemd服务需要创建一个.service文件,放在/etc/systemd/system/目录下。

4.3 进程组和会话与终端的关系
终端驱动程序会将输入和信号发送给前台进程组。当用户按下Ctrl+C时,终端驱动程序会向前台进程组发送SIGINT信号。同样,Ctrl+Z发送SIGTSTP信号。这使得用户可以通过终端控制作业的执行。

4.4 实际应用:实现一个简单的shell
一个简单的shell需要管理作业(jobs),包括前台作业和后台作业。这需要使用进程组和会话。当shell运行一个前台作业时,它会将作业放入一个新的进程组,并设置为前台进程组。当作业结束时,shell会重新获得控制权。

下面对进程信号处理、守护进程、进程组与会话:深度解析

一、信号处理(Signal)深度解析

1.1 信号的本质与分类

1.1.1 信号的完整生命周期
信号生命周期(从产生到处理):
┌─────────────────────────────────────────┐
│          信号产生(Generation)          │
│  1. 硬件异常(除零、内存访问错误)        │
│  2. 软件事件(定时器到期、子进程终止)     │
│  3. 终端事件(Ctrl+C, Ctrl+Z)          │
│  4. 其他进程发送(kill, sigqueue)       │
└───────────────────┬─────────────────────┘
                    │
                    ▼
┌─────────────────────────────────────────┐
│          信号递送(Delivery)            │
│  内核检查目标进程的信号屏蔽字            │
│  如果信号被阻塞,标记为未决(Pending)    │
│  如果未被阻塞,立即递送                   │
└───────────────────┬─────────────────────┘
                    │
                    ▼
┌─────────────────────────────────────────┐
│          信号处理(Handling)            │
│  1. 忽略(Ignore)                      │
│  2. 捕获(Catch):执行用户处理函数       │
│  3. 默认(Default):执行系统默认动作     │
└─────────────────────────────────────────┘

关键状态:
- 产生(Generated):信号已发生但未处理
- 未决(Pending):信号已产生但被阻塞
- 递送(Delivered):信号已发送给进程
- 捕获(Caught):信号被用户处理函数处理
1.1.2 Linux信号分类表
// 标准信号(1-31)和实时信号(34-64)
#define _NSIG 65  // 信号总数

// 信号宏定义(部分)
#define SIGHUP     1   // 终端挂起或控制进程终止
#define SIGINT     2   // 键盘中断(Ctrl+C)
#define SIGQUIT    3   // 键盘退出(Ctrl+\)
#define SIGILL     4   // 非法指令
#define SIGTRAP    5   // 跟踪/断点陷阱
#define SIGABRT    6   // 异常终止
#define SIGBUS     7   // 总线错误
#define SIGFPE     8   // 浮点异常
#define SIGKILL    9   // 强制终止(不可捕获)
#define SIGUSR1   10   // 用户自定义信号1
#define SIGSEGV   11   // 段错误
#define SIGUSR2   12   // 用户自定义信号2
#define SIGPIPE   13   // 管道破裂
#define SIGALRM   14   // 定时器超时
#define SIGTERM   15   // 终止信号
#define SIGCHLD   17   // 子进程状态改变
#define SIGCONT   18   // 继续执行(如果停止)
#define SIGSTOP   19   // 停止进程(不可捕获)
#define SIGTSTP   20   // 终端停止信号(Ctrl+Z)
#define SIGTTIN   21   // 后台进程尝试读终端
#define SIGTTOU   22   // 后台进程尝试写终端
#define SIGURG    23   // 紧急数据到达socket
#define SIGXCPU   24   // CPU时间超限
#define SIGXFSZ   25   // 文件大小超限
#define SIGVTALRM 26   // 虚拟定时器超时
#define SIGPROF   27   // 性能分析定时器超时
#define SIGWINCH  28   // 窗口大小改变
#define SIGIO     29   // I/O就绪
#define SIGPWR    30   // 电源故障
#define SIGSYS    31   // 非法系统调用

// 实时信号(34-64)
#define SIGRTMIN  34
#define SIGRTMAX  64

// 信号标志
#define SA_NOCLDSTOP  0x00000001  // SIGCHLD不报告停止
#define SA_NOCLDWAIT  0x00000002  // 不创建僵尸进程
#define SA_SIGINFO    0x00000004  // 使用sa_sigaction而不是sa_handler
#define SA_ONSTACK    0x08000000  // 使用备用信号栈
#define SA_RESTART    0x10000000  // 系统调用自动重启
#define SA_NODEFER    0x40000000  // 不阻塞当前信号
#define SA_RESETHAND  0x80000000  // 处理一次后恢复默认

1.2 信号处理的内核机制

1.2.1 内核中的信号数据结构
// Linux内核信号相关数据结构
// task_struct中的信号相关字段
struct task_struct {
    // ... 其他字段
    
    /* Signal handlers: */
    struct signal_struct *signal;
    struct sighand_struct *sighand;
    sigset_t blocked;           // 阻塞的信号集
    sigset_t real_blocked;
    struct sigpending pending;  // 未决信号队列
    
    // ... 其他字段
};

// 信号描述符(每个进程共享)
struct signal_struct {
    atomic_t        count;      // 引用计数
    struct sigpending shared_pending;  // 共享未决信号
    
    // 资源限制
    unsigned long rlim[RLIM_NLIMITS];
    
    // 进程组和会话
    struct pid *leader_pid;
    struct tty_struct *tty;
    
    // 统计信息
    unsigned long long cutime, cstime;  // 子进程用户/系统时间
    unsigned long long utime, stime;    // 本进程用户/系统时间
    
    // 信号处理
    int group_exit_code;
    int group_stop_count;
    unsigned int flags;
};

// 信号处理描述符
struct sighand_struct {
    atomic_t        count;      // 引用计数
    struct k_sigaction action[_NSIG];  // 信号处理数组
    spinlock_t      siglock;    // 保护信号处理
    wait_queue_head_t signalfd_wqh;
};

// 未决信号队列
struct sigpending {
    struct list_head list;      // 未决信号链表
    sigset_t signal;            // 信号位图
};

// 内核信号动作
struct k_sigaction {
    struct sigaction sa;
#ifdef __ARCH_HAS_KA_RESTORER
    __sigrestore_t ka_restorer;
#endif
};

// 用户空间sigaction结构
struct sigaction {
    union {
        __sighandler_t sa_handler;         // 简单处理函数
        void (*sa_sigaction)(int, siginfo_t *, void *);  // 扩展处理函数
    } __sigaction_handler;
    
    unsigned long sa_flags;
    __sigrestore_t sa_restorer;
    sigset_t sa_mask;  // 执行处理函数时阻塞的信号
};
1.2.2 信号递送的核心流程
// 内核信号递送核心代码(简化版)
void signal_deliver(struct task_struct *tsk, struct k_sigaction *ka,
                    int sig, siginfo_t *info, sigset_t *oldset) {
    struct pt_regs *regs = task_pt_regs(tsk);
    
    // 1. 设置用户栈帧
    if (ka->sa.sa_flags & SA_SIGINFO) {
        // 使用siginfo版本
        if (setup_rt_frame(sig, ka, info, oldset, regs) < 0)
            return;
    } else {
        // 简单版本
        if (setup_frame(sig, ka, oldset, regs) < 0)
            return;
    }
    
    // 2. 设置信号处理函数地址
    regs->ARM_ip = (unsigned long)ka->sa.sa_handler;
    
    // 3. 设置返回地址(从信号处理返回后执行的地址)
    regs->ARM_lr = (unsigned long)ka->sa.sa_restorer;
    
    // 4. 设置程序计数器指向信号处理函数
    regs->ARM_pc = regs->ARM_ip;
    
    // 5. 清除阻塞标志(除非指定SA_NODEFER)
    if (!(ka->sa.sa_flags & SA_NODEFER)) {
        sigdelset(&tsk->blocked, sig);
    }
    
    // 6. 如果指定SA_ONESHOT,恢复默认处理
    if (ka->sa.sa_flags & SA_RESETHAND) {
        ka->sa.sa_handler = SIG_DFL;
    }
}

// 设置信号栈帧(x86-64架构示例)
int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
                   sigset_t *set, struct pt_regs *regs) {
    struct rt_sigframe __user *frame;
    void __user *restorer;
    int err = 0;
    
    // 在用户栈上分配帧空间
    frame = get_sigframe(ka, regs, sizeof(*frame));
    
    if (!access_ok(frame, sizeof(*frame)))
        return -EFAULT;
    
    // 设置ucontext
    err |= __put_user(0, &frame->uc.uc_flags);
    err |= __put_user(NULL, &frame->uc.uc_link);
    err |= save_altstack(&frame->uc.uc_stack, regs->sp);
    
    // 保存寄存器状态
    err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, set->sig[0]);
    
    // 保存siginfo
    err |= copy_siginfo_to_user(&frame->info, info);
    
    // 设置返回地址
    restorer = ka->sa.sa_restorer;
    if (!restorer)
        restorer = VDSO_SYMBOL(current->mm->context.vdso, rt_sigreturn);
    
    err |= __put_user(restorer, &frame->pretcode);
    
    // 设置用户栈指针和指令指针
    regs->sp = (unsigned long)frame;
    regs->ip = (unsigned long)ka->sa.sa_handler;
    regs->ax = sig;  // 信号编号作为第一个参数
    regs->si = (unsigned long)&frame->info;  // siginfo作为第二个参数
    regs->dx = (unsigned long)&frame->uc;    // ucontext作为第三个参数
    
    return err;
}

1.3 高级信号处理技术

1.3.1 信号掩码与阻塞
// 信号掩码操作完整示例
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

void print_signal_mask(const char *msg) {
    sigset_t curr_mask;
    
    // 获取当前信号掩码
    if (sigprocmask(SIG_BLOCK, NULL, &curr_mask) == -1) {
        perror("sigprocmask");
        return;
    }
    
    printf("%s: ", msg);
    for (int i = 1; i < NSIG; i++) {
        if (sigismember(&curr_mask, i)) {
            printf("%d ", i);
        }
    }
    printf("\n");
}

int main() {
    sigset_t new_mask, old_mask, pending_mask;
    
    printf("PID: %d\n", getpid());
    
    // 初始化信号集
    sigemptyset(&new_mask);
    sigaddset(&new_mask, SIGINT);
    sigaddset(&new_mask, SIGQUIT);
    sigaddset(&new_mask, SIGUSR1);
    
    // 阻塞SIGINT, SIGQUIT, SIGUSR1
    if (sigprocmask(SIG_BLOCK, &new_mask, &old_mask) == -1) {
        perror("sigprocmask");
        return 1;
    }
    
    print_signal_mask("Blocked signals");
    
    // 睡眠期间,发送的信号将被阻塞
    printf("Sleeping for 10 seconds...\n");
    printf("Try sending signals: kill -INT %d\n", getpid());
    printf("                     kill -QUIT %d\n", getpid());
    printf("                     kill -USR1 %d\n", getpid());
    
    sleep(10);
    
    // 检查未决信号
    sigpending(&pending_mask);
    printf("Pending signals: ");
    for (int i = 1; i < NSIG; i++) {
        if (sigismember(&pending_mask, i)) {
            printf("%d ", i);
        }
    }
    printf("\n");
    
    // 恢复原来的信号掩码
    if (sigprocmask(SIG_SETMASK, &old_mask, NULL) == -1) {
        perror("sigprocmask");
        return 1;
    }
    
    print_signal_mask("Restored signal mask");
    
    // 现在信号将被递送
    printf("Signals will be delivered now...\n");
    sleep(5);
    
    return 0;
}
1.3.2 信号处理函数的安全实践
// 异步信号安全的信号处理函数
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

// 全局标志,由信号处理函数设置
volatile sig_atomic_t flag = 0;

// 异步信号安全的处理函数
void signal_handler(int sig) {
    // 只设置标志,不做其他事情
    flag = 1;
    
    // 可以安全地使用write
    const char msg[] = "Signal received\n";
    write(STDOUT_FILENO, msg, sizeof(msg) - 1);
}

// 非安全的处理函数(演示问题)
void unsafe_handler(int sig) {
    // printf不是异步信号安全的!
    printf("Received signal %d\n", sig);  // 危险!
    
    // malloc/free也不是安全的
    char *buf = malloc(100);  // 危险!
    if (buf) {
        sprintf(buf, "Signal %d", sig);  // sprintf也不是安全的
        free(buf);  // 危险!
    }
}

// 主程序:安全的信号处理模式
int main() {
    struct sigaction sa;
    
    // 设置信号处理
    sa.sa_handler = signal_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    
    if (sigaction(SIGINT, &sa, NULL) == -1) {
        perror("sigaction");
        return 1;
    }
    
    if (sigaction(SIGTERM, &sa, NULL) == -1) {
        perror("sigaction");
        return 1;
    }
    
    // 主循环:检查标志而不是在信号处理函数中做复杂工作
    while (1) {
        // 阻塞所有信号以避免竞态条件
        sigset_t all_signals, old_signals;
        sigfillset(&all_signals);
        sigprocmask(SIG_BLOCK, &all_signals, &old_signals);
        
        if (flag) {
            // 重置标志
            flag = 0;
            
            // 在这里执行实际工作(在信号被阻塞的情况下)
            printf("Processing signal...\n");
            // 可以安全地使用非异步安全的函数
        }
        
        // 恢复信号掩码
        sigprocmask(SIG_SETMASK, &old_signals, NULL);
        
        // 休眠(可能被信号中断)
        pause();  // 等待信号
    }
    
    return 0;
}
1.3.3 实时信号与排队信号
// 实时信号(排队信号)示例
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

// 实时信号处理函数
void rt_signal_handler(int sig, siginfo_t *info, void *context) {
    // 可以获取发送者的PID和数据
    printf("Received real-time signal %d\n", sig);
    printf("  From PID: %d\n", info->si_pid);
    printf("  Value: %d\n", info->si_value.sival_int);
    printf("  Code: %d\n", info->si_code);
    
    // 可以使用用户数据
    if (info->si_code == SI_QUEUE) {
        printf("  This is a queued signal with data\n");
    }
}

int main() {
    struct sigaction sa;
    union sigval value;
    
    printf("PID: %d\n", getpid());
    
    // 设置实时信号处理
    sa.sa_sigaction = rt_signal_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_SIGINFO | SA_RESTART;  // 重要:必须使用SA_SIGINFO
    
    // 注册实时信号处理
    if (sigaction(SIGRTMIN, &sa, NULL) == -1) {
        perror("sigaction");
        return 1;
    }
    
    if (sigaction(SIGRTMIN + 1, &sa, NULL) == -1) {
        perror("sigaction");
        return 1;
    }
    
    // 如果是父进程,发送排队信号
    pid_t pid = fork();
    if (pid == -1) {
        perror("fork");
        return 1;
    }
    
    if (pid > 0) {
        // 父进程:发送多个排队信号
        sleep(1);  // 给子进程时间设置信号处理
        
        for (int i = 0; i < 5; i++) {
            value.sival_int = i * 100;
            
            printf("Parent: sending signal with value %d\n", value.sival_int);
            
            // 发送排队信号(实时信号可以排队)
            if (sigqueue(pid, SIGRTMIN, value) == -1) {
                perror("sigqueue");
            }
            
            usleep(100000);  // 100ms间隔
        }
        
        // 等待子进程
        wait(NULL);
    } else {
        // 子进程:接收信号
        printf("Child: waiting for signals...\n");
        
        // 实时信号会被排队,所以会按顺序处理
        while (1) {
            pause();  // 等待信号
        }
    }
    
    return 0;
}

二、守护进程(Daemon)深度解析

2.1 守护进程的完整创建流程

2.1.1 传统UNIX守护进程创建
// 完整的守护进程创建函数
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <syslog.h>

int become_daemon(int flags) {
    int maxfd, fd;
    
    // 1. 创建子进程,父进程退出
    switch (fork()) {
        case -1: return -1;  // 失败
        case 0:  break;      // 子进程继续
        default: _exit(EXIT_SUCCESS);  // 父进程退出
    }
    
    // 2. 成为新会话的首进程,脱离控制终端
    if (setsid() == -1)
        return -1;
    
    // 3. 确保不是会话首进程(防止获取控制终端)
    switch (fork()) {
        case -1: return -1;
        case 0:  break;
        default: _exit(EXIT_SUCCESS);
    }
    
    // 4. 清除文件创建掩码
    if (!(flags & BD_NO_UMASK0))
        umask(0);
    
    // 5. 改变工作目录到根目录
    if (!(flags & BD_NO_CHDIR))
        if (chdir("/") == -1)
            return -1;
    
    // 6. 关闭所有打开的文件描述符
    if (!(flags & BD_NO_CLOSE_FILES)) {
        maxfd = sysconf(_SC_OPEN_MAX);
        if (maxfd == -1)  // 如果限制不确定,假设为1024
            maxfd = 1024;
        
        for (fd = 0; fd < maxfd; fd++)
            close(fd);
    }
    
    // 7. 重新打开标准文件描述符到/dev/null
    if (!(flags & BD_NO_REOPEN_STD_FDS)) {
        close(STDIN_FILENO);
        
        fd = open("/dev/null", O_RDWR);
        if (fd != STDIN_FILENO)  // fd应该是0
            return -1;
        
        if (dup2(STDIN_FILENO, STDOUT_FILENO) != STDOUT_FILENO)
            return -1;
        
        if (dup2(STDIN_FILENO, STDERR_FILENO) != STDERR_FILENO)
            return -1;
    }
    
    return 0;
}

// 守护进程标志
#define BD_NO_CHDIR       01    // 不改变工作目录
#define BD_NO_CLOSE_FILES 02    // 不关闭所有打开的文件
#define BD_NO_REOPEN_STD_FDS 04 // 不重新打开stdin/stdout/stderr
#define BD_NO_UMASK0      010   // 不清除umask
#define BD_MAX_CLOSE      8192  // 如果sysconf返回不确定值
2.1.2 现代守护进程的最佳实践
// 使用双重fork和syslog的现代守护进程
#include <sys/types.h>
#include <sys/stat.h>
#include <syslog.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

// 信号处理函数
static void signal_handler(int sig) {
    switch (sig) {
        case SIGHUP:
            syslog(LOG_INFO, "Received SIGHUP, reloading configuration");
            // 重新加载配置
            break;
        case SIGTERM:
            syslog(LOG_INFO, "Received SIGTERM, exiting");
            closelog();
            exit(EXIT_SUCCESS);
        default:
            syslog(LOG_WARNING, "Received unexpected signal %d", sig);
    }
}

// 设置信号处理
static void setup_signal_handlers(void) {
    struct sigaction sa;
    
    // 设置SIGHUP处理(重新加载配置)
    sa.sa_handler = signal_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;
    
    if (sigaction(SIGHUP, &sa, NULL) == -1) {
        syslog(LOG_ERR, "Failed to set up SIGHUP handler: %s", strerror(errno));
        exit(EXIT_FAILURE);
    }
    
    // 设置SIGTERM处理(优雅退出)
    if (sigaction(SIGTERM, &sa, NULL) == -1) {
        syslog(LOG_ERR, "Failed to set up SIGTERM handler: %s", strerror(errno));
        exit(EXIT_FAILURE);
    }
    
    // 忽略不必要的信号
    sa.sa_handler = SIG_IGN;
    
    if (sigaction(SIGPIPE, &sa, NULL) == -1) {
        syslog(LOG_ERR, "Failed to ignore SIGPIPE: %s", strerror(errno));
        exit(EXIT_FAILURE);
    }
    
    if (sigaction(SIGUSR1, &sa, NULL) == -1) {
        syslog(LOG_ERR, "Failed to ignore SIGUSR1: %s", strerror(errno));
        exit(EXIT_FAILURE);
    }
    
    if (sigaction(SIGUSR2, &sa, NULL) == -1) {
        syslog(LOG_ERR, "Failed to ignore SIGUSR2: %s", strerror(errno));
        exit(EXIT_FAILURE);
    }
}

// 创建PID文件
static int create_pidfile(const char *pidfile) {
    int fd;
    char pid_str[20];
    
    fd = open(pidfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd == -1) {
        syslog(LOG_ERR, "Cannot open PID file %s: %s", pidfile, strerror(errno));
        return -1;
    }
    
    snprintf(pid_str, sizeof(pid_str), "%ld\n", (long)getpid());
    
    if (write(fd, pid_str, strlen(pid_str)) != (ssize_t)strlen(pid_str)) {
        syslog(LOG_ERR, "Cannot write to PID file %s: %s", pidfile, strerror(errno));
        close(fd);
        return -1;
    }
    
    close(fd);
    return 0;
}

// 守护进程主函数
int daemonize(const char *name, const char *pidfile, int facility) {
    pid_t pid, sid;
    
    // 第一次fork
    pid = fork();
    if (pid < 0) {
        // fork失败
        return -1;
    } else if (pid > 0) {
        // 父进程退出
        exit(EXIT_SUCCESS);
    }
    
    // 子进程继续
    
    // 创建新会话,脱离控制终端
    sid = setsid();
    if (sid < 0) {
        return -1;
    }
    
    // 第二次fork,确保不是会话首进程,防止获取控制终端
    pid = fork();
    if (pid < 0) {
        return -1;
    } else if (pid > 0) {
        // 父进程退出
        exit(EXIT_SUCCESS);
    }
    
    // 孙子进程继续(真正的守护进程)
    
    // 清除文件创建掩码
    umask(0);
    
    // 改变工作目录到根目录
    if (chdir("/") < 0) {
        return -1;
    }
    
    // 关闭标准文件描述符
    close(STDIN_FILENO);
    close(STDOUT_FILENO);
    close(STDERR_FILENO);
    
    // 重新打开stdin, stdout, stderr到/dev/null
    open("/dev/null", O_RDONLY);  // stdin
    open("/dev/null", O_RDWR);    // stdout
    open("/dev/null", O_RDWR);    // stderr
    
    // 初始化syslog
    openlog(name, LOG_PID, facility);
    
    // 创建PID文件
    if (pidfile && create_pidfile(pidfile) < 0) {
        syslog(LOG_ERR, "Cannot create PID file");
        return -1;
    }
    
    // 设置信号处理
    setup_signal_handlers();
    
    syslog(LOG_INFO, "Daemon %s started with PID %ld", name, (long)getpid());
    
    return 0;
}

// 示例守护进程主程序
int main(int argc, char *argv[]) {
    const char *daemon_name = "mydaemon";
    const char *pidfile = "/var/run/mydaemon.pid";
    
    // 成为守护进程
    if (daemonize(daemon_name, pidfile, LOG_DAEMON) < 0) {
        fprintf(stderr, "Failed to daemonize\n");
        exit(EXIT_FAILURE);
    }
    
    // 守护进程主循环
    syslog(LOG_INFO, "Daemon is running");
    
    while (1) {
        // 守护进程的工作
        // 例如:检查配置、处理请求等
        
        sleep(10);  // 示例:每10秒循环一次
        
        syslog(LOG_DEBUG, "Daemon heartbeat");
    }
    
    // 理论上不会到达这里
    closelog();
    return 0;
}

2.2 systemd时代的守护进程

2.2.1 systemd服务单元文件
# /etc/systemd/system/mydaemon.service
[Unit]
Description=My Custom Daemon
Documentation=man:mydaemon(8)
After=network.target
Requires=network.target

[Service]
Type=notify                      # 使用sd_notify()通知systemd
ExecStart=/usr/sbin/mydaemon     # 守护进程二进制文件
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/bin/kill -TERM $MAINPID
KillSignal=SIGTERM
KillMode=process                 # 只杀死主进程
Restart=on-failure               # 失败时重启
RestartSec=5s                    # 重启前等待5秒
TimeoutStopSec=30s               # 停止超时30秒

# 安全设置
User=daemonuser
Group=daemongroup
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/mydaemon /var/log/mydaemon

# 资源限制
LimitNOFILE=65536
LimitNPROC=512
LimitMEMLOCK=infinity

# 环境变量
Environment="MY_DAEMON_DEBUG=0"
EnvironmentFile=-/etc/default/mydaemon

[Install]
WantedBy=multi-user.target
2.2.2 使用sd-daemon库的现代守护进程
// 使用systemd的sd-daemon库的守护进程
#define _GNU_SOURCE
#include <systemd/sd-daemon.h>
#include <systemd/sd-event.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <syslog.h>
#include <errno.h>
#include <string.h>

// 事件回调函数
static int signal_handler(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) {
    switch (si->ssi_signo) {
        case SIGTERM:
            sd_notify(0, "STOPPING=1");
            sd_event_exit(sd_event_source_get_event(es), 0);
            break;
        case SIGHUP:
            sd_notify(0, "RELOADING=1");
            // 重新加载配置
            sd_notify(0, "READY=1");
            break;
        default:
            break;
    }
    return 0;
}

// 定时器回调函数
static int timer_handler(sd_event_source *es, uint64_t usec, void *userdata) {
    static int count = 0;
    count++;
    
    syslog(LOG_INFO, "Timer tick %d", count);
    
    // 发送watchdog通知(如果启用了WatchdogSec)
    sd_notify(0, "WATCHDOG=1");
    
    return 0;
}

int main(int argc, char *argv[]) {
    sd_event *event = NULL;
    sd_event_source *signal_source = NULL;
    sd_event_source *timer_source = NULL;
    sigset_t ss;
    int r;
    
    // 初始化syslog
    openlog("systemd-daemon", LOG_PID, LOG_DAEMON);
    
    // 创建事件循环
    r = sd_event_default(&event);
    if (r < 0) {
        syslog(LOG_ERR, "Failed to create event loop: %s", strerror(-r));
        goto finish;
    }
    
    // 设置信号处理
    sigemptyset(&ss);
    sigaddset(&ss, SIGTERM);
    sigaddset(&ss, SIGHUP);
    sigaddset(&ss, SIGINT);
    
    r = sd_event_add_signal(event, &signal_source, &ss, signal_handler, NULL);
    if (r < 0) {
        syslog(LOG_ERR, "Failed to add signal handler: %s", strerror(-r));
        goto finish;
    }
    
    // 添加定时器(每秒触发一次)
    r = sd_event_add_time(event, &timer_source, CLOCK_MONOTONIC, 
                          UINT64_MAX, 1000000, timer_handler, NULL);
    if (r < 0) {
        syslog(LOG_ERR, "Failed to add timer: %s", strerror(-r));
        goto finish;
    }
    
    // 通知systemd服务已启动
    sd_notify(0, 
        "READY=1\n"
        "STATUS=Daemon is running\n"
        "MAINPID=%lu",
        (unsigned long) getpid());
    
    syslog(LOG_INFO, "Daemon started with PID %ld", (long)getpid());
    
    // 运行事件循环
    r = sd_event_loop(event);
    if (r < 0) {
        syslog(LOG_ERR, "Event loop failed: %s", strerror(-r));
    }
    
finish:
    // 清理
    if (signal_source)
        sd_event_source_unref(signal_source);
    if (timer_source)
        sd_event_source_unref(timer_source);
    if (event)
        sd_event_unref(event);
    
    syslog(LOG_INFO, "Daemon stopped");
    closelog();
    
    return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}

三、进程组(Process Group)与会话(Session)

3.1 进程组与会话的基本概念

3.1.1 关系层次
进程、进程组、会话、终端的关系:
┌─────────────────────────────────────────┐
│         终端(Terminal)                │ ← 物理或伪终端
│        /dev/tty1, pts/0, etc.           │
└───────────────────┬─────────────────────┘
                    │ 控制关系
                    ▼
┌─────────────────────────────────────────┐
│         会话(Session)                 │ ← 一次登录形成一个会话
│  - 会话ID(SID)                       │
│  - 控制进程(Session Leader)          │
│  - 控制终端(Controlling Terminal)    │
│  - 前台进程组(Foreground Process Group)│
│  - 后台进程组(Background Process Group)│
└───────────────────┬─────────────────────┘
                    │ 包含关系
                    ▼
┌─────────────────────────────────────────┐
│       进程组(Process Group)           │ ← 一个作业(job)
│  - 进程组ID(PGID)                     │
│  - 组长进程(Process Group Leader)     │
└───────────────────┬─────────────────────┘
                    │ 包含关系
                    ▼
┌─────────────────────────────────────────┐
│         进程(Process)                 │
│  - 进程ID(PID)                        │
│  - 父进程ID(PPID)                     │
│  - 实际用户ID(RUID)                   │
│  - 有效用户ID(EUID)                   │
└─────────────────────────────────────────┘
3.1.2 内核数据结构
// 内核中的进程组和会话数据结构
// task_struct中的相关字段
struct task_struct {
    // ... 其他字段
    
    pid_t pid;                  // 进程ID
    pid_t tgid;                 // 线程组ID(POSIX线程)
    
    // 进程组和会话
    struct pid_link pids[PIDTYPE_MAX];  // PID链接
    struct task_struct *group_leader;   // 线程组领导者
    
    // ... 其他字段
};

// PID类型枚举
enum pid_type {
    PIDTYPE_PID,      // 进程ID
    PIDTYPE_TGID,     // 线程组ID
    PIDTYPE_PGID,     // 进程组ID
    PIDTYPE_SID,      // 会话ID
    PIDTYPE_MAX
};

// PID结构
struct pid {
    atomic_t count;
    unsigned int level;
    struct hlist_head tasks[PIDTYPE_MAX];
    struct rcu_head rcu;
    struct upid numbers[1];
};

// 用户空间可见的PID
struct upid {
    int nr;                     // PID数值
    struct pid_namespace *ns;   // 命名空间
    struct hlist_node pid_chain;
};

3.2 进程组操作与会话管理

3.2.1 创建新会话和进程组
// 创建新会话和进程组的完整示例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>

void print_ids(const char *name) {
    printf("%s: PID=%ld, PPID=%ld, PGID=%ld, SID=%ld\n",
           name,
           (long)getpid(),
           (long)getppid(),
           (long)getpgrp(),
           (long)getsid(0));
    
    // 获取控制终端
    int tty = tcgetpgrp(STDIN_FILENO);
    if (tty == -1) {
        printf("%s: No controlling terminal\n", name);
    } else {
        printf("%s: Controlling terminal PGID=%ld\n", name, (long)tty);
    }
}

int main() {
    pid_t pid;
    
    printf("Original process:\n");
    print_ids("Parent");
    
    // 创建子进程
    pid = fork();
    if (pid == -1) {
        perror("fork");
        return 1;
    }
    
    if (pid == 0) {
        // 子进程
        
        // 创建新会话
        if (setsid() == -1) {
            perror("setsid");
            exit(1);
        }
        
        printf("\nAfter setsid():\n");
        print_ids("Child (new session leader)");
        
        // 创建孙子进程(在新的会话中)
        pid_t grandchild_pid = fork();
        if (grandchild_pid == -1) {
            perror("fork in child");
            exit(1);
        }
        
        if (grandchild_pid == 0) {
            // 孙子进程
            
            // 创建新进程组(孙子进程成为组长)
            if (setpgid(0, 0) == -1) {
                perror("setpgid");
                exit(1);
            }
            
            printf("\nGrandchild in new process group:\n");
            print_ids("Grandchild (new PG leader)");
            
            // 孙子进程的工作
            sleep(5);
            exit(0);
        } else {
            // 子进程等待孙子进程
            wait(NULL);
        }
        
        exit(0);
    } else {
        // 父进程
        sleep(1);
        
        printf("\nParent after child created new session:\n");
        print_ids("Parent");
        
        // 父进程等待子进程
        wait(NULL);
    }
    
    return 0;
}
3.2.2 作业控制(Job Control)实现
// 简单的shell作业控制实现
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <termios.h>
#include <string.h>

#define MAX_JOBS 100

// 作业状态
typedef enum {
    JOB_RUNNING,
    JOB_STOPPED,
    JOB_DONE
} job_state_t;

// 作业结构
typedef struct {
    pid_t pgid;           // 进程组ID
    int job_id;           // 作业ID
    job_state_t state;    // 状态
    char *command;        // 命令字符串
} job_t;

job_t jobs[MAX_JOBS];
int next_job_id = 1;

// 添加作业
int add_job(pid_t pgid, const char *command) {
    for (int i = 0; i < MAX_JOBS; i++) {
        if (jobs[i].state == JOB_DONE || jobs[i].pgid == 0) {
            jobs[i].pgid = pgid;
            jobs[i].job_id = next_job_id++;
            jobs[i].state = JOB_RUNNING;
            jobs[i].command = strdup(command);
            return jobs[i].job_id;
        }
    }
    return -1;
}

// 更新作业状态
void update_job_status(pid_t pgid, job_state_t state) {
    for (int i = 0; i < MAX_JOBS; i++) {
        if (jobs[i].pgid == pgid) {
            jobs[i].state = state;
            break;
        }
    }
}

// 列出所有作业
void list_jobs() {
    printf("Job List:\n");
    printf("ID\tPGID\tState\tCommand\n");
    for (int i = 0; i < MAX_JOBS; i++) {
        if (jobs[i].pgid != 0 && jobs[i].state != JOB_DONE) {
            const char *state_str;
            switch (jobs[i].state) {
                case JOB_RUNNING: state_str = "Running"; break;
                case JOB_STOPPED: state_str = "Stopped"; break;
                default: state_str = "Unknown"; break;
            }
            printf("[%d]\t%ld\t%s\t%s\n",
                   jobs[i].job_id,
                   (long)jobs[i].pgid,
                   state_str,
                   jobs[i].command);
        }
    }
}

// 前台运行命令
pid_t run_foreground(const char *command) {
    pid_t pid = fork();
    
    if (pid == 0) {
        // 子进程
        
        // 创建新进程组,子进程成为组长
        setpgid(0, 0);
        
        // 将新进程组设置为前台进程组
        tcsetpgrp(STDIN_FILENO, getpgrp());
        
        // 恢复默认信号处理
        signal(SIGINT, SIG_DFL);
        signal(SIGQUIT, SIG_DFL);
        signal(SIGTSTP, SIG_DFL);
        signal(SIGTTIN, SIG_DFL);
        signal(SIGTTOU, SIG_DFL);
        
        // 执行命令
        execl("/bin/sh", "sh", "-c", command, (char *)NULL);
        
        // 如果exec失败
        perror("execl");
        exit(1);
    } else if (pid > 0) {
        // 父进程(shell)
        
        // 等待子进程的进程组
        int status;
        waitpid(-pid, &status, WUNTRACED);
        
        // 将控制终端返回给shell
        tcsetpgrp(STDIN_FILENO, getpgrp());
        
        // 检查子进程是否停止
        if (WIFSTOPPED(status)) {
            // 子进程被停止(如Ctrl+Z)
            printf("\nJob stopped\n");
            add_job(pid, command);
            update_job_status(pid, JOB_STOPPED);
        } else if (WIFSIGNALED(status)) {
            // 子进程被信号终止
            printf("\nJob terminated by signal %d\n", WTERMSIG(status));
        }
        
        return pid;
    }
    
    return -1;
}

// 后台运行命令
pid_t run_background(const char *command) {
    pid_t pid = fork();
    
    if (pid == 0) {
        // 子进程
        
        // 创建新进程组
        setpgid(0, 0);
        
        // 后台进程应该忽略终端信号
        signal(SIGINT, SIG_IGN);
        signal(SIGQUIT, SIG_IGN);
        signal(SIGTSTP, SIG_IGN);
        signal(SIGTTIN, SIG_IGN);
        signal(SIGTTOU, SIG_IGN);
        
        // 重定向标准输入从/dev/null
        int null_fd = open("/dev/null", O_RDONLY);
        if (null_fd != -1) {
            dup2(null_fd, STDIN_FILENO);
            close(null_fd);
        }
        
        // 执行命令
        execl("/bin/sh", "sh", "-c", command, (char *)NULL);
        
        perror("execl");
        exit(1);
    } else if (pid > 0) {
        // 父进程(shell)
        int job_id = add_job(pid, command);
        printf("[%d] %ld\n", job_id, (long)pid);
        return pid;
    }
    
    return -1;
}

// 继续运行作业(前台或后台)
int continue_job(int job_id, int foreground) {
    job_t *job = NULL;
    
    // 查找作业
    for (int i = 0; i < MAX_JOBS; i++) {
        if (jobs[i].job_id == job_id && jobs[i].state == JOB_STOPPED) {
            job = &jobs[i];
            break;
        }
    }
    
    if (!job) {
        printf("No such stopped job: %d\n", job_id);
        return -1;
    }
    
    // 发送SIGCONT信号继续作业
    if (kill(-job->pgid, SIGCONT) == -1) {
        perror("kill");
        return -1;
    }
    
    if (foreground) {
        // 前台继续
        job->state = JOB_RUNNING;
        
        // 将作业的进程组设置为前台进程组
        tcsetpgrp(STDIN_FILENO, job->pgid);
        
        // 等待作业完成
        int status;
        waitpid(-job->pgid, &status, WUNTRACED);
        
        // 将控制终端返回给shell
        tcsetpgrp(STDIN_FILENO, getpgrp());
        
        if (WIFSTOPPED(status)) {
            job->state = JOB_STOPPED;
            printf("\nJob %d stopped again\n", job_id);
        } else {
            job->state = JOB_DONE;
            printf("\nJob %d completed\n", job_id);
        }
    } else {
        // 后台继续
        job->state = JOB_RUNNING;
        printf("Continued job %d in background\n", job_id);
    }
    
    return 0;
}

int main() {
    char command[256];
    struct termios original_termios;
    
    // 保存原始终端设置
    tcgetattr(STDIN_FILENO, &original_termios);
    
    printf("Simple Shell with Job Control\n");
    printf("Commands:\n");
    printf("  command &          - Run in background\n");
    printf("  jobs              - List jobs\n");
    printf("  fg <job_id>       - Bring job to foreground\n");
    printf("  bg <job_id>       - Continue job in background\n");
    printf("  exit              - Exit shell\n");
    
    while (1) {
        printf("\nshell> ");
        fflush(stdout);
        
        if (!fgets(command, sizeof(command), stdin)) {
            break;
        }
        
        // 去掉换行符
        command[strcspn(command, "\n")] = '\0';
        
        if (strlen(command) == 0) {
            continue;
        }
        
        if (strcmp(command, "exit") == 0) {
            break;
        } else if (strcmp(command, "jobs") == 0) {
            list_jobs();
        } else if (strncmp(command, "fg ", 3) == 0) {
            int job_id = atoi(command + 3);
            continue_job(job_id, 1);
        } else if (strncmp(command, "bg ", 3) == 0) {
            int job_id = atoi(command + 3);
            continue_job(job_id, 0);
        } else if (command[strlen(command) - 1] == '&') {
            // 后台运行
            command[strlen(command) - 1] = '\0';
            run_background(command);
        } else {
            // 前台运行
            run_foreground(command);
        }
    }
    
    // 恢复原始终端设置
    tcsetattr(STDIN_FILENO, TCSANOW, &original_termios);
    
    return 0;
}

3.3 终端与控制进程

3.3.1 终端控制原语
// 终端控制完整示例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <signal.h>
#include <sys/ioctl.h>

// 保存和恢复终端设置
struct termios original_termios;

void restore_terminal() {
    tcsetattr(STDIN_FILENO, TCSAFLUSH, &original_termios);
}

void setup_terminal() {
    struct termios new_termios;
    
    // 获取当前终端设置
    if (tcgetattr(STDIN_FILENO, &original_termios) == -1) {
        perror("tcgetattr");
        exit(1);
    }
    
    // 设置atexit处理函数
    atexit(restore_terminal);
    
    // 配置新终端设置
    new_termios = original_termios;
    
    // 禁用规范模式(逐行处理)和回显
    new_termios.c_lflag &= ~(ICANON | ECHO);
    
    // 设置最小读取字符数和超时
    new_termios.c_cc[VMIN] = 1;   // 至少读取1个字符
    new_termios.c_cc[VTIME] = 0;  // 无超时
    
    // 应用新设置
    if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &new_termios) == -1) {
        perror("tcsetattr");
        exit(1);
    }
}

// 获取终端大小
void get_terminal_size(int *rows, int *cols) {
    struct winsize ws;
    
    if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) {
        // 如果ioctl失败,使用默认值
        *rows = 24;
        *cols = 80;
    } else {
        *rows = ws.ws_row;
        *cols = ws.ws_col;
    }
}

// 终端信号处理
void sigwinch_handler(int sig) {
    int rows, cols;
    
    // 获取新的终端大小
    get_terminal_size(&rows, &cols);
    
    printf("\rTerminal resized: %dx%d\n", cols, rows);
    fflush(stdout);
}

// 简单的终端应用程序
int main() {
    char ch;
    int rows, cols;
    
    // 设置信号处理
    signal(SIGWINCH, sigwinch_handler);
    
    // 设置终端
    setup_terminal();
    
    // 获取初始终端大小
    get_terminal_size(&rows, &cols);
    printf("Terminal size: %dx%d\n", cols, rows);
    printf("Press 'q' to quit, any other key to continue\n");
    
    while (1) {
        // 读取单个字符(非阻塞模式)
        if (read(STDIN_FILENO, &ch, 1) == 1) {
            if (ch == 'q' || ch == 'Q') {
                break;
            }
            
            printf("You pressed: %c (0x%02x)\r\n", 
                   isprint(ch) ? ch : ' ', 
                   (unsigned char)ch);
            
            // 如果是Ctrl+C,不会中断程序(因为我们处理了终端设置)
            if (ch == 0x03) {  // Ctrl+C
                printf("Ctrl+C pressed (but ignored)\r\n");
            }
            
            // 如果是Ctrl+Z,发送SIGTSTP给自己
            if (ch == 0x1a) {  // Ctrl+Z
                printf("Ctrl+Z pressed, stopping...\r\n");
                fflush(stdout);
                kill(getpid(), SIGTSTP);
                // 当进程继续时,从这里恢复
                printf("Process continued\r\n");
            }
        }
    }
    
    printf("\nExiting...\n");
    return 0;
}

四、高级主题与最佳实践

4.1 信号处理的最佳实践

4.1.1 信号处理中的竞态条件
// 避免信号处理中的竞态条件
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>

// 使用sig_atomic_t保证原子性
volatile sig_atomic_t flag = 0;
volatile sig_atomic_t processed_flag = 1;  // 初始为已处理

// 信号处理函数 - 只设置标志
void signal_handler(int sig) {
    if (processed_flag) {
        flag = 1;
        processed_flag = 0;
    }
}

// 线程安全的信号处理
void *worker_thread(void *arg) {
    sigset_t mask;
    
    // 在这个线程中阻塞所有信号
    sigfillset(&mask);
    pthread_sigmask(SIG_BLOCK, &mask, NULL);
    
    while (1) {
        // 在这里执行工作,不会被信号中断
        usleep(100000);  // 100ms
        
        // 检查是否需要处理信号(通过线程间通信)
        // ...
    }
    
    return NULL;
}

int main() {
    struct sigaction sa;
    pthread_t thread;
    sigset_t mask, oldmask;
    
    // 设置信号处理
    sa.sa_handler = signal_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    
    if (sigaction(SIGINT, &sa, NULL) == -1) {
        perror("sigaction");
        return 1;
    }
    
    if (sigaction(SIGTERM, &sa, NULL) == -1) {
        perror("sigaction");
        return 1;
    }
    
    // 创建工作者线程
    pthread_create(&thread, NULL, worker_thread, NULL);
    
    // 在主线程中处理信号
    sigemptyset(&mask);
    sigaddset(&mask, SIGINT);
    sigaddset(&mask, SIGTERM);
    
    while (1) {
        // 等待信号
        sigsuspend(&oldmask);
        
        // 当信号处理函数返回后,检查标志
        if (flag && !processed_flag) {
            // 处理信号
            printf("Processing signal...\n");
            
            // 重置标志
            flag = 0;
            processed_flag = 1;
        }
    }
    
    pthread_join(thread, NULL);
    return 0;
}

4.2 现代守护进程架构

4.2.1 基于事件循环的守护进程
// 使用libevent的现代守护进程
#include <event2/event.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>
#include <signal.h>
#include <syslog.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

static void signal_cb(evutil_socket_t fd, short event, void *arg) {
    struct event_base *base = arg;
    
    syslog(LOG_INFO, "Received signal, shutting down");
    event_base_loopbreak(base);
}

static void accept_cb(struct evconnlistener *listener,
                      evutil_socket_t fd,
                      struct sockaddr *addr,
                      int socklen,
                      void *arg) {
    struct event_base *base = arg;
    struct bufferevent *bev;
    
    bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
    if (!bev) {
        syslog(LOG_ERR, "Error creating bufferevent");
        return;
    }
    
    // 设置回调
    bufferevent_setcb(bev, NULL, NULL, NULL, NULL);
    bufferevent_enable(bev, EV_READ | EV_WRITE);
}

int main(int argc, char *argv[]) {
    struct event_base *base;
    struct evconnlistener *listener;
    struct event *signal_event;
    struct sockaddr_in sin;
    
    // 成为守护进程
    daemon(1, 0);
    
    // 打开syslog
    openlog("libevent-daemon", LOG_PID, LOG_DAEMON);
    syslog(LOG_INFO, "Daemon started");
    
    // 初始化libevent
    base = event_base_new();
    if (!base) {
        syslog(LOG_ERR, "Could not initialize libevent");
        return 1;
    }
    
    // 设置信号处理
    signal_event = evsignal_new(base, SIGINT, signal_cb, base);
    evsignal_add(signal_event, NULL);
    
    signal_event = evsignal_new(base, SIGTERM, signal_cb, base);
    evsignal_add(signal_event, NULL);
    
    // 设置监听socket
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(8888);
    
    listener = evconnlistener_new_bind(base, accept_cb, base,
                                       LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE,
                                       -1, (struct sockaddr*)&sin, sizeof(sin));
    if (!listener) {
        syslog(LOG_ERR, "Could not create listener");
        return 1;
    }
    
    syslog(LOG_INFO, "Daemon listening on port 8888");
    
    // 运行事件循环
    event_base_dispatch(base);
    
    // 清理
    evconnlistener_free(listener);
    event_base_free(base);
    
    syslog(LOG_INFO, "Daemon stopped");
    closelog();
    
    return 0;
}

五、总结

信号处理、守护进程、进程组和会话是Linux进程管理中的核心概念。理解这些概念对于编写健壮的、功能丰富的应用程序至关重要。在实际开发中,我们经常需要处理信号、编写守护进程,并通过进程组和会话来管理作业。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千江明月

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值