該程序包含兩個徹底的bug和幾個infelicities。
第一錯誤是非常簡單的:
int switch_context = 0, first_call = 1;
可變switch_context
用於從異步信號處理程序進行通信,以主程序。因此,爲了正確操作,必須被賦予volatile sig_atomic_t
的類型。 (如果不這樣做,編譯器可能會認爲沒有人將switch_context
設置爲1,並刪除所有對swapcontext
的調用!)sig_atomic_t
可能小到char
,但您只將switch_context
設置爲0或1,所以這不是問題。
第二個錯誤是更多的參與:你根本沒有初始化你的協同程序上下文。這是非常挑剔的,並且很少被manpages解釋。您必須先在每個環境下撥打getcontext
。對於除原始上下文以外的每個上下文,您必須爲其分配一個堆棧,並應用makecontext
來定義入口點。如果你沒有做所有這些事情,swapcontext
/setcontext
將崩潰。一個完整的初始化看起來是這樣的:
getcontext(&c1);
c1.uc_stack.ss_size = 1024 * 1024 * 8;
c1.uc_stack.ss_sp = malloc(1024 * 1024 * 8);
if (!c1.uc_stack.ss_sp) {
perror("malloc");
exit(1);
}
makecontext(&c1, nextEven, 0);
(有知道多少堆棧分配,但8兆字節應該對任何人是不夠的我想你可以使用getrlimit(RLIMIT_STACK)
沒有什麼好辦法。在生產級。節目我會用mmap
這樣我就可以再使用mprotect
定義在疊層兩側的防護帶,但就是在這樣一個演示了很多額外的代碼。)
上至infelicities。您應始終使用sigaction
來設置信號處理程序,而不是signal
,因爲signal
未指定。 (請注意,sigaction
並不適用於Windows,這是因爲信號在Windows上是假,不應在所有使用。)您也不要使用alarm
也不sleep
,因爲他們是也尚未得以確認,並可能災難性互動與彼此。取而代之的是使用setitimer
(或timer_settime
,但這在POSIX.1-2008中是新的,而在2008年撤銷的上下文函數是)和nanosleep
。這也有一個好處,你可以設置一個重複計時器並忘記它。
此外,您的程序可以通過意識到您只需要兩個上下文而不是三個。原始上下文使用c2
,並直接撥打nextOdd
。這消除了first_call
和cmain
以及nextOdd
和nextEven
中的複雜切換邏輯。
最後,在nextOdd
和nextEven
你的循環索引變量,應該是unsigned
,這樣的行爲是在卷繞時左右(如果你願意等待2^31秒),明確的,你應該設置標準輸出到行緩衝這樣即使重定向到文件,每行輸出也會立即出現。
全部放在一起我得到這個:
#define _XOPEN_SOURCE 600 /* ucontext was XSI in Issue 6 and withdrawn in 7 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
#include <ucontext.h>
#include <unistd.h>
#ifdef __GNUC__
#define UNUSED(arg) arg __attribute__((unused))
#else
#define UNUSED(arg) arg
#endif
static ucontext_t c1, c2;
static volatile sig_atomic_t switch_context = 0;
static void
handler(int UNUSED(signo))
{
switch_context = 1;
}
static void
nextEven(void)
{
struct timespec delay = { 1, 0 };
for (unsigned int i = 0;; i += 2) {
if (switch_context) {
switch_context = 0;
swapcontext(&c1, &c2);
}
printf("even:%d\n", i);
nanosleep(&delay, 0);
}
}
static void
nextOdd(void)
{
struct timespec delay = { 1, 0 };
for (unsigned int i = 1;; i += 2) {
if (switch_context) {
switch_context = 0;
swapcontext(&c2, &c1);
}
printf("odd:%d\n", i);
nanosleep(&delay, 0);
}
}
int
main(void)
{
/* flush each printf as it happens */
setvbuf(stdout, 0, _IOLBF, 0);
/* initialize main context */
getcontext(&c2);
/* initialize coroutine context */
getcontext(&c1);
c1.uc_stack.ss_size = 1024 * 1024 * 8;
c1.uc_stack.ss_sp = malloc(1024 * 1024 * 8);
if (!c1.uc_stack.ss_sp) {
perror("malloc");
exit(1);
}
makecontext(&c1, nextEven, 0);
/* initiate periodic timer signals */
struct sigaction sa;
memset(&sa, 0, sizeof sa);
sa.sa_handler = handler;
sa.sa_flags = SA_RESTART;
if (sigaction(SIGALRM, &sa, 0)) {
perror("sigaction");
exit(1);
}
struct itimerval it;
memset(&it, 0, sizeof it);
it.it_interval.tv_sec = 2;
it.it_value.tv_sec = 2;
if (setitimer(ITIMER_REAL, &it, 0)) {
perror("setitimer");
exit(1);
}
nextOdd(); /* does not return */
}
鑑於你的函數異步訪問非只讀,非,自由原子鎖的非揮發性'對象sigatomic_t',該行爲是未定義* *。 – EOF
@EOF這是真的,但它也是一個無用的牆專業術語。 – zwol
@zwol:爲什麼?它確切而清晰。它使用標準的術語,而不是一些無意義的句子。 – Olaf