Using multithreads to copy a single file at the same time, outputting the same file down to the MD5 check sum - c

The problem I'm running into is that when the file tries to copy, it only copies part of the file and the other part is a bunch of unreadable characters. This is for an academic assignment that wants us to use barrier synchronization so we need to use open, write, and read.
I've reworked the thread function many times but if it's the problem I can change it again, I haven't changed the for loop in main at all so even that might be the problem but I don't know what it could be. Lastly, I don't really know what to do with the barrier; my professor was very vague and I can't really ask him questions, maybe the barrier is the part that I'm truly missing.
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <semaphore.h>
#include <pthread.h>
#include <unistd.h>
typedef struct args {
int fd;
int copy;
long int start;
long int size;
}threadarg;
int barrier = 0;
int main(int argc, char *argv[])
{
void usage(char *progname);
int chkdst(char **argv);
void die(char *why);
long int filesize(char *srcpath);
void buildpath(char *src, char *dst, char **dstpath);
int isvalid(char *path, char *dst);
void *dowork(void *arg);
if (argc < 4) usage("a8");
int workers, i;
char *check;
workers = strtol(argv[3], &check, 10);
if (!check) usage("a8");
else if (!chkdst(&argv[2])) die ("DST not valid!");
long int size = filesize(argv[1]);
if (size == -1) die ("Could not find file size");
char *dstpath; buildpath(argv[1], argv[2], &dstpath);
if (!isvalid(argv[1], dstpath)) die ("scr not valid!");
long int chunksize = size / workers;
long int remainder = size % workers;
int fd = open(argv[1], O_RDONLY);
int copy = open(dstpath, O_CREAT | O_RDWR, 0644);
if (fd < 0 || copy < 0) die("Fail to access or create files");
barrier = workers;
threadarg threadargs[workers];
pthread_t threads[workers];
for (i = 0; i < workers; i++)
{
threadargs[i].fd = fd;
threadargs[i].copy = copy;
threadargs[i].start = i * chunksize;
if (i == workers - 1)
threadargs[i].size = chunksize + remainder;
else
threadargs[i].size = chunksize;
if (pthread_create(&threads[i], NULL, dowork, (void *) &threadargs[i]))
die("Thread Creation Failure");
}
for (i = 0; i < workers; i++)
pthread_join(threads[i], NULL);
}
void usage(char *progname)
{
fprintf(stderr, "./%s srcpath dstpath workercount\n", progname);
exit(0);
}
void die(char *why)
{
fprintf(stderr, "Program Killed...\nReason: %s\n", why);
exit(1);
}
long int filesize(char *srcpath)
{
struct stat st;
if(stat(srcpath, &st) != 0) return 0;
return st.st_size;
}
/*
void domd5(char *path)
{
}
*/
void *dowork(void *arg)
{
threadarg *args = (threadarg *)arg;
int fd = args->fd,
copy = args->copy, rd;
long int start = args->start,
size = args->size;
char bufs[2048], *remains;
lseek(fd, start, SEEK_SET);
lseek(copy, start, SEEK_SET);
printf("%d thread with offset %ldKB, reached barrier\n", (int) pthread_self(), start);
barrier--;
while (barrier > 0);
long int count = 0, remainder = 0, i;
for (i = 0; i < size; i += 2048)
{
if (i + 2048 > size)
{
remainder = size - count;
remains = malloc(remainder * sizeof(char));
rd = read (fd, remains, sizeof(remains));
if (write(copy, remains, rd) != rd)
die("Error accessing files during copy");
count += remainder;
}
else
{
rd = read(fd, bufs, sizeof(bufs));
if (write(copy, bufs, rd) != rd)
die("Error accessing files during copy");
count += 2048;
}
}
pthread_exit(NULL);
}
/* Takes a single pointer, *argv, and passes it to isdir()
to check if the directory exists. If isdir returns a 1 a
1 is returned from this module. Otherwise, an error message
is printed and a 0 is returned.
Calls isdir().
Called by main().
*/
int chkdst(char **argv)
{
int isdir(char *path);
if (isdir(*argv)) return 1;
return 0;
}
/* Uses the Stat struct to construct a struct, sbuf,
and uses stat() to obtain information from the file and
write it to sbuf. Uses S_ISDIR() on sbuf.st_mode to see
the mode of the file. A 1 is returned if the file is a
directory otherwise a 0 is returned.
Called by isvalid().
*/
int isdir(char *path)
{
struct stat sbuf;
if (stat(path, &sbuf)) return 0;
return S_ISDIR(sbuf.st_mode);
}
/* Uses the Stat struct to construct a struct, sbuf,
and uses stat() to obtain information from the file and
write it to sbuf. Uses S_ISREG on sbuf.st_mode to see if
the file is regular. A 1 is returned if the S_ISREG is true
otherwise a 0 is returned.
Called by isvalid().
*/
int isregular(char *path)
{
struct stat sbuf;
if (stat(path, &sbuf)) return 0;
return S_ISREG(sbuf.st_mode);
}
/* Checks if the source path is a directory first, then if its
a regular file return 0 if it is dir and if it isn't a regular
file, then checks if the destionation path was created or if
the file exist at the destination if either return 0, if none
of these return 1.
Calls isdir() and isregular().
Called by copyfiles().
*/
int isvalid(char *path, char *dst)
{
if (isdir(path))
{
return 0;
}
else if (!isregular(path))
{
return 0;
}
else if (dst == NULL)
{
return 0;
}
else if (isregular(dst))
{
return 0;
}
return 1;
}
/* Builds destination-path using strrchr() function from library,
dstpath is null on error and defined otherwise. The src file has
its original destination removed and replaced with the new one if
it has a original destination on it otherwise it is just added to
the end of the existing name of the file.
Called by copyfiles().
*/
void buildpath(char *src, char *dst, char **dstpath)
{
char *ptr;
int n;
ptr = strrchr(src, '/');
if (ptr) n = strlen(dst) + strlen(ptr) + 2;
else n = strlen(dst) + strlen(src) + 2;
*dstpath = malloc(n);
if (!dstpath) return;
if (ptr)
{
strcpy(*dstpath, dst);
strcat(*dstpath, ptr);
}
else
{
strcpy(*dstpath, dst);
strcat(*dstpath, "/");
strcat(*dstpath, src);
}
}

