不落辰

知不可乎骤得,托遗响于悲风

0%

csapp-8-异常控制流2

  • 信号 signal
    信号编号、事件、默认行为、处理方式(见Linux信号)
    发送信号方式、接收信号
    阻塞、接触阻塞信号
    进程接收信号流程
    内核为每个进程维护了pending和blocked

  • 非本地跳转
    catch – setjmp、throw – longjmp

Signal 信号

  • Linux信号:更高层次,软件形式的异常。它允许进程和内核中断其他进程。
  • 定义:一个信号就是一条小消息,它通知进程系统中发生了一个某种类型的事件。
    • 信号由内核(可能是应另一个进程的要求)发送给一个进程。
    • 信号提供了一种机制,通知用户进程发生了什么类型的异常。
  • fork出的子进程会继承父进程关于signal的dispostion 如处理函数、掩码等
    1
    2
    A  child created via fork(2) inherits a copy of its parent's signal dispositions.  During an execve(2), the dispositions of handled signals are reset to the default; the
    dispositions of ignored signals are left unchanged.

信号的两个阶段

以下仅为方便自己理解

  • 被处理的信号:信号被接收
  • 发出但还没被处理的信号:待处理信号

待处理信号集 / 未决信号集:1表示待处理,即内核发送了,但该信号还没被进程接收(处理)。

阻塞/屏蔽信号集:1表示该信号目前不可被prog进程接收(处理)。
一般是因为该信号在被进程的处理程序处理,此时,若内核再发送一个该信号SIGINT,则将该信号加入待处理信号集(置1),待处理信号集不可排队(只是二进制而已);即有多个该信号发送来,也只能有一个被加入待处理信号集,其余被抛弃。

传送信号的步骤

  • kernal -> process

    发送信号

  • 如何递送信号:

    • 内核通过更新目的进程上下文中的某个状态,发送(递送)一个信号给目的进程
  • 发送信号原因

    • 内核检测到一个系统事件,比如除0错误或者子进程错误
    • 一个进程调用了kill函数,显示地要求内核发送一个信号给目的进程,一个进程可以放信号给它自己。
  • 一个进程可以发送信号给他自己

接收信号

  • 接收信号
    • 当目的进程被内核强迫以某种方式对信号的发送做出反应时,他就接收了信号。
    • 进程对信号的反应
      • 忽略 Ignore the signal (do nothing)
      • 终止 Terminate the process (with optional core dump)
      • 信号处理程序 Catch the signal by executing a user-level function called signal handler。
        • 具体信号处理程序的默认行为都是什么。下文有接收信号有。
        • Akin to a hardware exception handler being called in response to an asynchronous interrupt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Signal dispositions
Each signal has a current disposition, which determines how the process behaves when it is delivered the signal.

The entries in the "Action" column of the tables below specify the default disposition for each signal, as follows:

Term Default action is to terminate the process.

Ign Default action is to ignore the signal.

Core Default action is to terminate the process and dump core (see core(5)).

Stop Default action is to stop the process.

Cont Default action is to continue the process if it is currently stopped.

send signal 发送信号

进程组 & 会话session

  • 作业job:对一条命令求值而创建的进程。
  • 每个进程只属于一个进程组
  • 进程组是一个或者多个进程的集合
  • 会话session 是一个或者多个进程组的集合。
  • 一般情况下 session 和终端是一对一的关系,当我们打开多个终端窗口时,实际上就创建了多个 session。
  • ./a.out &。表示将命令放入后台执行。这样该命令对应的进程组即为后台进程组。
  • shell 中可以存在多个进程组,无论是前台进程组还是后台进程组,它们或多或少存在一定的联系,为了更好地控制这些进程组(或者称为作业),系统引入了会话的概念。
  • 会话(Session)的意义在于将很多的工作集中在一个终端,选取其中一个作为前台来直接接收终端的输入及信号,其他的工作则放在后台执行。
  • 一个会话的几个进程组可被分成一个前台进程组(foreground process group)以及一个或几个后台进程组(background process group)。
    • 在任意时刻,可能同时存在多个后台进程组,但是不管什么时候都只能有一个前台进程组。
    • 如果一个会话有一个控制终端,则它有一个前台进程组,其他进程组则为后台进程组。

