Upload on github

parents
This diff is collapsed.
all:
gcc -Wall -D_GNU_SOURCE -fpic -shared -o pinthread.so pinthread.c -ldl -lpthread
gcc -Wall -lpthread dotprod_mutex.c -o dotprod_mutex
install:
install -m 0644 pinthread.so /usr/lib
ldconfig
clean:
rm -f pinthread.so
rm -f dotprod_mutex
## NOTE:
Developement of pinthread is on https://git.devuan.org/packages-base/pinthread
Here on github there is only a stub and it can be outdated.
For any issue, merge request or other suggestion, please
use Devuan gitlab.
## pinthread
pinthread is a little library intended for use with LD_PRELOAD environment var.
I've wrote it cause in devuan we intesively use qemu-static to build packages for
different archs, but it doesn't work well with some processes using threads
when the base host is multicore, so, this library will override the
pthread_create() call and will pin the threads and the main process
to run only on a specific core, making qemu happy and avoiding a lot
of segfaults.
### Build:
just type make and, optionally, make install
It will build a library (pinthread.so) and a binary
(dotprod_mutex)
dotprod_mutex is just a test binary you can use for testing the library.
### Usage:
LD_PRELOAD=/path/to/pinthread.so [binary_to_launch]
Environment variables:
* PINTHREAD_CORE: set to the core number you want to pin to.
if omitted, it defaults to the result of
sched_getcpu()
* PINTHREAD_PNAMES: accept a space separated list of strings,
if any string match the process name or
PINTHREAD_PNAMES is empty/omitted, it will
pin all thread according to PINTHREAD_CORE,
otherwise it will just call the "real"
pthread_create without any pinning.
### Known bugs:
When used in qemu-static for arm* architectures setting
PINTHREAD_CORE is mandatory as internally we use the
sched_getcpu() syscall that isn't (yet?) supported in qemu.
/*****************************************************************************
* FILE: dotprod_mutex.c
* DESCRIPTION:
* This example program illustrates the use of mutex variables
* in a threads program. This version was obtained by modifying the
* serial version of the program (dotprod_serial.c) which performs a
* dot product. The main data is made available to all threads through
* a globally accessible structure. Each thread works on a different
* part of the data. The main thread waits for all the threads to complete
* their computations, and then it prints the resulting sum.
* SOURCE: Vijay Sonnad, IBM
* LAST REVISED: 01/29/09 Blaise Barney
* gcc -Wall -lpthread dotprod_mutex.c -o dotprod_mutex
******************************************************************************/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
/*
The following structure contains the necessary information
to allow the function "dotprod" to access its input data and
place its output into the structure. This structure is
unchanged from the sequential version.
*/
typedef struct
{
double *a;
double *b;
double sum;
int veclen;
} DOTDATA;
/* Define globally accessible variables and a mutex */
#define NUMTHRDS 10
#define VECLEN 100000
DOTDATA dotstr;
pthread_t callThd[NUMTHRDS];
pthread_mutex_t mutexsum;
/*
The function dotprod is activated when the thread is created.
As before, all input to this routine is obtained from a structure
of type DOTDATA and all output from this function is written into
this structure. The benefit of this approach is apparent for the
multi-threaded program: when a thread is created we pass a single
argument to the activated function - typically this argument
is a thread number. All the other information required by the
function is accessed from the globally accessible structure.
*/
void *dotprod(void *arg)
{
/* Define and use local variables for convenience */
int i, start, end, len ;
long offset;
double mysum, *x, *y;
offset = (long)arg;
len = dotstr.veclen;
start = offset*len;
end = start + len;
x = dotstr.a;
y = dotstr.b;
/*
Perform the dot product and assign result
to the appropriate variable in the structure.
*/
mysum = 0;
for (i=start; i<end ; i++)
{
mysum += (x[i] * y[i]);
}
/*
Lock a mutex prior to updating the value in the shared
structure, and unlock it upon updating.
*/
pthread_mutex_lock (&mutexsum);
dotstr.sum += mysum;
printf("Thread %ld did %d to %d: mysum=%f global sum=%f\n",offset,start,end,mysum,dotstr.sum);
pthread_mutex_unlock (&mutexsum);
pthread_exit((void*) 0);
}
/*
The main program creates threads which do all the work and then
print out result upon completion. Before creating the threads,
The input data is created. Since all threads update a shared structure, we
need a mutex for mutual exclusion. The main thread needs to wait for
all threads to complete, it waits for each one of the threads. We specify
a thread attribute value that allow the main thread to join with the
threads it creates. Note also that we free up handles when they are
no longer needed.
*/
int main (int argc, char *argv[])
{
long i;
double *a, *b;
void *status;
pthread_attr_t attr;
/* Assign storage and initialize values */
a = (double*) malloc (NUMTHRDS*VECLEN*sizeof(double));
b = (double*) malloc (NUMTHRDS*VECLEN*sizeof(double));
for (i=0; i<VECLEN*NUMTHRDS; i++) {
a[i]=1;
b[i]=a[i];
}
dotstr.veclen = VECLEN;
dotstr.a = a;
dotstr.b = b;
dotstr.sum=0;
pthread_mutex_init(&mutexsum, NULL);
/* Create threads to perform the dotproduct */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
for(i=0;i<NUMTHRDS;i++)
{
/* Each thread works on a different set of data.
* The offset is specified by 'i'. The size of
* the data for each thread is indicated by VECLEN.
*/
pthread_create(&callThd[i], &attr, dotprod, (void *)i);
}
pthread_attr_destroy(&attr);
/* Wait on the other threads */
for(i=0;i<NUMTHRDS;i++) {
pthread_join(callThd[i], &status);
}
/* After joining, print out the results and cleanup */
printf ("Sum = %f \n", dotstr.sum);
free (a);
free (b);
pthread_mutex_destroy(&mutexsum);
pthread_exit(NULL);
}
/*
* Copyright (c) 2015 Franco (nextime) Lanza <nextime@nexlab.it>
*
* pinthread [https://git.devuan.org/packages-base/pinthread]
*
* pinthread is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*************************************************************************************
*
* compile with :
* $ gcc -Wall -D_GNU_SOURCE -fpic -shared -o pinthread.so pinthread.c -ldl -lpthread
*
* and test it with :
* LD_PRELOAD=/path/to/pinthread.so something
*
* Environment variables:
* - PINTHREAD_CORE: set to the core number you want to pin to.
* if omitted, it defaults to the result of
* sched_getcpu()
*
* - PINTHREAD_PNAMES: accept a space separated list of strings,
* if any string match the process name or
* PINTHREAD_PNAMES is empty/omitted, it will
* pin all thread according to PINTHREAD_CORE,
* otherwise it will just call the "real"
* pthread_create without any pinning.
*
*/
//#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <dlfcn.h>
#include <sched.h> //cpu_set_t , CPU_SET
#include <libgen.h>
#include <stdbool.h>
#include <string.h>
static char *procname;
static bool pinthread_override = false;
static unsigned int ncore;
static unsigned int setcore;
static int (*real_pthread_create)(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
static void main_pinthread(int argc, char* argv[], char* envp[])
{
char *pch;
char *msg;
cpu_set_t mask; /* Define your cpu_set bit mask. */
CPU_ZERO(&mask); /* Initialize it all to 0, i.e. no CPUs selected. */
procname = basename(argv[0]);
msg = getenv("PINTHREAD_PNAMES");
if(msg == NULL)
{
pinthread_override = true;
} else {
pch = strtok (msg," ");
while (pch != NULL)
{
if (!strcmp(procname,pch))
{
pinthread_override = true;
pch = NULL;
} else {
pch = strtok (NULL," ");
}
}
}
ncore = sysconf (_SC_NPROCESSORS_CONF);
msg = getenv("PINTHREAD_CORE");
if (msg != NULL)
{
setcore = (unsigned int) strtoul(msg, (char **)NULL, 10);
if(setcore >= ncore)
{
fprintf(stderr, "E:PINTHREAD wrong value for PINTHREAD_CORE: %u - using default.\n", setcore);
setcore = sched_getcpu();
}
} else {
setcore = sched_getcpu();
}
CPU_SET(setcore, &mask);
// make sure the main thread is running on the same core:
sched_setaffinity(getpid(), sizeof(mask), &mask);
real_pthread_create = dlsym(RTLD_NEXT,"pthread_create");
if ((msg=dlerror())!=NULL)
fprintf(stderr, "E:PINTHREAD pthread_create dlsym failed : %s\n", msg);
//printf("*wrapping done\n");
}
__attribute__((section(".init_array"))) void (* p_main_pinthread)(int,char*[],char*[]) = &main_pinthread;
int pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg)
{
int ret;
cpu_set_t mask; /* Define your cpu_set bit mask. */
CPU_ZERO(&mask); /* Initialize it all to 0, i.e. no CPUs selected. */
CPU_SET(setcore, &mask);
//printf("*about to call original pthread_create\n");
ret = real_pthread_create(thread,attr,start_routine,arg);
if(pinthread_override)
pthread_setaffinity_np(*thread, sizeof(mask), &mask);
return ret;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment