APUE 習題15.15,一個XSI共享存儲的測試程序:
#include <fcntl.h>
#include <stdio.h>
#include <assert.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <stdbool.h>
#include <signal.h>
#include <stddef.h>
static volatile sig_atomic_t sigflag;
static sigset_t newmask, oldmask, zeromask;
static void sig_usr(int signo)
{
sigflag = 1;
}
bool TELL_WAIT()
{
if(signal(SIGUSR1, sig_usr) != 0) return false;
if(sigemptyset(&zeromask) != 0) return false;
if(sigemptyset(&newmask) != 0) return false;
if(sigaddset(&newmask, SIGUSR1) != 0) return false;
if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) != 0) return false;
return true;
}
void TELL(pid_t pid)
{
kill(pid, SIGUSR1);
}
void WAIT()
{
while(sigflag == 0) sigsuspend(&zeromask);
sigflag = 0;
sigprocmask(SIG_SETMASK, &oldmask, NULL);
}
static int update(int *ptr)
{
return ((*ptr)++);
}
int main()
{
int shmid = shmget(IPC_PRIVATE, sizeof(int), 0600);
assert(shmid != -1);
TELL_WAIT();
pid_t pid = fork();
if(pid < 0) return 3;
else if(pid > 0)
{
void *area = shmat(shmid, 0, 0);
assert(area != (void*)-1);
printf("parent shm addr: %p\n", area);
for(int i=0; i<100; i+=2)
{
printf("parent %d\n", update((int*)area));
TELL(pid);
WAIT();
}
shmdt(area);
}
else
{
void *area = shmat(shmid, 0, 0);
assert(area != (void*)-1);
printf("child shm addr: %p\n", area);
for(int i=1; i<101; i+=2)
{
WAIT();
printf("child %d\n", update((int*)area));
TELL(getppid());
}
shmdt(area);
shmctl(shmid, IPC_RMID, 0);
}
return 0;
}
gcc -std=c99 -D_XOPEN_SOURCE test.c
運行結果不對:
parent shm addr: 0x7f9a4476e000
parent 0
child shm addr: 0x7f9a4476e000
child 1
parent 2
然後查看子進程處於<defunct>狀態,子進程退出了,父進程還在WAIT。
修改父進程,調用wait()獲取子進程狀態是10,正好是信號SIGUSR1的值,懷疑是子進程收到SIGUSR1就退出了,查了SIGUSR1的默認動作是終止程序。在WAIT()中sigsuspend返回之後重新設置信號處理程序,輸出變正常了。
結合man和帖子signal()註冊的信號處理函數裏面,不需要再次註冊信號處理函數嗎? :
The kernel’s signal() system call provides System V semantics. System V會在處理信號之後將信號處理程序重置爲SIG_DFL。如果編譯時加了額外的選項例如-std=c99,那麼用內核的signal()系統調用。默認情況下使用sigaction函數的方式。
嘗試去掉-std=c99並調整代碼並沒有作用,可能是還帶了-D_XOPEN_SOURCE;以下兩種方式是可以的:
g++ test.c
gcc -std=gnu99 test.c
APUE 10.14節對sigaction函數明確指出:一旦對給定的信號設置了一個動作,那麼在調用sigaction顯式地改變它之前,該設置一直有效。所以將
if(signal(SIGUSR1, sig_usr) != 0) return false;
替換爲
struct sigaction act;
act.sa_handler = sig_usr;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
if(sigaction(SIGUSR1, &act, NULL) != 0) return false;
也可以解決問題。
結論:
最好使用sigaction函數替代signal函數。