从键盘发送信号

  • 无论何时键入中断键(常常是DELETE或Ctrl - C)或退出键(常常是Ctrl - \),内核就会将中断信号或退出信号送至前台进程组的所有进程。
    • 只有在前台进程组中进程才能在控制终端读取输入。当用户在终端输入信号生成终端字符(如 ctrl+c、ctrl+z、ctr+\等)时,内核会把对应的信号发送给前台进程组。前台进程组的输出也会显示在控制终端上。

用/bin/kill程序发送信号

  • /bin/kill program sends arbitrary signal to a process or process group
  • send a signal to a process 不是杀掉一个process!
  • DESCRIPTION
    • The default signal for kill is TERM. Use -l or -L to list available signals.
    • Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0.
    • Alternate signals may be specified in three ways: -9, -SIGKILL or -KILL.
    • Negative PID values may be used to choose whole process groups
      • A PID of -1 is special; it indicates all processes except the kill process itself and init.
  • Examples
    • /bin/kill –9 24818 :Send SIGKILL to process 24818
    • /bin/kill –9 –24817 :Send SIGKILL to every process in process group 24817

用kill函数发送信号

  • pid>0,kill函数发送信号sig给pid
  • pid=0,kill发送sig给调用进程所在进程组的每个进程
  • pid<0,kill发送给进程组|pid|中的每个进程。
    1
    2
    3
    4
    5
    6
    #include<sys/types.h>
    #include<signal.h>
    int kill(pid_t pid,int sig);
    return val
    success : 0
    fail : -1

用alarm函数发送信号

1
2
3
4
#include<unistd.h>
unsigned int alarm(unsigned int secs);
return val:
前一次闹钟剩余的秒数,若以前没有设定闹钟,则为0
  • alarm函数:安排内核在secs秒后发送一个SIGALRM信号给调用进程。
    • secs=0,则不会调度安排新的alarm

接收信号

简介

  • 内核把进程p从内核模式切换到用户模式时(例如,从系统调用返回或完成了一次上下文切换),它会检查进程p的**未被阻塞的待处理信号的集合(pending & ~blocked)**。
    • 若集合为空:内核将控制传递到p的逻辑控制流的下一条指令(I_next)
    • 若集合非空:内核选择集合中的某个信号k(通常是最小的k),并强制p接收信号k。进程反应安慰你之后,控制就传递回p的逻辑控制流中的下一条指令(I_next)
      • 信号对应的默认行为(信号处理程序默认做什么)
        • The process terminates
        • The process terminates and dumps core(转储内存)
        • The process stops until restarted by a SIGCONT signal
        • The process ignores the signal
  • SIGSTOP 和 SIGKILL的默认行为不能更改

signal

  • signal

    1
    2
    3
    4
    5
    6
    #include<signal.h>
    typedef void (*sighandler_t)(int);
    sighandler_t signal(int signum,sighandler_t handler);
    return :
    success :返回指向前次处理程序的指针
    error:SIG_ERR 且 不设置errno
    • handler = SIG_IGN ,那么忽略signum型信号
    • handler = SIG_DFL,那么signum的信号行为恢复为默认行为
    • Otherwise, handler is the address of a user-level signal handler。调用信号处理程序称为捕获信号,执行信号处理程序称为处理信号。
      • Called when process receives signal of type signum
      • Executing handler is called “catching” or “handling” the signal
      • return时控制传递给被中断的指令:When the handler executes its return statement, control passes back to instruction in the control flow of the process that was interrupted by receipt of the signal