Related

Message queues: Bad file descriptor in notification

I've created a table of mq file descriptors and I'm trying to pass numbers from stdin by one of them.
I'm using notification using threads and when a number occures in one of the queues it should print for example "Number: 1 from queue: 3".
Here's my code:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <mqueue.h>
#define MAX_LENGTH 20
#define ERR(source) (\
fprintf(stderr, "%s:%d\n", __FILE__, __LINE__),\
perror(source),\
kill(0, SIGKILL),\
exit(EXIT_FAILURE)\
)
static void not_func(union sigval sv) {
mqd_t queue;
uint8_t number;
unsigned msg_prio;
queue = *((mqd_t*)sv.sival_ptr);
static struct sigevent not;
not.sigev_notify = SIGEV_THREAD;
not.sigev_notify_function = not_func;
not.sigev_value.sival_ptr = &queue;
if(mq_notify(queue, &not)<0) ERR("mq_notify");
for(;;) {
if(mq_receive(queue, (char*)&number, 1, &msg_prio)<1) {
if(errno == EAGAIN) break;
else ERR("mq_receive");
printf("Number: %d from queue: %d", number, msg_prio);
}
}
}
void get_queue_name(int nr, char *str) {
snprintf(str, MAX_LENGTH, "/queue%d", nr);
}
mqd_t create_message_queue(int nr) {
mqd_t queue;
char name[MAX_LENGTH] = "";
get_queue_name(nr, name);
struct mq_attr attr;
attr.mq_maxmsg = 10;
attr.mq_msgsize = 1;
if((queue = TEMP_FAILURE_RETRY(mq_open(name, O_RDWR|O_NONBLOCK|O_CREAT, 0600, &attr))) == (mqd_t)-1) ERR("mq open in");
static struct sigevent not;
not.sigev_notify = SIGEV_THREAD;
not.sigev_notify_function = not_func;
not.sigev_value.sival_ptr = &queue;
if(mq_notify(queue, &not)<0) ERR("mq_notify");
return queue;
}
void delete_message_queue(mqd_t queue, int nr) {
char name[MAX_LENGTH] = "";
get_queue_name(nr, name);
mq_close(queue);
if(mq_unlink(name)) ERR("mq_unlink");
}
void usage(void) {
fprintf(stderr, "USAGE: mqueue n\n");
fprintf(stderr, "100 > n > 0 - number of children\n");
exit(EXIT_FAILURE);
}
int main(int argc, char **argv) {
int n, i;
char strnumber[MAX_LENGTH];
int number;
mqd_t *queues;
srand(time(NULL));
if(argc != 2) usage();
n = atoi(argv[1]);
if(n<=0 || n>=100) usage();
queues = (mqd_t*)malloc(sizeof(mqd_t) * n);
if(queues == NULL) ERR("malloc");
for(i = 0; i < n; i++) {
queues[i] = create_message_queue(i+1);
}
while(fgets(strnumber, MAX_LENGTH, stdin)!=NULL) {
number = (uint8_t)atoi(strnumber);
if(number<=0) continue;
int randomQueue = rand()%n;
if(TEMP_FAILURE_RETRY(mq_send(queues[randomQueue], (const char *)&number, 1, (unsigned)randomQueue))) ERR("mq_send");
}
for(i = 0; i < n; i++) {
delete_message_queue(queues[i], i+1);
}
free(queues);
return EXIT_SUCCESS;
}
When I execute my code nothing happens:
or I have such an error:
You pass a pointer to queue (which is a local variable) to the thread (via not.sigev_value.sival_ptr) which runs after that variable goes out of scope. So it gets a dangling pointer.
Either pass the descriptor by value (if it fits in sigval; it should), or store it on the heap (with new/malloc) and pass that pointer.

