In the Redis system also exists the concept of background services, background service, background thread in the Redis performance mainly for background I/O service, with the support of the background thread, the system in the implementation of the efficiency will also be a different improvement. In Redis code, the file that describes this feature is BIO.C, and it's also a chance to learn how multithreaded programming in C is all about. Let's take a look at the working form of the background job in Redis;
/* Background I/O service for Redis.
* * Background I/O Service * This file implements operations so we need to perform in the background. * Currently there is a single operation, which is a background close (2) * system call. This are needed as when the ' process is ' reference to a file closing it means unlinking it, and the de
Letion of the * file is slow, blocking the server. * * In the future we ll either continue implementing new things we need or * we'll switch to Libeio. However There are probably long term uses for this * file as we'll want to put here Redis specific the tasks (for
Instance * It is isn't impossible that we ' ll need a non blocking flushdb/flushall * implementation). * * Design *------* * * * * trivial, we have a structure representing a job to perform * and a different th
The read and job queue for every job type.
* Every thread wait for the new jobs in its queue, and process Every job * sequentially. * * Jobs of the same type are guaranteed to is processed from the least * recently inserted to the most recently (inserted jobs older *
a). * * Currently there is no way for the creator of the job to be notified about * The completion of the operation, this WI
ll be added when/if needed. * The author defines a struct representing a job, each thread waits for a job to be fetched from the corresponding job type work queue, and the arrangement of each job is arranged in the order of time *---------------------------------------- ------------------------------------
There are altogether 2 types of background I/O type:
/* Background Job opcodes
/* Defines 2 types of background work * * *
#define REDIS_BIO_CLOSE_FILE 0/* Deferred close (2) syscall. Part of the closure/
#define REDIS_BIO_AOF_FSYNC 1/* Deferred aof Fsync. AOF file synchronization/
* BIO background operation type Total 2/
#define REDIS_BIO_NUM_OPS 2
One is aof file synchronization operation, AOF is "Append only file" abbreviation, record every time the data changes write operations, for data recovery. There's another one I don't seem to have met, close file, is it the meaning of the asynchronous shutdown.
Static pthread_t Bio_threads[redis_bio_num_ops]; /* Defines the BIO thread group variable/static pthread_mutex_t Bio_mutex[redis_bio_num_ops];
/* Line threads relative corresponding mutex variable, for synchronous operation * * static pthread_cond_t bio_condvar[redis_bio_num_ops]; Static list *bio_jobs[redis_bio_num_ops]; /* Each job type is a list *//* The following array is used to hold the number of pending jobs for every * OP type. This allows us to export the Biopendingjobsoftype () API, which is * useful while the main thread wants to perform some opera tion that may involve * objects is shared with the background thread. The main thread would just wait * so there are no longer jobs of this type to being executed before performing * the Sensi ble operation. This data is also useful for reporting. */static unsigned long long bio_pending[redis_bio_num_ops]; * * The number of this type of job waiting to be executed/* This structure represents a background job. It is only used locally to this * file as the API does not expose the internals at all. *///////* Background Job structure/* struct Bio_job {//job created time Time_t time; /* time in which the job was created. * * * Job specific arguments pointers. If we than three * arguments we can just pass a pointer or structure need.
*////* Job specific parameter pointer/void *ARG1, *ARG2, *ARG3;
};
Some variables are declared above, including the bio_threads thread array, the total number of 2, and the Bio_jobs list array, which holds the job for each type. Here we look at some of the main methods:
/* Exported API/
void bioinit (void);/* background I/O initialization operation/
void biocreatebackgroundjob (int type, void *arg1, void *arg2, void *arg3); /* Create a background job to initialize
/unsigned long long biopendingjobsoftype (int type) by passing in the 3 parameters passed in;/* Returns the number of jobs being executed by type: *
Biowaitpendingjobsle (int type, unsigned long long num); /* Returns the number of the type job waiting to be executed
/time_t bioolderjoboftype (int type);
void biokillthreads (void); /* Kill all threads in the background * *
First look at the initialization of the operation;
/* Initialize The background system, spawning the thread.
*/* Background I/O initialization operation/void Bioinit (void) {pthread_attr_t attr;
pthread_t thread;
size_t stacksize;
Int J; /* Initialization of State VARs and Objects/for (j = 0; J < Redis_bio_num_ops; J +) {Pthread_mutex_init
(&bio_mutex[j],null);
Pthread_cond_init (&bio_condvar[j],null);
Create list lists for each job type bio_jobs[j] = Listcreate ();
BIO_PENDING[J] = 0; }/* Set the stack size as by default it is small in some system *///Set line stacks space Pthread_attr_init (&attr
);
Pthread_attr_getstacksize (&attr,&stacksize); if (!stacksize) stacksize = 1;
/* The world was full of Solaris fixes/while (StackSize < redis_thread_stack_size) StackSize *= 2;
Pthread_attr_setstacksize (&attr, stacksize); * Ready to spawn our threads. We use the ' single argument ' thread * function accepts in ' order to ' pass ' job ID the THREAD is * responsible of.
* for (j = 0 J < Redis_bio_num_ops J + +) {void *arg = (void*) (unsigned long) j; Create 2 threads, specifically run the appropriate type of job if (Pthread_create (&thread,&attr,bioprocessbackgroundjobs,arg)!= 0) {R
Edislog (redis_warning, "Fatal:can ' t initialize Background Jobs.");
Exit (1);
//Assign value to the corresponding thread bio_threads[j] = thread;
}
}
That is, after performing the above actions, 2 threads are running in the bio_threads thread, and the corresponding waiting Jo is taken out of the respective job list;
/* Create a background job to initialize the
/void biocreatebackgroundjob (int type, void *arg1, void *arg2, void *arg3) {struct Bio_ by passing in 3 parameters)
Job *job = zmalloc (sizeof (*job));
Job->time = time (NULL);
JOB->ARG1 = arg1;
JOB->ARG2 = arg2;
JOB->ARG3 = Arg3;
Pthread_mutex_lock (&bio_mutex[type]);
Add the corresponding job type list
listaddnodetail (bio_jobs[type],job);
The waiting job number increased by 1
bio_pending[type]++;
Pthread_cond_signal (&bio_condvar[type]);
Pthread_mutex_unlock (&bio_mutex[type]);
The simple creation background job operation, above utilizes the mutex variable to implement the thread synchronization operation, guarantees the thread to be safe. Here's a look at the most important implementation of the background job (omitting some of the code):
/* Perform background job with parameters containing the type/void *bioprocessbackgroundjobs (void *arg) {... while (1) {ListNode *ln; /* The loop always starts with the lock hold. */if (Listlength (bio_jobs[type]) = = 0) {pthread_cond_wait (&bio_condvar[type],&bio_mutex[type
]);
Continue }/* Pop the job from the queue.
///////Remove the first job from the list of jobs ln = Listfirst (Bio_jobs[type]);
Job = ln->value;
/* It is now possible to unlock the background system as we know have * a stand alone job structure to process.*/
Pthread_mutex_unlock (&bio_mutex[type]); /* Process the job accordingly to its type.
////Execute specific work if (type = = Redis_bio_close_file) {Close ((long) job->arg1);
else if (type = = Redis_bio_aof_fsync) {aof_fsync (long) job->arg1);
else {redispanic ("wrong job type in Bioprocessbackgroundjobs ()."); }
Zfree (Job); /* Lock again before reiterating the loop, if there are no longer * jobs to process we ' ll blocks again in Pthread_ Cond_wait ().
* * Pthread_mutex_lock (&bio_mutex[type]);
Listdelnode (BIO_JOBS[TYPE],LN);
bio_pending[type]--;
}
}
While loop, remove one from the queue and perform an action. Of course, if you want to stop all background threads at once, you can execute the following method, calling the
Pthread_cancel:
/* Kill The running bio threads in a unclean way. This function should is
* used only when it's critical to stop the threads for some reason.
* Currently Redis does this is only crash (for instance on SIGSEGV) in order
* To perform a fast memory check without Other threads messing with memory. */
* kill all threads
in the background/void biokillthreads (void) {
int err, J;
for (j = 0; J < Redis_bio_num_ops; J + +) {
//Call Pthread_cancel method to kill the current background thread
if (Pthread_cancel (BIO_THREADS[J)) = = 0) {
if ((Err = Pthread_join (bio_threads[j],null))!= 0) {
redislog (redis_warning,
"Bio thread for job Ty PE #%d can be joined:%s ",
J, Strerror (err));
} else {
Redislog (redis_warning,
Bio thread for job type #%d terminated ", j);}
}
}
}