SIGCHLD信号处理


  • 如何利用SIGCHLD 非阻塞 回收子进程
  • 下面这段代码不见得对
  • handler里面的waitpid的返回值究竟应该和0比较还是和-1比较,目前来看,我认为 如果!=-1的判断条件的话,那么while会一直轮询下去,很消耗性能,甚至还不如阻塞等待wait(null)。我认为应当是和0比较,如果>0,就轮询,==0或者=-1就不轮询了。
    • WNOHANG =-1是error ,=0是集合里剩余的所有进程还没结束
    • 默认(HANG)=-1 && errno = echild是 所有进程都被回收完毕。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      //  回收子进程比较好的方案,一定要会!!: 利用信号机制去回收子进程,防止使用execlp而无法回收子线程的情况
      // 利用的信号:SIGCHLD

      #include<unistd.h>
      #include<signal.h>
      #include<sys/wait.h>
      #include<stdio.h>
      void catch(int signo)
      {
      pid_t wpid;
      int status;
      // while((wpid=wait(&status)))
      // while((wpid=waitpid(-1,&status,0)!=-1))
      while((wpid=waitpid(-1,&status,WNOHANG))!=-1)
      {
      if(wpid==0) continue;
      if(WIFEXITED(status))
      printf("child %d exit with %d\n",wpid,WEXITSTATUS(status));
      else if(WIFSIGNALED(status))
      printf("child %d killed with %d\n",wpid,WTERMSIG(status));
      }

      return ;
      }



      int main()
      {
      pid_t pid;
      int i = 0;
      for( ;i<10;++i)
      {
      if((pid=fork())==0)
      break;
      }

      // 设置PCB中的mask屏蔽字屏蔽SIGCHLD
      sigset_t set;
      sigemptyset(&set);
      sigaddset(&set,SIGCHLD);
      sigprocmask(SIG_BLOCK,&set,NULL);

      if(i==10)
      {
      sleep(10);
      // 在进行主函数逻辑之前
      // 注册信号处理函数 来 处理捕捉信号

      // 准备act
      struct sigaction act;
      act.sa_handler = catch;
      act.sa_flags = 0;
      sigemptyset(&act.sa_mask);

      //注册捕捉信号的处理函数
      sigaction(SIGCHLD,&act,NULL);

      //解除屏蔽
      sigprocmask(SIG_UNBLOCK,&set,NULL);

      // 模拟后序逻辑
      while(1);
      }
      else
      {
      printf("i am child id = %d\n",getpid());
      return i;
      }
      return 0;
      }

      shc@shc-virtual-machine:~/code/revier/src$ ./signal_to_catch_child.out
      i am child id = 8585
      i am child id = 8586
      i am child id = 8589
      i am child id = 8588
      i am child id = 8587
      i am child id = 8590
      i am child id = 8593
      i am child id = 8591
      i am child id = 8594
      i am child id = 8592
      child 8585 exit with 0
      child 8586 exit with 1
      child 8587 exit with 2
      child 8588 exit with 3
      child 8589 exit with 4
      child 8590 exit with 5
      child 8591 exit with 6
      child 8592 exit with 7
      child 8593 exit with 8
      child 8594 exit with 9
      ^C
  • 忽略了SIGCHLD信号之后,子进程不必再手动wait回收获取信息,也没有残余资源。所以,如果不需要子进程的信息的话,应该直接忽略SIGCHLD信号。
  • 如果要忽略SIGCHLD信号,需直接写出signal(SIGCHLD,SIG_IGN)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    #include<unistd.h>
    #include<sys/wait.h>
    #include<sys/types.h>
    #include<signal.h>
    #include<stdlib.h>
    #include<stdio.h>
    #include<errno.h>


    int main()
    {
    pid_t pid;
    if((pid=fork())==0){
    sleep(5);
    exit(0);
    }

    printf("my child is %d\n",pid);

    signal(SIGCHLD,SIG_IGN); // 忽略 SIGCHLD

    sleep(10);

    int status;
    pid_t child = waitpid(pid,&status,0);

    if(child == -1 && errno == ECHILD){
    printf("SIGCHLD is connected with waitpid\n");
    }else{
    if(WIFEXITED(status)){ // true <---> child process return / exit
    printf("child %d terminated normally with exit status %d\n",child,WEXITSTATUS(status)); // return the exist status of a normally exit process
    }else{
    printf("child %d terminated abnormally\n",child);
    }
    }
    return 0;
    }

    shc@shc-virtual-machine:~/code/csapp_try/process$ ./sigchld_wait
    my child is 125233
    SIGCHLD is connected with waitpid

    $ ps ajx
    123968 124077 124077 124077 pts/9 125232 Ss 1000 0:00 /bin/bash
    123754 125153 123754 123754 ? -1 S 1000 0:00 sleep 180
    124077 125232 125232 124077 pts/9 125232 S+ 1000 0:00 ./sigchld_wait
    125232 125233 125232 124077 pts/9 125232 S+ 1000 0:00 ./sigchld_wait
    116918 125255 125255 116918 pts/1 125255 R+ 1000 0:00 ps ajx

    shc@shc-virtual-machine:~/code/csapp_try/process$ ps ajx
    PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
    0 1 1 1 ? -1 Ss 0 1:17 /sbin/init splash
    123754 125153 123754 123754 ? -1 S 1000 0:00 sleep 180
    124077 125232 125232 124077 pts/9 125232 S+ 1000 0:00 ./sigchld_wait
    116918 125267 125267 116918 pts/1 125267 R+ 1000 0:00 ps ajx

