コンテキストの切り替えと割り込みを使用してスレッドをシミュレートする必要があります。プログラムで時折 (1/25 実行) segfault が発生します。何が原因なのかよくわかりません。コンテキスト切り替えのやり方に関係しているのではないかと思います。gdb と valgrind を使用して segfault の原因を特定しようとしましたが、成功しませんでした。
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)
私の-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);
}
}