How to read the same file byte by byte asynchronously from within a few threads?

I am trying to read a file with aio.h byte by byte using aio_read with a number of threads. But I don't know if I am on the right track since there are not so many stuff to read on the Internet.
I have just created a worker function to pass it to my threads. And also as an argument to pass to the thread, I created a struct called thread_arguments and I pass a few necessary arguments in it, which will be needed for aiocb such as offset, file_path to open, buffer size, and priority.
I can read a file with one thread from start to end successfully. But when it comes to reading a file as chunks from within a few threads, I couldn't make it. And I am not even sure if I can do that with aio->reqprio without using semaphores or mutexes. (Trying to open a file from within a few threads at the same time?)
How can I read a few number of bytes from within a few threads asynchronously?
Let's say the file contains "foobarquax" and we have three threads using aio library.
Then first one should read "foo",
the second should read "bar" and
the last one should read "quax" asynchronously.
You can see screenshots of issues regarding running it with multiple threads on here
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <aio.h>
#include <string.h>
#include <fcntl.h> // open -> file descriptor O_RDONLY, O_WRONLY, O_RDWR
#include <errno.h>
#include <unistd.h>
typedef struct thread_args {
char *source_path;
char *destination_path;
long int buffer_size;
long int buffer_size_last; // buffer size for the last thread in case there is a remainder.
long int offset;
int priority;
} t_args;
void *worker(void *thread_args) {
t_args *arguments = (t_args *) thread_args;
struct aiocb *aiocbRead;
aiocbRead = calloc(1, sizeof(struct aiocb));
aiocbRead->aio_fildes = open(arguments->source_path, O_RDONLY);
if (aiocbRead->aio_fildes == -1) {
printf("Error");
}
printf("opened on descriptor %d\n", aiocbRead->aio_fildes);
aiocbRead->aio_buf = malloc(sizeof(arguments->buffer_size));
aiocbRead->aio_offset = arguments->offset;
aiocbRead->aio_nbytes = (size_t) arguments->buffer_size;
aiocbRead->aio_reqprio = arguments->priority;
int s = aio_read(aiocbRead);
if (s == -1) {
printf("There was an error");
}
while (aio_error(aiocbRead) == EINPROGRESS) {}
printf("Bytes read %ld", aio_return(aiocbRead));
close(aiocbRead->aio_fildes);
for (int i = 0; i < arguments->buffer_size ; ++i) {
printf("%c\n", (*((char *) aiocbRead->aio_buf + i)));
}
}
// Returns a random alphabetic character
char getrandomChar() {
int letterTypeFlag = (rand() % 2);
if (letterTypeFlag)
return (char) ('a' + (rand() % 26));
else
return (char) ('A' + (rand() % 26));
}
void createRandomFile(char *source, int numberofBytes) {
FILE *fp = fopen(source, "w");
for (int i = 0; i < numberofBytes; i++) {
fprintf(fp, "%c", getrandomChar());
}
fclose(fp);
}
int main(int argc, char *argv[]) {
char *source_path = argv[1];
char *destination_path = argv[2];
long int nthreads = strtol(argv[3], NULL, 10);
// Set the seed.
srand(time(NULL));
// Get random number of bytes to write to create the random file.
int numberofBytes = 10 + (rand() % 100000001);
// Create a random filled file at the source path.
createRandomFile(source_path, 100);
// Calculate the payload for each thread.
long int payload = 100 / nthreads;
long int payloadLast = payload + 100 % nthreads;
// Create a thread argument to pass to pthread.
t_args *thread_arguments = (t_args *) malloc(nthreads * sizeof(t_args));
for (int l = 0; l < nthreads; ++l) {
// Set arguments in the struct.
(&thread_arguments)[l]->source_path = source_path;
(&thread_arguments)[l]->destination_path = destination_path;
(&thread_arguments)[l]->buffer_size = payload;
(&thread_arguments)[l]->buffer_size_last = payloadLast;
(&thread_arguments)[l]->offset = l * payload;
(&thread_arguments)[l]->priority = l;
}
pthread_t tID[nthreads];
// Create pthreads.
for (int i = 0; i < nthreads; ++i) {
pthread_create(&tID[i], NULL, worker, (void *) &thread_arguments[i]);
}
// Wait for pthreads to be done.
for (int j = 0; j < nthreads; ++j) {
pthread_join(tID[j], NULL);
}
free(thread_arguments);
return 0;
}
This code is reading succesfully if I just call it from one thread but doesn't work if I use it for more than one threads which is what I want.