waitpid函数里面是怎么实现的呀?我觉得是先判断有没有已经死去的子进程,没有则阻塞,再等待SIGCHLD信号触发SIGCHLD的信号处理程序,处理信号 获取进程信息?

wait a signal to be caught 等待(任意)一个信号

  • Waiting for a signal to be caught
    • The following system calls suspend execution of the calling process or thread until a signal is caught (or an unhandled signal terminates the process):
    • pause(2) Suspends execution until any signal is caught.
    • sigsuspend(2) Temporarily changes the signal mask (see below) and suspends execution until one of the unmasked signals is caught.

Signal mask and pending signals

  • Signal mask and pending signals
    • Blocked阻塞 : A signal may be blocked, which means that it will not be delivered until it is later unblocked.
      • not be delivered – 没有递送到 — 没被process处理
    • pending 没处理 : Between the time when it is generated and when it is delivered a signal is said to be pending.
    • **signal mask ** : Each thread in a process has an independent signal mask, which indicates the set of signals that the thread is currently blocking.
      • signal mask 就是当前的 block set
      • sigprocmask : A thread can manipulate its signal mask using pthread_sigmask(3). In a traditional single-threaded application, sigprocmask(2) can be used to manipulate the signal mask.
      • fork and signal mask : A child created via fork(2) inherits a copy of its parent’s signal mask; the signal mask is preserved across execve(2).
    • which to deliver signal : A signal may be generated (and thus pending) for a process as a whole (e.g., when sent using kill(2)) or for a specific thread (e.g., certain signals, such as SIGSEGV and SIGFPE, generated as a consequence of executing a specific machine-language instruction are thread directed, as are signals targeted at a specific thread using pthread_kill(3)). A process-directed signal may be delivered to any one of the threads that does not currently have the signal blocked. If more than one of the threads has the signal unblocked, then the kernel chooses an arbitrary thread to which to deliver the signal.
    • A thread can obtain the set of signals that it currently has pending using sigpending(2). This set will consist of the union of the set of pending process-directed signals and the set of signals pending for the calling thread.
    • fork and pending set : A child created via fork(2) initially has an empty pending signal set; the pending signal set is preserved across an execve(2).

阻塞、解除阻塞 sigprocmask

  • sigprocmask DESCRIPTION
    • sigprocmask() is used to fetch and/or change the signal mask of the calling thread.
    • The signal mask is the set of signals whose delivery is currently blocked for the caller (see also signal(7) for more details).
    • The behavior of the call is dependent on the value of how, as follows.
  • 隐式阻塞:内核默认阻塞任何当前处理程序正在处理信号类型的待处理的信号。

    • eg: A SIGINT handler can’t be interrupted by another SIGINT
  • 显示阻塞:sigprocmask

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include<signal.h>

    int sigprocmask(int how,const sigset_t *set,sigset *oldset);

    int sigemptyset(sigset_t *set);
    // create empty set
    int sigfillset(sigset_t *set);
    // add every signal number to set
    int sigaddset(sigset_t *set,int signum);
    // add signal number to set
    int sigdelset(sigset_t *set,int signum);
    // delete signal number from set
    int sigismember(const sigset_t *set,int signum);
  • int sigprocmask(int how,const sigset_t *set,sigset *oldset);

    • 改变当前阻塞的信号集合
    • how = SIG_BLOCK:blocked = blocked | set
    • how = SIG_UNBLOCK:blocked = blocked & (~set)
    • how = SIG_SETMASK:blockded = set
    • oldset = prev_mask;
  • 例子:如何阻塞SIGINT信号

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    sigset_t mask,prev_mask;

    sigemptyset(&mask); // mask = null
    sigaddset(&mask,SIGINT); // mask = mask | SIGINT

    // blocked sigint and save previous blocked set
    sigprocmask(SIG_BLOCK,&mask,&prev_mask);
    //... Code region that will not be interrupted by SIGINT
    // **restore previous blocked set , unblocking SIGINT**
    sigprocmask(SIG_SETMASK,&prev_mask,NULL);

