Advanced Programming in UNIX Environment Episode 39

本文探讨了信号处理机制中同步信号安全函数的应用,以及SIGCLD与SIGCHLD信号的不同语义,特别是在处理子进程状态变化时的行为差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Reentrant Functions

When a signal that is being caught is handled by a process, the normal sequence of instructions being executed by the process is temporarily interrupted by the signal handler. The process then continues executing, but the instructions in the signal handler are now executed.

The Single UNIX Specification specifies the functions that are guaranteed to be safe to call from within a signal handler. These functions are reentrant and are called async-signal safe by the Single UNIX Specification. Besides being reentrant, they block any signals during operation if delivery of a signal might cause inconsistencies.

#include "apue.h"
#include <pwd.h>

static void my_alarm(nt signo)
{
    struct passwd *rootptr;

    printf("in signal handler\n");
    if((rootptr=getpwnam("root"))==NULL)
        err_sys("getpwnam(root) error");
    alarm(1);
}

int main(void)
{
    struct passwd *ptr;

    signal(SIGALRM, my_alarm);
    alarm(1);
    for( ; ; )
    {
        if((ptr=getpwnam("sar"))==NULL)
            err_sys("getpwnam error");
        if(strcmp(ptr->pw_name,"sar")!=0)
            printf("return value corrupted! pw_name=%s\n", ptr->pw_name);
    }
}

Call a nonreentrant function from a signal handler

SIGCLD Semantics

Two signals that continually generate confusion are SIGCLD and SIGCHLD. The name SIGCLD (without the H) is from System V, and this signal has different semantics from the BSD signal, named SIGCHLD. The POSIX.1 signal is also named SIGCHLD.

The semantics of the BSD SIGCHLD signal are normal, in the sense that its semantics are similar to those of all other signals. When the signal occurs, the status of a child has changed, and we need to call one of the wait functions to determine what has happened.

System V, however, has traditionally handled the SIGCLD signal differently from other signals. SVR4-based systems continue this questionable tradition (i.e., compatibility constraint) if we set its disposition using either signal or sigset (the older, SVR3-compatible functions to set the disposition of a signal). This older handling of SIGCLD consists of the following behavior:

1.If the process specifically sets its disposition to SIG_IGN, children of the calling
process will not generate zombie processes.

POSIX.1 does not specify what happens when SIGCHLD is ignored, so this behavior is allowed. The XSI option requires this behavior to be supported for SIGCHLD.
4.4BSD always generates zombies if SIGCHLD is ignored. If we want to avoid zombies, we have to wait for our children. With SVR4, if either signal or sigset is called to set the disposition of SIGCHLD to be ignored, zombies are never generated. All four platforms described in this book follow SVR4 in this behavior.
With sigaction, we can set the SA_NOCLDWAIT flag (Figure 10.16) to avoid zombies. This action is also supported on all four platforms.

2.If we set the disposition of SIGCLD to be caught, the kernel immediately checks whether any child processes are ready to be waited for and, if so, calls the SIGCLD handler.

#include "apue.h"
#include <sys/wait.h>

static void sig_cld(int);

int main(void)
{
    pid_t pid;

    if(signal(SIGCLD,sig_cld)==SIG_ERR)
        perror("signal error");
    if((pid=fork())<0)
        perror("fork error");
    else if(pid==0)
    {
        sleep(2);
        _exit(0);
    }

    pause();
    exit(0);
}

static void sig_cld(int signo)
{
    pid_t pid;
    int status;

    printf("SIGCLD received\n");

    if(signal(SIGCLD,sig_cld)=SIG_ERR)
        perror("signal error");
    if((pid=wait(&status))<0)
        perror("wait error");

    printf("pid=%d\n",pid);
}

System V SIGCLD handler that doesn’t work

FreeBSD 8.0 and Mac OS X 10.6.8 don’t exhibit this problem, because BSD-based systems generally don’t support historical System V semantics for SIGCLD. Linux 3.2.0 also doesn’t exhibit this problem, because it doesn’t call the SIGCHLD signal handler when a process arranges to catch SIGCHLD and child processes are ready to be waited for, even though SIGCLD and SIGCHLD are defined to be the same value. Solaris 10, on the other hand, does call the signal handler in this situation, but includes extra code in the kernel to avoid this problem.

POSIX.1 states that when we establish a signal handler for SIGCHLD and there exists a terminated child we have not yet waited for, it is unspecified whether the signal is generated. This allows the behavior described previously. But since POSIX.1 does not reset a signal’s disposition to its default when the signal occurs (assuming that we’re using the POSIX.1 sigaction function to set its disposition), there is no need for us to ever establish a signal handler for SIGCHLD within that handler.

Be cognizant of the SIGCHLD semantics for your implementation. Be especially
aware of some systems that #define SIGCHLD to be SIGCLD, or vice versa. Changing the name may allow you to compile a program that was written for another system, but if that program depends on the other semantics, it may not work.

Of the four platforms described in this text, only Linux 3.2.0 and Solaris 10 define SIGCLD. On these platforms, SIGCLD is equivalent to SIGCHLD.

### Python 中循环 Episode 的代码片段 在 Python 编程中,`for` 循环常用于遍历一系列的迭代对象,比如列表、元组或范围。当涉及到模拟多个 episode(例如,在强化学习环境中),可以使用 `range()` 函数来指定要执行的次数。 下面是一个简单的例子,展示了如何设置一个 for 循环来进行多次 episode 运行: ```python num_episodes = 10 # 定义想要运行的episode数量 for episode in range(num_episodes): # 使用range()函数创建从0到num_episodes-1的序列 print(f"Starting episode {episode + 1}") # 输出当前是第几个episode # 此处放置每轮episode的具体逻辑操作... print(f"Finished episode {episode + 1}\n") # 结束本轮后打印完成信息 ``` 在这个例子中,变量 `num_episodes` 设置了总的 episode 数目;而 `range(num_episodes)` 则会生成一个由整数组成的不可变序列,这些整数是从零开始直到 `num_episodes - 1` 。每次迭代时,都会更新 `episode` 变量,并可以在循环体内访问其值以跟踪进度或者作为参数传递给其他功能模块[^1]。 如果希望调整上述代码中的某些行为,可以根据具体需求更改 `num_episodes` 的数值大小或是向循环内部添加更多处理步骤。此外,还可以引入条件判断语句或其他控制结构使程序更加灵活多变。 #### 修改循环Episode的行为 假设现在有一个特定的任务需要每隔一定数量的 episodes 执行一次特殊动作,则可以通过增加额外的计数器以及相应的条件测试来实现这一点: ```python special_action_interval = 5 # 设定每隔多少个episodes触发一次特别行动 counter = 0 # 初始化辅助计数器 for episode in range(num_episodes): counter += 1 # 增加计数器 if counter % special_action_interval == 0: print(f"Performing special action at the end of episode {episode}") # 继续常规的episode流程... print("All episodes have been completed.") ``` 这里增加了两个新的元素:一个是用来记录已经过了多少个连续 episodes 的计数器 `counter`; 另外则是每当到达预设间隔(`special_action_interval`)就采取措施的一段逻辑分支。这样就可以轻松地扩展基础框架的功能而不改变原有核心部分的工作方式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值