print garbage values from the shared memory

I am currently working on a producer-consumer implementation using C.
First, I create a buffer on the shared memory of a variable length that is given by the user in the consumer process.
Then, in the producer process, I need to access the shared memory and puts new data to the buffer so the consumer can consume.
Below is the consumer code:
#include "common.h"
#include <unistd.h>
int fd;
int errno;
int MY_LEN = 0;
Shared* shared_mem;
char *job[4];
int setup_shared_memory(){
fd = shm_open(MY_SHM, O_CREAT | O_RDWR, 0666);
if(fd == -1){
printf("shm_open() failed\n");
exit(1);
}
ftruncate(fd, sizeof(Shared) + MY_LEN*sizeof(char *));
}
int attach_shared_memory(){
shared_mem = (Shared*) mmap(NULL, sizeof(Shared) + MY_LEN*sizeof(char *), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if(shared_mem == MAP_FAILED){
printf("mmap() failed\n");
exit(1);
}
return 0;
}
int init_shared_memory() {
shared_mem->data = 0;
int i;
for(i = 0; i < shared_mem->length; i++)
{
shared_mem->arr[i] = 0;
// shared_mem->arr[i] = (char *)calloc(1, sizeof(char*));
}
sem_init(&(shared_mem->mutex), 1, 1);
}
int init_job(){
int i;
for(i = 0; i < 4; i++)
{
job[i] = (char *)malloc(sizeof(char *));
}
}
int take_a_job(int index){
init_job();
char *ds = strdup(shared_mem->arr[index]);
job[0] = strtok(ds, "-");
int i = 1;
while(i < 4)
{
job[i] = strtok(NULL, "-");
i++;
}
// remove the job from the buffer
shared_mem->arr[index] = NULL;
}
int consume_job(int index){
printf("\nPrinter starts printing the job %s, %s pages from Buffer[%d]. The duration is %s seconds and the source is %s.\n",job[3], job[2], index, job[1], job[0]);
sleep(atoi(job[1])); // sleep for job[1] seconds.
}
int main(int args, char *argv[]) {
setup_shared_memory();
attach_shared_memory();
init_shared_memory();
MY_LEN = atoi(argv[1]); // the first parameter following ./printer = the length of the buffer
shared_mem->length = MY_LEN;
//shared_mem->arr = (int*) &shared_mem->arr;
int index = 1;
*(shared_mem->arr) = "1-10-5-6";
*(shared_mem->arr + 1) = "2-5-2-7";
*(shared_mem->arr + 2) = "3-20-10-8";
*(shared_mem->arr + 3) = "4-7-4-9";
take_a_job(index);
int i;
for(i = 0; i < shared_mem->length; i++){
printf("\n\n%d set %s\n", i, shared_mem->arr[i]);
}
consume_job(index);
printf("\n\nHello second check\n\n");
while (1) {}
return 0;
}
Here is the producer code:
#include "common.h"
int fd;
Shared* shared_mem;
char *job;
int setup_shared_memory(){
fd = shm_open(MY_SHM, O_RDWR, 0666);
if(fd == -1){
printf("shm_open() failed\n");
exit(1);
}
}
int attach_shared_memory(){
shared_mem = (Shared*) mmap(NULL, sizeof(Shared), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if(shared_mem == MAP_FAILED){
printf("mmap() failed\n");
exit(1);
}
return 0;
}
int create_a_job(int args, char *argv[]){
int i;
job = (char *)calloc(8, sizeof(char *));
if(args != 5)
return 0; //the parameters are not correctly formatted
else{
for(i = 1; i < args; i++)
{
if(i > 1)
strcat(job, "-");
strcat(job, argv[i]);
}
}
strcat(job, "\0");
printf("\nthe job is %s\n", job);
}
int put_a_job(){
printf("shared_mem->length is %d\n\n", shared_mem->length);
int i;
for(i = 0; i < shared_mem->length; i++)
{
if(*(shared_mem->arr + i) == 0)
{
//shared_mem->arr[i] = (char *)malloc(sizeof(job));
//strcpy(shared_mem->arr[i], job);
*(shared_mem->arr + i) = (char *)job;
printf("\n\nThe index is %d\n", i);
//printf("\n\nthe argument is %s at %d\n", job, i);
return i;
}
}
printf("\n\nThe index is %d\n", i);
}
int main(int args, char *argv[]) {
setup_shared_memory();
attach_shared_memory();
// create a job with the parameters
int result = create_a_job(args, argv);
if(result == 0)
{
printf("Not the right parameters.\n");
printf("Plase enter client ID, job duration, number of pages and job ID.\n");
return 0;
}
int i;
put_a_job();
for (i=0; i < shared_mem->length; i++) {
printf("the argument is %s at %d\n", (char *)(shared_mem->arr + i), i);
}
printf("\n\n");
return 0;
}
The common.h file is
#ifndef _INCLUDE_COMMON_H_
#define _INCLUDE_COMMON_H_
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
// from `man shm_open`
#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <fcntl.h> /* For O_* constants */
#include <string.h>
#include <semaphore.h>
#define MY_SHM "/JIT"
typedef struct {
sem_t mutex;
int data;
int length; // the length of the buffer
char *arr[0];
} Shared;
#endif //_INCLUDE_COMMON_H_
I first run ./consumer 10 & to allocate a buffer of length 10 and after, I run ./producer 1 2 3 4 to put the job to the buffer and print the buffer, I got garbage values
Any help would be really appreciated! Thank you!
Instruction
*(shared_mem->arr + i) = (char *)job;
is storing the pointer job into the shared mem, not the pointed value.
Maybe you want to use a strncpy.
You cannot share memory address between processes, because of Linux uses virtual memory. To make the story short an address in a process is not valid for a different process.
Be aware that you have a memory leakage because you never call free() for the allocated job.

How to List Active Ports and Processes using them in Linux, C Code

I am trying to write a C Code to do the same Job as:
netstat -vatp
List all Remote/Local Addresses and Processes using them. But I dunno which files should I be reading?
I tried looking into /proc/net/tcp and /proc/net/udp, but they don't have the process name or process identifier like netstat displays it!
Thanks.
You could check the source code http://freecode.com/projects/net-tools. Just download, unpack the bz2 file and you'll find the netstat.c source code
Quick analyse:
/proc/net/tcp for example has an inode tab, in /proc there is a subfolder for each of these inodes, which contains the information you need.
Some more analysing:
I think it's even worse. netstat just loops through the /proc directory and checks the contents of the numeric sub-directories to find the actual process matching the inode. Not sure as I'm just analysing
http://linux.die.net/man/5/proc is very nice reading :)
For your answer, see How can i match each /proc/net/tcp entry to each opened socket?
You could call the netstat application from within your code. Have a look at execve to capture stdout and stderr.
EDIT:
Since code says more than words:
IEFTask.h
#ifndef IEFTASK_H
#define IEFTASK_H
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <signal.h>
#include <assert.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* MARK: Structure */
struct IEFTask {
const char **arguments; /* last argument should be NULL */
int standardInput;
void *callbackArgument;
void (*callback)(int term, char *out, size_t outLen,
char *err, size_t errLen, void *arg);
};
typedef struct IEFTask IEFTask;
/* MARK: Running */
int
IEFTaskRun(IEFTask *theTask);
#endif /* IEFTASK_H */
IEFTask.c
#include "IEFTask.h"
/* MARK: DECLARATION: Data Conversion */
char *
IEFTaskCreateBufferFromPipe(int fd, size_t *bufLen);
/* MARK: Running */
int
IEFTaskRun(IEFTask *myTask) {
pid_t pid;
int exitStatus, status;
int outPipe[2], errPipe[2];
assert(myTask != NULL);
/* Create stdout and stderr pipes */
{
status = pipe(outPipe);
if(status != 0) {
return -1;
}
status = pipe(errPipe);
if(status != 0) {
close(errPipe[0]);
close(errPipe[1]);
return -1;
}
}
/* Fork the process and wait pid */
{
pid = fork();
if(pid < 0) { /* error */
return -1;
} else if(pid > 0) { /* parent */
waitpid(pid, &exitStatus, 0);
exitStatus = WEXITSTATUS(exitStatus);
} else { /* child */
/* close unneeded pipes */
close(outPipe[0]);
close(errPipe[0]);
/* redirect stdout, stdin, stderr */
if(myTask->standardInput >= 0) {
close(STDIN_FILENO);
dup2(myTask->standardInput, STDIN_FILENO);
close(myTask->standardInput);
}
close(STDOUT_FILENO);
dup2(outPipe[1], STDOUT_FILENO);
close(outPipe[1]);
close(STDERR_FILENO);
dup2(errPipe[1], STDERR_FILENO);
close(errPipe[1]);
execve(myTask->arguments[0],
(char *const *)myTask->arguments, NULL);
exit(127);
}
}
/* Parent continues */
{
char *output, *error;
size_t outLen, errLen;
/* 127 = execve failed */
if(exitStatus == 127) {
close(errPipe[0]);
close(errPipe[1]);
close(outPipe[0]);
close(outPipe[1]);
return -1;
}
/* Read in data */
close(errPipe[1]);
close(outPipe[1]);
output = IEFTaskCreateBufferFromPipe(outPipe[0], &outLen);
error = IEFTaskCreateBufferFromPipe(errPipe[0], &errLen);
close(errPipe[0]);
close(outPipe[0]);
/* Call callback */
(*myTask->callback)(exitStatus,
output, outLen,
error, errLen, myTask->callbackArgument);
if(output) free(output);
if(error) free(error);
}
return 0;
}
/* MARK: Data Conversion */
#define READ_BUF_SIZE (128)
char *
IEFTaskCreateBufferFromPipe(int fd, size_t *bufLen) {
ssize_t totalRead = 0, nowRead;
char readBuffer[READ_BUF_SIZE], *myBuffer = NULL;
char *ptr;
while(1) {
nowRead = read(fd, readBuffer, READ_BUF_SIZE);
if(nowRead == -1) {
free(myBuffer);
return NULL;
} else if(nowRead == 0) {
break;
} else {
ptr = realloc(myBuffer, totalRead + nowRead);
if(ptr == NULL) {
free(myBuffer);
return NULL;
}
myBuffer = ptr;
memcpy(&(myBuffer[totalRead]), readBuffer, nowRead);
totalRead += nowRead;
}
}
if(bufLen) *bufLen = (size_t)totalRead;
return myBuffer;
}
main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "IEFTask.h"
void taskCallback(int term,
char *out, size_t outlen,
char *err, size_t errlen)
{
char *ptr;
printf("Task terminated: %d\n", term);
ptr = malloc(outlen + 1);
memcpy(ptr, out, outlen);
ptr[outlen] = '\0';
printf("***STDOUT:\n%s\n***END\n", ptr);
free(ptr);
ptr = malloc(errlen + 1);
memcpy(ptr, err, errlen);
ptr[errlen] = '\0';
printf("***STDERR:\n%s\n***END\n", ptr);
free(ptr);
}
int main() {
const char *arguments[] = {
"/bin/echo",
"Hello",
"World",
NULL
};
IEFTask myTask;
myTask.arguments = arguments;
myTask.standardInput = -1;
myTask.callback = &taskCallback;
int status;
status = IEFTaskRun(&myTask);
if(status != 0) {
printf("Failed: %s\n", strerror(errno));
}
return 0;
}

linux kernel aio functionality

I am testing kernel asynchronous io functions (not posix aio) and am trying to figure out how it works. The code below is a complete program where I simply write an array repeatedly to a file opened using O_DIRECT. I get an error in the callback function "write missed bytes expect 1024 got 0" (see the fprintf statement in work_done()).
For those not familiar with kernel aio, the code below does the following:
Init some structs
Prepare aio (io_prep_pwrite)
Submit io requests (io_submit)
Check for event completion (io_getevents)
Call a callback function to see if everything went ok.
I get an error at step 5. If I do not open the file using O_DIRECT, things work fine, but it beats the purpose of having async writes.
Can someone tell me what I am doing wrong? Is this the correct usage of kernel aio, for example, is my use of callbacks correct? Are there any restrictions on the usage of O_DIRECT?
I compile using 'gcc -Wall test.c -laio'
Thanks in advance.
/*
* File: myaiocp.c
* Author: kmehta
*
* Created on July 11, 2011, 12:50 PM
*
*
* Testing kernel aio.
* Program creates a 2D matrix and writes it multiple times to create a file of desired size.
* Writes are performed using kernel aio functions (io_prep_pwrite, io_submit, etc.)
*/
#define _GNU_SOURCE
#define _XOPEN_SOURCE 600
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <pthread.h>
#include <fcntl.h>
#include <string.h>
#include <sys/uio.h>
#include <sys/time.h>
#include <omp.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <libaio.h>
char ** buf;
long seg_size;
int seg_rows;
double total_size;
char * filename;
static int wait_count = 0;
void io_task();
void cleanup();
void allocate_2D_matrix(int[]);
int file_open(char *);
void wr_done(io_context_t ctx, struct iocb* iocb, long res, long res2);
int main(int argc, char **argv) {
total_size = 1048576; //1MB
seg_size = 1024; //1kB
seg_rows = 1024;
filename = "aio.out";
int dims[] = {seg_rows, seg_size};
allocate_2D_matrix(dims); //Creates 2D matrix
io_task();
cleanup();
return 0;
}
/*
* Create a 2D matrix
*/
void allocate_2D_matrix(int dims[2]) {
int i;
char *data;
//create the matrix
data = (char *) calloc(1, dims[0] * dims[1] * sizeof (char));
if (data == NULL) {
printf("\nCould not allocate memory for matrix.\n");
exit(1);
}
buf = (char **) malloc(dims[0] * sizeof (char *));
if (buf == NULL) {
printf("\nCould not allocate memory for matrix.\n");
exit(1);
}
for (i = 0; i < dims[0]; i++) {
buf[i] = &(data[i * dims[1]]);
}
}
static void io_error(const char *func, int rc)
{
if (rc == -ENOSYS)
fprintf(stderr, "AIO not in this kernel\n");
else if (rc < 0)
fprintf(stderr, "%s: %s\n", func, strerror(-rc));
else
fprintf(stderr, "%s: error %d\n", func, rc);
exit(1);
}
/*
* Callback function
*/
static void work_done(io_context_t ctx, struct iocb *iocb, long res, long res2)
{
if (res2 != 0) {
io_error("aio write", res2);
}
if (res != iocb->u.c.nbytes) {
fprintf(stderr, "write missed bytes expect %lu got %ld\n",
iocb->u.c.nbytes, res2);
exit(1);
}
wait_count --;
printf("%d ", wait_count);
}
/*
* Wait routine. Get events and call the callback function work_done()
*/
int io_wait_run(io_context_t ctx, long iter)
{
struct io_event events[iter];
struct io_event *ep;
int ret, n;
/*
* get up to aio_maxio events at a time.
*/
ret = n = io_getevents(ctx, iter, iter, events, NULL);
printf("got %d events\n", n);
/*
* Call the callback functions for each event.
*/
for (ep = events ; n-- > 0 ; ep++) {
io_callback_t cb = (io_callback_t)ep->data ; struct iocb *iocb = ep->obj ; cb(ctx, iocb, ep->res, ep->res2);
}
return ret;
}
void io_task() {
long offset = 0;
int bufIndex = 0;
//Open file
int fd = file_open(filename);
//Initialize structures
long i;
long iter = total_size / seg_size; //No. of iterations to reach desired file size (total_size)
io_context_t myctx;
if(0 != io_queue_init(iter, &myctx))
{
perror("Could not initialize io queue");
exit(EXIT_FAILURE);
}
struct iocb * ioq[iter];
//loop through iter times to reach desired file size
for (i = 0; i < iter; i++) {
struct iocb *io = (struct iocb*) malloc(sizeof (struct iocb));
io_prep_pwrite(io, fd, buf[bufIndex], seg_size, offset);
io_set_callback(io, work_done);
ioq[i] = io;
offset += seg_size;
bufIndex ++;
if (bufIndex > seg_rows - 1) //If entire matrix written, start again from index 0
bufIndex = 0;
}
printf("done preparing. Now submitting..\n");
if(iter != io_submit(myctx, iter, ioq))
{
perror("Failure on submit");
exit(EXIT_FAILURE);
}
printf("now awaiting completion..\n");
wait_count = iter;
int res;
while (wait_count) {
res = io_wait_run(myctx, iter);
if (res < 0)
io_error("io_wait_run", res);
}
close(fd);
}
void cleanup() {
free(buf[0]);
free(buf);
}
int file_open(char *filename) {
int fd;
if (-1 == (fd = open(filename, O_DIRECT | O_CREAT | O_WRONLY | O_TRUNC, 0666))) {
printf("\nError opening file. \n");
exit(-1);
}
return fd;
}
First of all, good job using libaio instead of POSIX aio.
Are there any restrictions on the usage of O_DIRECT ?
I'm not 100% sure this is the real problem, but O_DIRECT has some requirements (quoting mostly from TLPI):
The data buffer being transferred must be aligned on a memory boundary that is a multiple of the block size (use posix_memalign)
The file or device offset at which data transfer commences must be a multiple of the block size
The length of the data to be transferred must be a multiple of the block size
At a glance, I can see you are not taking aby precautions to align memory in allocate_2D_matrix.
If I do not open the file using O_DIRECT, things work fine, but it
beats the purpose of having async writes.
This happens not to be the case. Asynchronous I/O works well without O_DIRECT (for instance think of the number of system calls slashed).

Resources