Safe Signal Handling 信号处理程序注意

  • 棘手
    • signal handler 和 main program concurrently 运行,sharing data structures 共享全局变量,可能互相干扰
    • 如何以及何时接收信号
    • 不通过的系统有不同的信号处理语义

Guidlines for Writing Safe Handlers CSAPP P534

  • G0: Keep your handlers as simple as possible

    • e.g., Set a global flag and return
  • G1: Call only async-signal-safe functions in your handlers

    • printf, sprintf, malloc, and exit are not safe!
    • write is safe
  • G2: Save and restore errno on entry and exit

    • So that other handlers don’t overwrite your value of errno
  • G3: Protect accesses to shared data structures by temporarily blocking all signals.

    • To prevent possible corruption
      • 从主程序访问一个data structure 需要一系列指令,如果指令序列被访问d的处理程序中断,那么前后d的状态不一致,可能会导致不可预知的结果
  • G4: Declare global variables as volatile

    • To prevent compiler from storing them in a register
  • G5: Declare global flags as volatile sig_atomic_t

    • flag: variable that is only read or wriien not incremented or updated(e.g. flag = 1, not flag++)
    • if we do that then teh system guarantees that reads and writen to that variable will be atmoic
    • So if Flag declared this way does not need to be protected like other globals
  • 不可以用信号来对其他进程中发生的事件计数。

Async-Signal-Safety 异步信号安全

  • function : async signal safety 异步信号安全
    • printf就不是异步信号安全的,printf获得一个terminal的锁。
    • 如下,main和handler里面都调用print,造成了deadlock
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      void handler(int sig){  //  handler INT
      while(1){
      // lock aquire ?
      printf("hi\n");
      }
      }

      int main()
      {
      while(1){
      // terminal lock
      printd("hello\n"); // 被信号打断,进入handler。handler里的printf还想获得锁,但是显然不可。死锁等待。
      }
      }

      write是异步信号安全的
  • Function is async-signal-safe if either reentrant (e.g., all variables stored on stack frame, CS:APP3e 12.7.2) or non-interruptible by signals.
  • Posix guarantees 117 functions to be async-signal-safe
    • Source: “man 7 signal”
    • Popular functions on the list:
      • _exit, write, wait, waitpid, sleep, kill
    • Popular functions that are not on the list:
      • printf, sprintf, malloc, exit
      • Unfortunate fact: write is the only async-signal-safe output function

Portable Signal Handling

  • Ugh! Different versions of Unix can have different signal handling semantics
    • Some older systems restore action to default after catching signal
    • Some interrupted system calls can return with errno == EINTR (慢速系统调用被中断)
    • Some systems don’t block signals of the type being handled
  • Solution: sigaction
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
       typedef void handler_t(int);
    151 /* $begin sigaction */
    152 handler_t *Signal(int signum, handler_t *handler)
    153 {
    154 struct sigaction action, old_action;
    155
    156 action.sa_handler = handler; // 指明处理函数
    157 sigemptyset(&action.sa_mask); /* Block sigs of type being handled */ // 解决有的系统不block正在被处理的信号
    158 action.sa_flags = SA_RESTART; /* Restart syscalls if possible */ // 解决慢速系统调用被中断
    159
    160 if (sigaction(signum, &action, &old_action) < 0)
    161 unix_error("Signal error");
    162 return (old_action.sa_handler);
    163 }

Synchronizing Flow to Avoid Races

Explicitly Waiting for Signals

  • Handlers for program explicitly waiting for SIGCHLD to arrive

    • while(!pid) 通过共享的全局变量global
    • correct! but wasteful!

  • Other options:

    • RACE!不可这样,可能导致一直卡在pause();
      1
      2
      3
      4
      while(!pid)
      // 如果在这里 接收了信号 hanlder处理了signal 改变了pid
      // 但是之后pause不会再被唤醒 因为没信号了。
      pause();
    • TOO SLOW!
      1
      2
      while(!pid)
      sleep(1)
  • Solution : sigsuspend

