我需要使用上下文切换和中断来模拟线程。我偶尔(1/25跑)在我的程序中得到段错误。我不确定它的原因是什么。我怀疑它与我进行上下文切换的方式有关。我已经尝试用gdb和valgrind确定段错误的原因并没有成功。
GDB结果:
0x0000007b in ?? ()
(gdb) bt
#0 0x0000007b in ?? ()
(gdb)
Valgrind结果(未完成):
==6343== Conditional jump or move depends on uninitialised value(s)
8 ==6343== at 0x808F8CA: __linkin_atfork (in my-test)
9 ==6343== by 0x809046B: _dl_non_dynamic_init (in my-test)
10 ==6343== by 0x8090C31: __libc_init_first (in my-test)
11 ==6343== by 0x805EEB0: (below main) (in my-test)
12 ==6343== Uninitialised value was created
13 ==6343== at 0x80900E2: _dl_sysinfo_int80 (in my-test)
14 ==6343== by 0x80C056F: brk (in my-test)
15 ==6343== by 0x808D6C9: sbrk (in my-test)
16 ==6343== by 0x805F1CB: __libc_setup_tls (in my-test)
17 ==6343== by 0x805F3C6: __pthread_initialize_minimal (in my-test)
18 ==6343== by 0x805EE5C: (below main) (in my-test)
19 ==6343==
20 ==6343== Conditional jump or move depends on uninitialised value(s)
21 ==6343== at 0x806C015: malloc (in my-test)
22 ==6343== by 0x809046B: _dl_non_dynamic_init (in my-test)
23 ==6343== by 0x8090C31: __libc_init_first (in my-test)
24 ==6343== by 0x805EEB0: (below main) (in my-test)
25 ==6343== Uninitialised value was created
26 ==6343== at 0x80900E2: _dl_sysinfo_int80 (in my-test)
27 ==6343== by 0x80C056F: brk (in my-test)
28 ==6343== by 0x808D6C9: sbrk (in my-test)
29 ==6343== by 0x805F1CB: __libc_setup_tls (in my-test)
30 ==6343== by 0x805F3C6: __pthread_initialize_minimal (in my-test)
31 ==6343== by 0x805EE5C: (below main) (in my-test)
...
==6343== Warning: client switching stacks? SP change: 0xbee18ea0 --> 0x8135e08
281 ==6343== to suppress, use: --max-stackframe=1228001128 or greater
282 ==6343== Conditional jump or move depends on uninitialised value(s)
283 ==6343== at 0x806C015: malloc (in my-test)
284 ==6343== by 0x804E5D4: mem_resize_fn (in my-test)
285 ==6343== by 0x804A5D1: expand (iin my-test)
286 ==6343== by 0x804B0C4: list_insert (in my-test)
287 ==6343== by 0x804BE7E: list_append_int (in my-test)
288 ==6343== by 0x8049B4D: handler (in my-test)
289 ==6343== by 0x8060B42: makecontext (in my-test)
290 ==6343== by 0x8049AB0: main (in my-test)
291 ==6343== Uninitialised value was created
292 ==6343== at 0x80900E2: _dl_sysinfo_int80 (in my-test)
293 ==6343== by 0x80C056F: brk (in my-test)
294 ==6343== by 0x808D6C9: sbrk (in my-test)
295 ==6343== by 0x805F1CB: __libc_setup_tls (in my-test)
296 ==6343== by 0x805F3C6: __pthread_initialize_minimal (in my-test)
297 ==6343== by 0x805EE5C: (below main) (in my-test)
...
==6343== Warning: client switching stacks? SP change: 0x813da60 --> 0xbee18ea0
385 ==6343== to suppress, use: --max-stackframe=1228032960 or greater
386 ==6343== Use of uninitialised value of size 4
387 ==6343== at 0x804A1E9: scheduler (in my-test)
388 ==6343== by 0x809F617: ??? (in my-test)
389 ==6343== by 0x809F617: ??? (in my-test)
390 ==6343== Uninitialised value was created by a stack allocation
391 ==6343== at 0x8060BD8: swapcontext (in my-test)
...
==6343== Use of uninitialised value of size 4
434 ==6343== at 0x805F644: sigprocmask (in my-test)
435 ==6343== by 0x1FFF: ???
436 ==6343== by 0x8049B4D: handler (in my-test)
437 ==6343== by 0x8060B42: makecontext (in my-test)
438 ==6343== by 0x8049AB0: main (in my-test)
439 ==6343== Uninitialised value was created by a stack allocation
440 ==6343== at 0x8060BD8: swapcontext (in my-test)
441 ==6343==
442 ==6343== Jump to the invalid address stated on the next line
443 ==6343== at 0x2000: ???
444 ==6343== by 0x8049B4D: handler (in my-test)
445 ==6343== by 0x8060B42: makecontext (in my-test)
446 ==6343== by 0x8049AB0: main (in my-test)
447 ==6343== Address 0x2000 is not stack'd, malloc'd or (recently) free'd
448 ==6343==
449 ==6343==
450 ==6343== Process terminating with default action of signal 11 (SIGSEGV)
451 ==6343== Bad permissions for mapped region at address 0x2000
452 ==6343== at 0x2000: ???
453 ==6343== by 0x8049B4D: handler (in my-test)
454 ==6343== by 0x8060B42: makecontext (in my-test)
455 ==6343== by 0x8049AB0: main (in my-test)
456 ==6343==
457 ==6343== HEAP SUMMARY:
458 ==6343== in use at exit: 0 bytes in 0 blocks
459 ==6343== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
460 ==6343==
461 ==6343== All heap blocks were freed -- no leaks are possible
462 ==6343==
463 ==6343== For counts of detected and suppressed errors, rerun with: -v
464 ==6343== ERROR SUMMARY: 114 errors from 38 contexts (suppressed: 0 from 0)
MY-test.c的:
/* Includes */
#include <stdio.h> /* Input/Output */
#include <stdlib.h> /* General Utilities */
#include <math.h>
#include "mythreads.h"
/* prototype for thread routine */
void handler ( );
/* global vars */
/* semaphores are declared global so they can be accessed
in main() and in thread routine,
here, the semaphore is used as a mutex */
int counter_mutex;
/* shared variables */
int counter;
double result = 0.0;
int main()
{
int thread_num = 10;
int j;
char* thread_names[] = {
"thread 0",
"thread 1",
"thread 2",
"thread 3",
"thread 4",
"thread 5",
"thread 6",
"thread 7",
"thread 8",
"thread 9"
};
/* Initialize MyThreads library. */
mythread_init();
/* 250 ms */
set_quantum_size(500);
counter_mutex = create_semaphore(1);
for(j=0; j<thread_num; j++)
{
mythread_create(thread_names[j], (void *) &handler, 6004);
}
/* Print threads informations before run */
//mythread_state();
/* When this function returns, all threads should have exited. */
runthreads();
destroy_semaphore(counter_mutex);
// /* Print threads informations after run */
mythread_state();
printf("The counter is %d\n", counter);
printf("The result is %f\n", result);
if (counter == 50 &&
(result - 151402.656521) < 0.000001)
printf(">>> Thread library PASSED the Test 1\n");
exit(0);
}
void handler ()
{
int i;
for(i=0; i < 5; i++)
{
/* If you remove this protection, you should be able to see different
* out of every time you run this program. With this protection, you
* should always be able to see result to be 151402.656521 */
semaphore_wait(counter_mutex); /* down semaphore */
/* START CRITICAL REGION */
int j;
for (j = 0; j < 1000; j++) {
result = result + sin(counter) * tan(counter);
}
counter++;
/* END CRITICAL REGION */
semaphore_signal(counter_mutex); /* up semaphore */
}
mythread_exit(); /* exit thread */
}
mythreads.h
#ifndef __MY_THREADS_H__
#define __MY_THREADS_H__
#include <signal.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <sys/timeb.h>
#include <ucontext.h>
#include <slack/std.h>
#include <slack/list.h>
#define THREAD_NAME_LEN 32
#define THREAD_MAX 128
#define SEMAPHORE_MAX 128
#define THREAD_STACK_SIZE 4096
#define QUANTUM_N_SIZE 60000
enum ThreadState {
NOTCREATED,
RUNNING,
RUNNABLE,
BLOCKED,
EXIT
};
typedef struct ControlBlock_t {
ucontext_t context;
char thread_name[THREAD_NAME_LEN];
int thread_id;
enum ThreadState state;
struct timespec start;
struct timespec end;
double run_time;
void *stack;
} ControlBlock;
typedef struct Semaphore_t {
List * thread_queue;
int value;
int initial;
int active;
} Semaphore;
int mythreads_init();
int mythread_create(char *threadname, void (*threadfunc)(), int stacksize);
void mythread_exit();
int mythread_id();
void runthreads();
void set_quantum_size(int quantum);
int create_semaphore(int value);
void semaphore_wait(int semaphore);
void semaphore_signal(int semaphore);
void destroy_semaphore(int semaphore);
void mythread_state();
void evict_thread();
void scheduler();
#endif /* __MY_THREADS_H__ */
mythreads.c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <slack/std.h>
#include <slack/list.h>
#include "mythreads.h"
static ControlBlock tcbTable[THREAD_MAX];
static Semaphore semTable[SEMAPHORE_MAX];
static List * runqueue;
static ControlBlock * current_thread;
static int threadId = 0;
static int runningThreadId = 0;
static ucontext_t uctx_main;
static int quantum_size;
struct itimerval timer;
static int current_threads;
static int my_threads_init;
static int current_semaphores;
static int done = 0;
static int totalThreadsCreated = 0;
static int totalThreadsExited = 0;
long long int switch_ctr;
int i=0;
/*
This function initializes all the globa data structures for the thread system
*/
int mythread_init(){
int i=0;
//Initialize the run queue.
runqueue = list_create(NULL);
//Initialize the thread control table.
for(i = 0;i < THREAD_MAX;i++){
tcbTable[i].state = NOTCREATED;
}
//Initialize the semaphore table.
for(i = 0;i < SEMAPHORE_MAX;i++){
semTable[i].active = 0;
}
}
int mythread_create(char *threadName, void(*threadfunc)(), int stacksize){
//Set basic information about the thread.
strcpy(tcbTable[threadId].thread_name, threadName);
tcbTable[threadId].thread_id = threadId;
tcbTable[threadId].state = RUNNABLE;
//Set the context information about the thread.
getcontext(&tcbTable[threadId].context); //Save the current context.
tcbTable[threadId].context.uc_link = &uctx_main; //The context that will be resumed when the current context exits.
tcbTable[threadId].stack = malloc(stacksize);
tcbTable[threadId].context.uc_stack.ss_sp = tcbTable[threadId].stack; //Allocate the stack and make it point to the beginning of the stack.
tcbTable[threadId].context.uc_stack.ss_size = stacksize; //Set the signal stack size.
makecontext(&tcbTable[threadId].context,threadfunc,0); //Creates the context with the information set above.
//Add thread to the runqueue.
runqueue = list_append_int(runqueue,threadId);
totalThreadsCreated++;
//If an error occured, return -1. Otherwise, return the thread id.
if(!tcbTable[threadId].context.uc_stack.ss_sp){
//We failed to allocate memory for the stack
printf("Failed to allocate memory for the stack\n");
fflush(stdout);
return -1;
}else{
int tmp = threadId;
threadId++;
return tmp;
}
}
/*
This function is called at the end of the function that was
invoked by the thread.
*/
void mythread_exit(){
sigset_t x;
sigemptyset(&x);
sigaddset(&x, SIGALRM);
sigprocmask(SIG_BLOCK, &x, NULL);
printf("Setting state of thread %d to EXIT.\n",runningThreadId);
fflush(stdout);
tcbTable[runningThreadId].state = EXIT; //Set the state of the thread to EXIT.
totalThreadsExited++;
if(totalThreadsCreated == totalThreadsExited){
done = 1;
}
sigprocmask(SIG_UNBLOCK, &x, NULL);
}
/*
Starts running one of the threads in the runqueue.
*/
void runthreads(){
/* Block Signals */
sigset_t x;
sigemptyset(&x);
sigaddset(&x, SIGALRM);
sigprocmask(SIG_BLOCK, &x, NULL);
struct itimerval timer;
timer.it_interval.tv_sec = 0;
timer.it_interval.tv_usec = quantum_size;
timer.it_value.tv_sec = 0;
timer.it_value.tv_usec = quantum_size;
setitimer(ITIMER_REAL, &timer, 0);
sigset(SIGALRM, &scheduler);
runningThreadId = list_shift_int(runqueue);
tcbTable[runningThreadId].state = RUNNING;
//begin_time(running_thread);
/* Unblock signal */
sigprocmask(SIG_UNBLOCK, &x, NULL);
if(swapcontext(&uctx_main, &tcbTable[runningThreadId].context) == -1) {
perror("swapcontext error");
exit(1);
}
while(!list_empty(semTable[0].thread_queue) || (totalThreadsExited<totalThreadsCreated));
sigprocmask(SIG_BLOCK, &x, NULL);
for(i = 0; i < 10 ; i++) {
tcbTable[i].state = EXIT;
}
printf("Back in main\n");
fflush(stdout);
}
void set_quantum_size(int size){
quantum_size = size;
}
/*
Called when the ALRM signal fires
*/
void scheduler(){
switch_ctr++;
if (list_empty(runqueue) && tcbTable[runningThreadId].state == EXIT) {
printf("returning to main");
fflush(stdout);
setcontext(&uctx_main);
}
if (!list_empty(runqueue) ) {
sigset_t x;
sigemptyset(&x);
sigaddset(&x, SIGALRM);
sigprocmask(SIG_BLOCK, &x, NULL);
int old_running_thread = runningThreadId;
runningThreadId = list_shift_int(runqueue);
if (tcbTable[old_running_thread].state == RUNNABLE || tcbTable[old_running_thread].state == RUNNING) {
runqueue = list_append_int(runqueue, old_running_thread);
}
sigprocmask(SIG_UNBLOCK, &x, NULL);
if (swapcontext(&tcbTable[old_running_thread].context, &tcbTable[runningThreadId].context) == -1) {
printf("swapcontext error");
fflush(stdout);
}
}
}
int create_semaphore(int val){
if (current_semaphores == SEMAPHORE_MAX){
//We already have the maximum number of semphores allowed
//so we cannot create new ones.
return -1;
}else{
semTable[current_semaphores].initial = val;
semTable[current_semaphores].value = val;
semTable[current_semaphores].thread_queue = list_create(NULL);
int tmp = current_semaphores;
current_semaphores++;
return tmp;
}
}
/*
Called by the handler() funcion in my-test.c
*/
void semaphore_wait(int semaphore){
sigset_t x;
sigemptyset(&x);
sigaddset(&x, SIGALRM);
sigprocmask(SIG_BLOCK, &x, NULL);
long long int old_switch_ctr = switch_ctr;
(semTable[semaphore]).value--;
if((semTable[semaphore]).value<0) {
(tcbTable[runningThreadId]).state = BLOCKED;
(semTable[semaphore]).thread_queue = list_append_int((semTable[semaphore]).thread_queue,runningThreadId);
}
sigprocmask(SIG_UNBLOCK,&x,NULL);
while(old_switch_ctr==switch_ctr);
}
/*
Called by the handler() funcion in my-test.c
*/
void semaphore_signal(int semaphore){
// Block Signals
sigset_t x;
sigemptyset (&x);
sigaddset(&x, SIGALRM);
sigprocmask(SIG_BLOCK, &x, NULL);
/* Increase Semaphore value */
semTable[semaphore].value = semTable[semaphore].value + 1;
if (semTable[semaphore].value < 1) {
int should_run;
should_run = list_shift_int(semTable[semaphore].thread_queue);
tcbTable[should_run].state = RUNNABLE;
runqueue = list_append_int(runqueue, should_run);
}
// Unblock Signals
sigprocmask(SIG_UNBLOCK, &x, NULL);
scheduler();
}
void destroy_semaphore(int semaphore){
if(!list_empty(semTable[semaphore].thread_queue)){
fprintf(stderr, "There are threads waiting on this semaphore. Thus, it cannot be destroyed\n");
return;
}
}
void mythread_state()
{
printf("\nTHREADNAME\tTHREAD\tTHREAD STATE\tCPU TIME(ns)\n");
fflush(stdout);
int i;
for(i = 0; (tcbTable[i].state != NOTCREATED) && (i < THREAD_MAX); ++i) {
char * state_i;
switch(tcbTable[i].state) {
case 0:
state_i = "NOTCREATED";
break;
case 1:
state_i = "RUNNING";
break;
case 2:
state_i = "RUNNABLE";
break;
case 3:
state_i = "RUNNABLE";
break;
case 4:
state_i = "EXIT";
break;
default:
state_i = "UNDEFINED";
break;
}
printf("\n%s\t%d\t%s\t\t%la\n", tcbTable[i].thread_name, i, state_i, tcbTable[i].run_time);
}
}