contributed by < 93i7xo2
>
sysprog2019
queue.[ch]
和連帶的檔案,測試後用 Git 管理各項修改。
qtest
的行為和裡頭的技巧,特別是 signal handlerMAGICHEADER
, MAGICFREE
, MAGICFOOTER
, FILLCHAR
等巨集的使用,並探討其動機和用法實作下列要求
修改queue_t
的結構以便實作O(1)時間的q_insert_tail
、q_size
建立一個新的queue
。因應q_size
和q_insert_tail
的實作,在queue_t
創立時紀錄元素個數和最後一個元素。
在queue的開頭插入新的元素。插入的元素如果是第一個也是最後一個,須同時修改last_element
在queue的尾端插入新的元素,須在O(1)時間內完成
從刪除 linked-list node 看程式設計的品味提到的有品味code,用此方式實作一遍O(n)的q_insert_tail
,因無法很流暢的寫出來,所以用註解的方式保留下。
移除queue開頭的元素,並將該元素的值複製至指定位置
首次make test
過了但過不了make valgrind
,考慮到bufsize
有可能比元素內的字串還要大,補上相關程式碼後就過了。
節錄strncpy
的介紹:
http://www.cplusplus.com/
不推這家
colinyoyo26和a9502854519都使用strncpy
處理字串複製,各使用不同的方法插入null character。
a9502854519
colinyoyo26
須在O(1)時間內完成,一樣留下了O(n)的實作。
反轉queue的順序,不得新增或移除任何queue中的元素,或是呼叫q_insert_head
, q_insert_tail
, q_remove_head
,必須重新排序queue內的元素
colinyoyo26以q->head
取代temp
,不僅省掉一個local variable也少一行assignment statement。
排版參照uccuser159的共筆
7.14 Signal handling <signal.h>
7.14.1 Specify signal handling
Synopsis
The signal function chooses one of three ways in which receipt of the signal number sig is to be subsequently handled. If the value of func is
SIG_DFL
, default handling for that signal will occur. If the value of func isSIG_IGN
, the signal will be ignored.Otherwise, func shall point to a function to be called when that signal occurs. An invocation of such a function because of a signal, or (recursively) of any further functions called by that invocation (other than functions in the standard library), is called a signal handler.
試著讀懂function declaration
When a signal occurs and
func
points to a function, it is implementation-defined whether the equivalent ofsignal(sig, SIG_DFL)
; is executed or the implementation prevents some implementation-defined set of signals (at least includingsig
) from occurring until the current signal handling has completed; in the case ofSIGILL
, the implementation may alternatively define that no action is taken. Then the equivalent of(*func)(sig)
; is executed. If and when the function returns, if the value ofsig
isSIGFPE
,SIGILL
,SIGSEGV
, or any other implementation-defined value corresponding to a computational exception, the behavior is undefined; otherwise the program will resume execution at the point it was interrupted.
用gdb看segmentation fault發生時在magic_handler內的return address指向*p = 0xdead;
,貿然return會產生infinite loop
要在segmentation fault後繼續執行執行程式能用setjump()
和longjmp()
返回指定位置。
7.13.1.1 The setjmp macro
Synopsis
Description
The setjmp macro saves its calling environment in its jmp_buf argument for later use by the longjmp function.
Returns
If the return is from a direct invocation, the setjmp macro returns the value zero. If the return is from a call to the longjmp function, the setjmp macro returns a nonzero value.
7.13.2.1 The longjmp function
Synopsis
Linux Programmer’s Manual詳述setjmp
和longjmp
的實作行為。
SYNOPSIS
alarm()
arranges for a SIGALRM signal to be delivered to the calling process in seconds seconds.
If seconds is zero, any pending alarm is canceled.
SIGNAL(2)
The behavior of signal()
varies across UNIX versions, and has also varied historically across different versions of Linux. Avoid its use:
use sigaction(2)
instead. See Portability below.
signal()
sets the disposition of the signal signum to handler, which is either SIG_IGN
, SIG_DFL
, or the address of a programmer-defined function (a "signal handler").
DESCRIPTION
Linux supports both POSIX reliable signals (hereinafter "standard signals") and POSIX real-time signals.
Signal mask and pending signals
A signal may be blocked
, which means that it will not be delivered until it is later unblocked. Between the time when it is generated and when it is delivered a signal is said to be pending
.
Each thread in a process has an independent signal mask
, which indicates the set of signals that the thread is currently blocking. 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.
換言之,process signal mask是用來表示在thread中目前blocked signal set
How are signals handled in Linux
Synopsis
Description
The longjmp() function uses the information saved in env to transfer control back to the point where setjmp() was called and to restore ("rewind") the stack to its state at the time of the setjmp() call.
In addition, and depending on the implementation (see NOTES), the values of some other registers and the process signal mask may be restored to their state at the time of the setjmp() call.
sigsetjmp() and siglongjmp()
sigsetjmp()
and siglongjmp()
also perform nonlocal gotos, but provide predictable handling of the process signal mask.
If, and only if, the savesigs argument provided to sigsetjmp() is nonzero, the process's current signal mask is saved in env and will be restored if a siglongjmp() is later performed with this env.
setjmp會保存calling environment(instruction pointer, stack pointer…)並return 0,在接下來呼叫longjmp如同在setjmp處return
val
,用return value控制程式流程。
Notes
先看看當下glibc的版本
POSIX does not specify whether setjmp() will save the signal mask (to be later restored during longjmp()). In System V it will not. In 4.3BSD it will, and there is a function _setjmp() that will not. The behavior under Linux depends on the glibc version and the setting of feature test macros.
Since glibc 2.19, <setjmp.h> exposes only the System V version of setjmp(). Programs that need the BSD semantics should replace calls to setjmp() with calls to sigsetjmp() with a nonzero savesigs argument.
harness.h
在qtest.c
可見queue的相關操作do_new、do_free、do_insert_head等由harness.c
的幾個function所包覆。
介紹這些function的功能,以q_new()
這段為例
bool exception_setup(bool limit_time)
以sigsetjmp(env, 1)
為分歧點
siglongjmp
跳回time_limit
秒後觸發SIGALRM signal,jmp_ready=true
,回傳true繼續執行接下來的函式。siglongjmp
跳回error_message
,回傳false,接序執行。void trigger_exception(char *msg)
error_occurred=true
,在非預期情況下被呼叫直接exit(1)
,否則直接呼叫siglongjmp
void exception_cancel()
清空error_message
,time_limited==true
則取消alram。
在沒有設定alarm的情況下呼叫
alarm(0)
會有事嗎
error_check()
返回error_occurred
並重設error_occurred
,類似Test-and-set,
所以q_new()
只要在執行時超過設定秒數、或是segmentation fault連帶呼叫signal handler內的trigger_exception()
去設定error_occurred
,最後!error_check();
會是false。
待補
待補