Waiting for Signals with sigsuspend

  • sigsuspend:等待任意一个信号到来
    1
    int sigsuspend(const sigset_t *mask);
    • DESCRIPTION
      • sigsuspend() temporarily replaces the signal mask of the calling process with the mask given by mask and then suspends the process until delivery of a signal whose action is to invoke a signal handler or to terminate a process.
      • If the signal terminates the process, then sigsuspend() does not return. If the signal is caught, then sigsuspend() returns after the signal handler returns, and the signal mask is restored to the state before the call to sigsuspend().
      • It is not possible to block SIGKILL or SIGSTOP; specifying these signals in mask, has no effect on the process’s signal mask.
  • 在不消耗性能的情况下,实现Explicitly Waiting for Signal

小结 内核让进程p接收信号流程

  • 内核为每个进程p维护两个向量

    • 一个是 pending —- 待处理信号的集合
    • 另一个是 blocked —- 当前阻塞的信号集合
  • 未被阻塞的待处理信号的集合:pending & ~blocked(进程p需要接收的信号)

  • 何时会有内核检查p的pending & ~blocked的契机

    • 内核把进程p从内核切换到用户模式
  • 一个signal发送给进程p时
    • p先将该信号加入pending向量中 因为仅仅是向量,所以并不支持排队
    • 然后看该信号是否被blocked(blocked集合中该信号是否置为1
      • 被blocked,则进程p不进行相应的signal handler。pending中保持该signal为1,直到相应signal handler被调用。
        • 当该signal不再被blocked时,pending中该signal又为1,故p会接收该signal(触发相应handler)
      • 没被blocked,则进程p触发相应的signal handler。pending中该signal归0,blocked向量中加入该信号,直到相应的signal handler执行完毕,blocked向量中移除该信号。

CSAPP 540-546

  • 同步流
  • 显示竞争

Nonlocal jump

  • 非本地跳转Nonlocal jump
    • 用户级的异常控制流形式
    • 通过setjmp,longjmp
    • 不需要经过正常的call-ret返回序列
    • 应用
        1. 从一个深层嵌套的函数调用中立即返回,通常是由检测到某个错误情况引起的。可以直接返回到一个普通的错误处理程序,而非费力地解开调用栈,
        1. 使得一个信号处理程序将控制传递到一个指定的代码位置,而非一定返回到被信号中断了的位置
  • C++中的catch <—> C中的setjmp
  • C++中的throw <—> C中的longjmp

setjmp

  • int setjmp(jmp_buf env)
    • 功能:指明之后的longjmp返回的位置
      • 在env缓冲区保存当前调用环境:PC、stack pointer、通用目的寄存器。
      • 必须在longjmp之前调用
      • Called once, returns one or more times
      • return 0

longjmp

  • void longjmp(jmp_buf env,int retval)
    • 功能:触发一个 从最近一次初始化env的 setjmp 的返回
      • 从env缓冲区中恢复调用环境,进而触发一个从最近一次初始化env的setjmp调用的返回,然后setjmp返回,并带有非0的返回值retval
      • 在setjmp之后调用。
      • called once , but never returns
      • Set %eax (the return value) to retval
  • 应用2:软中断

小结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Linux上进程的五种状态:

1.R——Runnable(运行):正在运行或在运行队列中等待

2.S——sleeping(中断):休眠中,受阻,在等待某个条件的形成或接收到信号

3.D——uninterruptible sleep(不可中断):收到信号不唤醒和不可运行,进程必须等待直到有中断发生

4.Z——zombie(僵死):进程已终止,但进程描述还在,直到父进程调用wait4()系统调用后释放

5.T——traced or stoppd(停止):进程收到SiGSTOP,SIGSTP,SIGTOU信号后停止运行

状态后缀表示:

<:优先级高的进程

N:优先级低的进程

L:有些页被锁进内存

s:进程的领导者(在它之下有子进程)

l:ismulti-threaded (using CLONE_THREAD, like NPTL pthreads do)

+:位于后台的进程组