简单检查共享内存 return SIGSEGV 错误 008b
Simple check in shared memory return SIGSEGV error 008b
我在 C 中实现了一个共享内存,让分叉 child 相互通信,这是一个最小的、完整的、可验证的例子:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#define SHMEMORY
#define NUM_SEMS 2
#define LOCK \
sops.sem_num = 1; \
sops.sem_op = -1; \
semop(sem_Id, &sops, 1);
#define UNLOCK \
sops.sem_num = 1; \
sops.sem_op = 1; \
semop(sem_Id, &sops, 1);
#define TEST_ERROR if (errno) {dprintf(STDERR_FILENO, \
"%s:%d: PID=%5d: Error %d (%s)\n", \
__FILE__, \
__LINE__, \
getpid(), \
errno, \
strerror(errno));}
#define POP_SIZE 100 //number of child
#define TRUE 1
struct shared_data {
/* index where next write will happen */
unsigned long cur_idx;
int invite_sent[POP_SIZE][POP_SIZE];
};
static void init();
static int invite_sent_check(int stud);
int maxMin_rand(int max,int min);
void handle_signal(int sig);
int sim_time = 10;
unsigned long next_num;
struct sembuf sops;
pid_t *kid_pids;
int mem_Id, sem_Id;
int main() {
int i = 0;
int j = 0;
int status, cur_i;
struct shared_data* corso;
pid_t child_pid, my_pid;
int stud = 0;
int exit_loop = 0;
/*********************************************************/
struct sigaction sa;
sigset_t my_mask;
/* handler SIGALRM
*/
sa.sa_handler = handle_signal;
sa.sa_flags = 0;
sigemptyset(&my_mask);
sa.sa_mask = my_mask;
sigaction(SIGALRM, &sa, NULL);
/**********************************************************/
mem_Id = shmget(IPC_PRIVATE, sizeof(*corso), 0600);
TEST_ERROR;
/* Attach the shared memory to a pointer */
corso = shmat(mem_Id, NULL, 0);
TEST_ERROR;
corso->cur_idx = 0; /* init first counter */
/*********************************************************/
sem_Id = semget(IPC_PRIVATE, NUM_SEMS, 0600);
TEST_ERROR;
/* Sem 0 to syncronize the start of child processes */
semctl(sem_Id, 0, SETVAL, 0);
#ifdef SHMEMORY
semctl(sem_Id, 1, SETVAL, 1);
#endif
TEST_ERROR;
sops.sem_num = 0; /* check the 0-th semaphore */
sops.sem_flg = 0; /* no flag */
init();
kid_pids = malloc(POP_SIZE*sizeof(*kid_pids));
for (i = 0; i < POP_SIZE; i++) {
switch (kid_pids[i] = fork()) {
case -1:
/* Handle error */
TEST_ERROR;
break;
case 0:
/* Wait for the green light */
sops.sem_op = -1;
semop(sem_Id, &sops, 1);
while(exit_loop==0 || exit_loop==1){
LOCK;
if(exit_loop == 0){
stud = corso->cur_idx;
printf("%d %d\n",stud,getpid());
corso->cur_idx++;
exit_loop = 1;
}
if(invite_sent_check(stud) == 1){
}
UNLOCK;
}
exit(0);
break;
default:
break;
}
}
alarm(sim_time);
while (shmctl(mem_Id, IPC_RMID, NULL)) { TEST_ERROR; }
sops.sem_op = POP_SIZE;
semop(sem_Id, &sops, 1);
/* Waiting for all child POP_SIZEesses to terminate */
while ((child_pid = wait(&status)) != -1) {
dprintf(2,"PID=%d. Sender (PID=%d) terminated with status 0x%04X\n",
getpid(),
child_pid,
status);
}
/* Now the semaphore may be deallocated */
semctl(sem_Id, 0, IPC_RMID);
exit(0);
}
static void init(){
printf("INIT\n");
struct shared_data * corso;
corso = shmat(mem_Id, NULL, 0);
corso->cur_idx=0;
int r, q, j;
j = 0;
q = 0;
while(j < POP_SIZE){
q = 0;
while(q < POP_SIZE){
corso->invite_sent[j][q] = -1;
q++;
}
j++;
}
}
int maxMin_rand(int max, int min){
int reset;
int randomics=12345;
int w=0;
while(w<reset) {
randomics++;
w++;
}
next_num = next_num+randomics;
next_num = next_num*1103515245 +12345;
unsigned int result=(unsigned int) ((next_num*65536)%(max+1))+min;
int reload;
w=0;
while(w<reload) {
next_num++;
w++;
}
return result;
}
static int invite_sent_check(int stud){
struct shared_data * corso;
corso = shmat(mem_Id, NULL, 0);
int i = 0;
int q = 0;
while(i < POP_SIZE){
if(i == stud){
q = 0;
while(q < POP_SIZE){
if(corso->invite_sent[i][q] != -1){
return 1;
}
q++;
}
}
i++;
}
return 0;
}
void handle_signal(int signal){
int child_pid;
int status;
struct shared_data * corso;
corso = shmat(mem_Id, NULL, 0);
switch (signal) {
case SIGALRM:
for(int i = 0; i < POP_SIZE; i++){
kill(kid_pids[i], SIGKILL);
}
while (shmctl(mem_Id, IPC_RMID, NULL)) {
TEST_ERROR;
}
while ((child_pid = wait(&status)) != -1) {
dprintf(2,"PID=%d. Sender (PID=%d) terminated with status 0x%04X\n",
getpid(),
child_pid,
status);
}
semctl(sem_Id, 0, IPC_RMID);
exit(0);
break;
}
}
分叉的 children 会在计时器结束时保持锁定和解锁 (sim_time = 10)。然后 SIGNAL_HANDLER 杀死所有 children,并终止。
我一直从 RANDOM child 收到 SIGSEGV 错误,该错误以状态 008B 终止并停止他的 "brothers" 直到处理程序杀死所有其他进程。据我所知,这个错误与共享内存中的指针有关,对吗?或者我 missing/I 真的写错了什么?即使是这个检查 INVITE_SENT 矩阵中是否有至少 1 个值与 -1 不同的小方法也会导致崩溃,而不仅仅是返回 0。
谢谢你的时间。
我一直无法在本地重现段错误,并且 Valgrind 没有检测到任何无效的内存访问。段错误还有其他可能的原因,但它们并不常见。由于无法在本地重现该问题,我无法确定其来源,但代码中存在许多问题,大部分都是小问题。
唯一似乎与您的问题相关的问题是函数 invite_sent_check()
中共享内存段的冗余附加,特别是考虑到您使用 return shmat()
的值而不检查它((void*)-1
在失败时被 return 编辑)。诸如此类的冗余附件是明确允许的,但调用者仅将其现有指针传递到段的原始附件点会更简洁、更高效。而且,如果你在那个函数中形成局部附着,那么你一定要确保在returns函数之前再次脱离。不这样做可能是问题的根源,因为元数据和地址-space 对由此产生的许多附件的保留可能会耗尽可用资源。
其他问题包括
dprintf()
不是异步信号安全的,而是从信号处理程序调用的(显式和通过宏 TEST_ERROR
)。
shmat()
不是异步信号安全的,但它是从信号处理程序调用的。此外,这似乎是不必要的,因为处理程序中没有使用新的段附件。而且,它也不分离。
semctl()
不是异步信号安全的,但它是从信号处理程序调用的。
exit()
不是异步信号安全的,但它是从信号处理程序调用的。您可以改用 _Exit()
或 _exit()
,但似乎该处理程序根本不应该退出程序,因为主进程似乎还有其他工作需要清理。
考虑到当您收到 SIGALRM
时您似乎想要做的所有事情,其中许多不是异步信号安全的,您应该考虑使用 sigsupend()
同步接收信号,然后调用常规函数来完成该工作。如果你朝那个方向走,那么最安全、最可靠的方法是先阻塞 SIGALRM
,然后再发出警报,然后将信号掩码传递给sigsuspend()
,允许那个信号。这将防止在进程准备好之前传递信号的任何机会。
函数init()
冗余附加共享内存段。这是允许的,但调用者最好只传递一个指向要初始化的 struct shared_data
的指针。此功能也无法分离。
如果你想通过检查errno
来检查错误,你必须确定在你想检查的调用之前将它设置为0(并在之后立即测试它,然后再做还要别的吗)。但是,更好的做法是使用函数的 return 值来检测是否发生了错误,并且仅依靠 errno
来辨别 哪个 .
函数 maxMin_rand()
的名称和签名表明它旨在 return max
和 min
之间的数字,但它看起来像它可以 return 与 max + min
一样大的数字。
我在 C 中实现了一个共享内存,让分叉 child 相互通信,这是一个最小的、完整的、可验证的例子:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#define SHMEMORY
#define NUM_SEMS 2
#define LOCK \
sops.sem_num = 1; \
sops.sem_op = -1; \
semop(sem_Id, &sops, 1);
#define UNLOCK \
sops.sem_num = 1; \
sops.sem_op = 1; \
semop(sem_Id, &sops, 1);
#define TEST_ERROR if (errno) {dprintf(STDERR_FILENO, \
"%s:%d: PID=%5d: Error %d (%s)\n", \
__FILE__, \
__LINE__, \
getpid(), \
errno, \
strerror(errno));}
#define POP_SIZE 100 //number of child
#define TRUE 1
struct shared_data {
/* index where next write will happen */
unsigned long cur_idx;
int invite_sent[POP_SIZE][POP_SIZE];
};
static void init();
static int invite_sent_check(int stud);
int maxMin_rand(int max,int min);
void handle_signal(int sig);
int sim_time = 10;
unsigned long next_num;
struct sembuf sops;
pid_t *kid_pids;
int mem_Id, sem_Id;
int main() {
int i = 0;
int j = 0;
int status, cur_i;
struct shared_data* corso;
pid_t child_pid, my_pid;
int stud = 0;
int exit_loop = 0;
/*********************************************************/
struct sigaction sa;
sigset_t my_mask;
/* handler SIGALRM
*/
sa.sa_handler = handle_signal;
sa.sa_flags = 0;
sigemptyset(&my_mask);
sa.sa_mask = my_mask;
sigaction(SIGALRM, &sa, NULL);
/**********************************************************/
mem_Id = shmget(IPC_PRIVATE, sizeof(*corso), 0600);
TEST_ERROR;
/* Attach the shared memory to a pointer */
corso = shmat(mem_Id, NULL, 0);
TEST_ERROR;
corso->cur_idx = 0; /* init first counter */
/*********************************************************/
sem_Id = semget(IPC_PRIVATE, NUM_SEMS, 0600);
TEST_ERROR;
/* Sem 0 to syncronize the start of child processes */
semctl(sem_Id, 0, SETVAL, 0);
#ifdef SHMEMORY
semctl(sem_Id, 1, SETVAL, 1);
#endif
TEST_ERROR;
sops.sem_num = 0; /* check the 0-th semaphore */
sops.sem_flg = 0; /* no flag */
init();
kid_pids = malloc(POP_SIZE*sizeof(*kid_pids));
for (i = 0; i < POP_SIZE; i++) {
switch (kid_pids[i] = fork()) {
case -1:
/* Handle error */
TEST_ERROR;
break;
case 0:
/* Wait for the green light */
sops.sem_op = -1;
semop(sem_Id, &sops, 1);
while(exit_loop==0 || exit_loop==1){
LOCK;
if(exit_loop == 0){
stud = corso->cur_idx;
printf("%d %d\n",stud,getpid());
corso->cur_idx++;
exit_loop = 1;
}
if(invite_sent_check(stud) == 1){
}
UNLOCK;
}
exit(0);
break;
default:
break;
}
}
alarm(sim_time);
while (shmctl(mem_Id, IPC_RMID, NULL)) { TEST_ERROR; }
sops.sem_op = POP_SIZE;
semop(sem_Id, &sops, 1);
/* Waiting for all child POP_SIZEesses to terminate */
while ((child_pid = wait(&status)) != -1) {
dprintf(2,"PID=%d. Sender (PID=%d) terminated with status 0x%04X\n",
getpid(),
child_pid,
status);
}
/* Now the semaphore may be deallocated */
semctl(sem_Id, 0, IPC_RMID);
exit(0);
}
static void init(){
printf("INIT\n");
struct shared_data * corso;
corso = shmat(mem_Id, NULL, 0);
corso->cur_idx=0;
int r, q, j;
j = 0;
q = 0;
while(j < POP_SIZE){
q = 0;
while(q < POP_SIZE){
corso->invite_sent[j][q] = -1;
q++;
}
j++;
}
}
int maxMin_rand(int max, int min){
int reset;
int randomics=12345;
int w=0;
while(w<reset) {
randomics++;
w++;
}
next_num = next_num+randomics;
next_num = next_num*1103515245 +12345;
unsigned int result=(unsigned int) ((next_num*65536)%(max+1))+min;
int reload;
w=0;
while(w<reload) {
next_num++;
w++;
}
return result;
}
static int invite_sent_check(int stud){
struct shared_data * corso;
corso = shmat(mem_Id, NULL, 0);
int i = 0;
int q = 0;
while(i < POP_SIZE){
if(i == stud){
q = 0;
while(q < POP_SIZE){
if(corso->invite_sent[i][q] != -1){
return 1;
}
q++;
}
}
i++;
}
return 0;
}
void handle_signal(int signal){
int child_pid;
int status;
struct shared_data * corso;
corso = shmat(mem_Id, NULL, 0);
switch (signal) {
case SIGALRM:
for(int i = 0; i < POP_SIZE; i++){
kill(kid_pids[i], SIGKILL);
}
while (shmctl(mem_Id, IPC_RMID, NULL)) {
TEST_ERROR;
}
while ((child_pid = wait(&status)) != -1) {
dprintf(2,"PID=%d. Sender (PID=%d) terminated with status 0x%04X\n",
getpid(),
child_pid,
status);
}
semctl(sem_Id, 0, IPC_RMID);
exit(0);
break;
}
}
分叉的 children 会在计时器结束时保持锁定和解锁 (sim_time = 10)。然后 SIGNAL_HANDLER 杀死所有 children,并终止。 我一直从 RANDOM child 收到 SIGSEGV 错误,该错误以状态 008B 终止并停止他的 "brothers" 直到处理程序杀死所有其他进程。据我所知,这个错误与共享内存中的指针有关,对吗?或者我 missing/I 真的写错了什么?即使是这个检查 INVITE_SENT 矩阵中是否有至少 1 个值与 -1 不同的小方法也会导致崩溃,而不仅仅是返回 0。 谢谢你的时间。
我一直无法在本地重现段错误,并且 Valgrind 没有检测到任何无效的内存访问。段错误还有其他可能的原因,但它们并不常见。由于无法在本地重现该问题,我无法确定其来源,但代码中存在许多问题,大部分都是小问题。
唯一似乎与您的问题相关的问题是函数 invite_sent_check()
中共享内存段的冗余附加,特别是考虑到您使用 return shmat()
的值而不检查它((void*)-1
在失败时被 return 编辑)。诸如此类的冗余附件是明确允许的,但调用者仅将其现有指针传递到段的原始附件点会更简洁、更高效。而且,如果你在那个函数中形成局部附着,那么你一定要确保在returns函数之前再次脱离。不这样做可能是问题的根源,因为元数据和地址-space 对由此产生的许多附件的保留可能会耗尽可用资源。
其他问题包括
dprintf()
不是异步信号安全的,而是从信号处理程序调用的(显式和通过宏TEST_ERROR
)。shmat()
不是异步信号安全的,但它是从信号处理程序调用的。此外,这似乎是不必要的,因为处理程序中没有使用新的段附件。而且,它也不分离。semctl()
不是异步信号安全的,但它是从信号处理程序调用的。exit()
不是异步信号安全的,但它是从信号处理程序调用的。您可以改用_Exit()
或_exit()
,但似乎该处理程序根本不应该退出程序,因为主进程似乎还有其他工作需要清理。考虑到当您收到
SIGALRM
时您似乎想要做的所有事情,其中许多不是异步信号安全的,您应该考虑使用sigsupend()
同步接收信号,然后调用常规函数来完成该工作。如果你朝那个方向走,那么最安全、最可靠的方法是先阻塞SIGALRM
,然后再发出警报,然后将信号掩码传递给sigsuspend()
,允许那个信号。这将防止在进程准备好之前传递信号的任何机会。函数
init()
冗余附加共享内存段。这是允许的,但调用者最好只传递一个指向要初始化的struct shared_data
的指针。此功能也无法分离。如果你想通过检查
errno
来检查错误,你必须确定在你想检查的调用之前将它设置为0(并在之后立即测试它,然后再做还要别的吗)。但是,更好的做法是使用函数的 return 值来检测是否发生了错误,并且仅依靠errno
来辨别 哪个 .函数
maxMin_rand()
的名称和签名表明它旨在 returnmax
和min
之间的数字,但它看起来像它可以 return 与max + min
一样大的数字。