I created a program to get all files in a directory, find the individual checksums and then find the total checksums using multithreading.
I am receiving a segmentation fault so I ran gdb and saw that the error is on line 60 where open() is. After researching the seg fault on SO, and on other forums, I attempted to implement a few different approaches such as changing open() to fopen() with a FILE *handle rather than an int. That change proved incorrect.
After hours of debugging and searching, I am clueless and would greatly appreciate any insight.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdarg.h>
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#include <dirent.h>
#include <pthread.h> ///Compile with -pthread or -lpthread
#include <sys/stat.h>
#define BUFFER_SIZE (1<<16)
void cleanup();
void get_filenames();
void* get_checksum();
char **filenames;
int file_cnt;
DIR *dir;
//int handle;
FILE *handle;
unsigned int checksum;
unsigned char* ptr;
int length;
int count;
unsigned char* buffer;
int* sum;
unsigned int total = 0;
int main(int argc, char *argv[]){
int i;
pthread_t* file;
atexit(cleanup);
get_filenames();
printf("There are %d files:\n", file_cnt);
file = calloc(sizeof(pthread_t), file_cnt);
sum = calloc(sizeof(int), file_cnt);
for(i=0; i<file_cnt; i++){
printf("%s\n", filenames[i]);
pthread_create(&(file[i]), NULL, get_checksum, (void*)&filenames[i]);
}
for(i=0; i<file_cnt; i++){
total += sum[i];
}
printf("total is: %u\n", total);
return EXIT_SUCCESS;
}
void* get_checksum(void* a){
int b = *((int *)a);
//handle = open(filenames[b], O_RDONLY); //SEG FAULT HERE
handle = fopen(filenames[b], "r"); //SEG FAULT HERE
if( handle == NULL ){
printf( "Can't open file: %s\n", filenames[b]);
exit(1);
}
buffer = malloc(BUFFER_SIZE);
if( buffer == NULL ){
printf( "Can't get enough memory\n" );
exit(1);
}
checksum = 0;
do{
//length = read( handle, buffer, BUFFER_SIZE );
length = read( handle, buffer, (sizeof(char)));
if( length == -1 ){
printf( "Error reading file: %s\n", filenames[b]);
//return NULL;
exit(1);
}
ptr = buffer;
count = length;
while( count-- ){
checksum = checksum + (unsigned int)( *ptr++ );
sum[b] = checksum;
}
} while( length );
printf("Checksum= %d\nTimes at: %d\n", checksum, (int)clock());
}
void cleanup() {
if(filenames && file_cnt > 0) {
while(file_cnt-- > 0) {
if(filenames[file_cnt]) {
free(filenames[file_cnt]);
}
}
free(filenames);
}
if(dir) {
closedir(dir);
}
return;
}
void get_filenames() {
struct dirent *dir_entry;
if((dir = opendir(".")) == NULL) {
fprintf(stderr, "Couldn't open the directory entry for reading\n");
exit(1);
}
errno = 0;
file_cnt = 0;
while((dir_entry = readdir(dir)) != NULL) {
char **new_filenames = filenames;
static int realative_dirs = 0;
if(realative_dirs < 2 &&
(strcmp(".", dir_entry->d_name) == 0 || strcmp("..", dir_entry->d_name) == 0)
) {
realative_dirs++;
continue;
}
new_filenames = (char **)realloc(filenames, sizeof(char **) * (file_cnt + 1));
if(new_filenames == NULL) {
free(filenames[file_cnt]);
fprintf(stderr, "Could not allocate reference for filename[%d]\n", file_cnt);
exit(1);
}
filenames = new_filenames;
filenames[file_cnt] = (char *)calloc(strlen(dir_entry->d_name) + 1, sizeof(char));
if(filenames[file_cnt] == NULL) {
fprintf(stderr, "Could not allocate memory for filename[%d]'s string: \"%s\"\n",
file_cnt, dir_entry->d_name);
exit(1);
}
strcpy(filenames[file_cnt], dir_entry->d_name);
file_cnt++;
}
if(errno != 0) {
fprintf(stderr, "An error occured getting the filenam list\n");
exit(1);
}
return;
}
Below is the output and gdb debugging:
There are 24 files:
.windows
.xscreensaver
.alias
.cshrc
Segmentation fault
(gdb) run
Starting program: /home/nolooking/a.out
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
There are 24 files:
.windows
[New Thread 0x7ffff781e700 (LWP 15957)]
.xscreensaver
[New Thread 0x7ffff701d700 (LWP 15958)]
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff781e700 (LWP 15957)]
0x0000000000400d53 in get_checksum (a=0x60b610) at checksum.c:60
60 handle = open(filenames[b], O_RDONLY);
(gdb) backtrace
#0 0x0000000000400d53 in get_checksum (a=0x60b610) at checksum.c:60
#1 0x00007ffff7bc6374 in start_thread () from /lib64/libpthread.so.0
#2 0x00007ffff7907c3d in clone () from /lib64/libc.so.6
(gdb) quit
A debugging session is active.
UPDATE:
I took the advice of one user in the comments who suggested that I use:
handle=fopen((char*)a, "r");. I can successfully print out the file names when the if statement if(handle==NULL) is commented out. When I include that if statement I receive the following output:
There are 24 files:
.windows
.xscreensaver
.alias
.cshrc
Can't open file: p▒`
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdarg.h>
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#include <dirent.h>
#include <pthread.h>
#include <sys/stat.h>
#define BUFFER_SIZE (1<<16)
void cleanup();
void get_filenames();
void* get_checksum();
char **filenames;
int file_cnt;
DIR *dir;
//int handle;
FILE *handle;
unsigned int checksum;
unsigned char* ptr;
int length;
int count;
unsigned char* buffer;
int* sum;
unsigned int total = 0;
int main(int argc, char *argv[]){
int i;
pthread_t* file;
atexit(cleanup);
get_filenames();
printf("There are %d files:\n", file_cnt);
file = calloc(sizeof(pthread_t), file_cnt);
sum = calloc(sizeof(int), file_cnt);
for(i=0; i<file_cnt; i++){
printf("%s\n", filenames[i]);
pthread_create(&(file[i]), NULL, get_checksum, (void*)&filenames[i]);
}
for(i=0; i<file_cnt; i++){
total += sum[i];
}
printf("total is: %u\n", total);
return EXIT_SUCCESS;
}
void* get_checksum(void* a){
int b = *((int *)a);
handle = fopen(((char*)a), "r");
if( handle == NULL ){
printf( "Can't open file: %s\n", ((char*)a));
exit(1);
}
buffer = malloc(BUFFER_SIZE);
if( buffer == NULL ){
printf( "Can't get enough memory\n" );
exit(1);
}
checksum = 0;
do{
length = read( handle, buffer, BUFFER_SIZE );
if( length == -1 ){
printf( "Error reading file: %s\n", ((char*)a));
//return NULL;
exit(1);
}
ptr = buffer;
count = length;
while( count-- ){
checksum = checksum + (unsigned int)( *ptr++ );
//sum[a] = checksum;
}
} while( length );
printf("Checksum= %d\nTimes at: %d\n", checksum, (int)clock());
}
void cleanup() {
if(filenames && file_cnt > 0) {
while(file_cnt-- > 0) {
if(filenames[file_cnt]) {
free(filenames[file_cnt]);
}
}
free(filenames);
}
if(dir) {
closedir(dir);
}
return;
}
void get_filenames() {
struct dirent *dir_entry;
if((dir = opendir(".")) == NULL) {
fprintf(stderr, "Couldn't open the directory entry for reading\n");
exit(1);
}
errno = 0;
file_cnt = 0;
while((dir_entry = readdir(dir)) != NULL) {
char **new_filenames = filenames;
static int realative_dirs = 0;
if(realative_dirs < 2 &&
(strcmp(".", dir_entry->d_name) == 0 || strcmp("..", dir_entry->d_name) == 0)
) {
realative_dirs++;
continue;
}
new_filenames = (char **)realloc(filenames, sizeof(char **) * (file_cnt + 1));
if(new_filenames == NULL) {
free(filenames[file_cnt]);
fprintf(stderr, "Could not allocate reference for filename[%d]\n", file_cnt);
exit(1);
}
filenames = new_filenames;
filenames[file_cnt] = (char *)calloc(strlen(dir_entry->d_name) + 1, sizeof(char));
if(filenames[file_cnt] == NULL) {
fprintf(stderr, "Could not allocate memory for filename[%d]'s string: \"%s\"\n",
file_cnt, dir_entry->d_name);
exit(1);
}
strcpy(filenames[file_cnt], dir_entry->d_name);
file_cnt++;
}
if(errno != 0) {
fprintf(stderr, "An error occured getting the filenam list\n");
exit(1);
}
return;
}
Why I am receiving that output once I uncomment the if statement?
Change this
pthread_create(&(file[i]), NULL, get_checksum, (void*)&filenames[i]);
to be
pthread_create(&(file[i]), NULL, get_checksum, (void*)i);
and this
int b = *((int *)a);
to be
int b = (int)a;
Also you cannot call read() on a FILE* as it is returned by fopen(). Use fread() instead.
Don't use &i. I'll explain in a bit. The argument you're passing to the thread is wrong a is not an integer. It's meant to be a pointer to a string...
Change the thread create to this...
pthread_create(&(file[i]), NULL, get_checksum, filenames[i]);
then print the string as follows...
void* get_checksum(void *a){
char *file_name = (char *)a;
printf("filename=%s\n", file_name);
You're passing the string as a pointer to the called function. In your code you're trying to use this as an index into the array.
If you want to pass the index as an integer beware... this won't work..
pthread_create(&(file[i]), NULL, get_checksum, &i);
This is multithreaded and the value pointed to by &i is changing as the loop runs. Pass the pointer to the string and do not under any circumstances change filenames as the threads run.
I think your problem is simply because you are passing &filenames[i] instead of simply &i.
Then in void* get_checksum(void* a) you are trying to use a char* as an int.
The code would be more like :
for(i=0; i<file_cnt; i++){
printf("%s\n", filenames[i]);
pthread_create(&(file[i]), NULL, get_checksum, (void*)&i);
}
and in void* get_checksum(void* a) :
int b = *((int *)a);
handle = fopen(filenames[b], "r");
if( handle == NULL ){
printf( "Can't open file: %s\n", filenames[b]);
exit(1);
}
Related
Currently I am attempting to parse words from all text files in a directory (in this case it is safe to assume there will only be text files within the directory). It seems as though I am able to open the file within the threads function, however I am unable to grab the text within. No error messages are being presented but the printf within splitInput is not printing to the terminal.
Forgive my semantic work within the code, I am a fresh novice with C! Along with this there may be unused code within main as this will be part of a larger project. I appreciate the assistance in advance!
#include <stdlib.h>
#include <dirent.h>
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include "queue.h"
void* splitInput(void *filename) {
printf("Thread %s Created\n", (char*)filename);
FILE *file;
int i = 0;
char *cp;
char *bp;
char line[255];
char *array[5000];
file = fopen((char*)filename, "r");
if(file == NULL) {
perror("Error opening file");
}
printf("Opened File %s\n", (char*)filename);
while(fgets(line, sizeof(line), file) != NULL) {
bp = line;
while(1) {
cp = strtok(bp, ",.!? \n");
bp = NULL;
if(cp == NULL) {
break;
}
array[i++] = cp;
printf("Check print - word %i:%s:\n", i-1, cp);
}
}
fclose(file);
return 0;
}
int main(int argc, char *argv[]) {
DIR* d;
struct dirent* e;
// grab our queueSize and threadCount
int queueSize = atoi(argv[2]);
int threadCount = atoi(argv[3]);
// var for creating a thread each file
int i = 0;
// open the dir
d = opendir(argv[1]);
printf("Queue Size: %d\n", queueSize);
printf("Thread Count: %d\n", threadCount);
// set our thread count now that we know how many files are in dir
pthread_t threads[threadCount];
// read through our directory
while((e = readdir(d)) != NULL) {
// make sure we aren't reading . and ..
if(strcmp(e->d_name, ".") == 0) {
continue;
}
if(strcmp(e->d_name, "..") == 0) {
continue;
}
printf("entered file %s\n", e->d_name);
char *filename = strdup(e->d_name);
if(i < threadCount) {
// create our threads
pthread_create(&threads[i], NULL, splitInput, filename);
}
// increment i
i++;
}
// join our existing threads
for(int j = 0; j < i; j++) {
pthread_join(threads[j], NULL);
}
return 0;
}
Current Output
device#user:~/os/testdir$ ./output ~/os/testdir/test 10 10 output
Queue Size: 10
Thread Count: 10
entered file test
Thread test Created
Opened File test
Found the answer, I was attempting to open a file outside my working directory which cannot be done without the full path. Changing the working directory to the parameter given solved the issue. This can be done in this case with chdir(argv[1])
Revised code below.
#include <stdlib.h>
#include <dirent.h>
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include "queue.h"
void* splitInput(void *filename) {
printf("Thread %s Created\n", (char*)filename);
FILE *file;
int i = 0;
char *cp;
char *bp;
char line[255];
char *array[5000];
file = fopen((char*)filename, "r");
if(file == NULL) {
perror("Error opening file");
}
printf("Opened File %s\n", (char*)filename);
while(fgets(line, sizeof(line), file) != NULL) {
bp = line;
while(1) {
cp = strtok(bp, ",.!? \n");
bp = NULL;
if(cp == NULL) {
break;
}
array[i++] = cp;
printf("Check print - word %i:%s:\n", i-1, cp);
}
}
fclose(file);
return 0;
}
int main(int argc, char *argv[]) {
DIR* d;
struct dirent* e;
// grab our queueSize and threadCount
int queueSize = atoi(argv[2]);
int threadCount = atoi(argv[3]);
// var for creating a thread each file
int i = 0;
// open the dir
chdir(argv[1]);
d = opendir(argv[1]);
printf("Queue Size: %d\n", queueSize);
printf("Thread Count: %d\n", threadCount);
// set our thread count now that we know how many files are in dir
pthread_t threads[threadCount];
// read through our directory
while((e = readdir(d)) != NULL) {
// make sure we aren't reading . and ..
if(strcmp(e->d_name, ".") == 0) {
continue;
}
if(strcmp(e->d_name, "..") == 0) {
continue;
}
printf("entered file %s\n", e->d_name);
char *filename = strdup(e->d_name);
if(i < threadCount) {
// create our threads
pthread_create(&threads[i], NULL, splitInput, filename);
}
// increment i
i++;
}
// join our existing threads
for(int j = 0; j < i; j++) {
pthread_join(threads[j], NULL);
}
return 0;
}
My program currently is traversing through directories finding mp3 files. In each thread, I'm finding mp3 files found in each directory. When I find an mp3 file, I'm inserting the file into an array.
Problem: I'm getting different segmentation faults every time I run the program.
I believe my pthread_mutex_lock and pthread_mutex_unlock are positioned in the correct area but I don't understand why my program keeps failing. Any help or advice is appreciated!
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
void *func(void *);
char **data;
int data_length;
char **directories;
int length;
int num = 0;
int directory_accum = 0;
unsigned long i = 0;
struct dirent *entry;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
void listFilesRecursively(char *path);
int main(int argc, char **argv)
{
// INITIALIZE DATA TO NULL
data = NULL;
directories = NULL;
if (argc != 2)
{
printf("Must give a path\n");
return -1;
}
// SWITCH CURRENT WORKING DIRECTORY TO DIR GIVEN ON COMMAND LINE
chdir(argv[1]); // JUST ONCE
// DIRECTORY POINTER
// OPEN THE CURRENT WORKING DIRECTORY
DIR *current = opendir(".");
//printf("%lx\n", (unsigned long)current);
int directoriessize = 1;
// read entries and output based on type
while( (entry = readdir(current)) )
{
if (entry->d_type == DT_DIR)
{
if((strcmp(entry->d_name, ".") != 0) && (strcmp(entry->d_name, "..") != 0))
{
printf("IN MAIN FUNCTION:\n");
directoriessize++;
directories = realloc(directories, sizeof(char*) * directoriessize);
directories[directory_accum] = malloc(sizeof(char*));
printf("%s is a directory\n", entry->d_name);
directories[directory_accum] = entry->d_name;
directory_accum++;
//listFilesRecursively(entry->d_name);
printf("\n");
}
} else if (entry->d_type == DT_REG) {
printf("IN MAIN FUNCTION:\n");
printf("%s is a regular file\n\n", entry->d_name);
} else {
printf("IN MAIN FUNCTION:\n");
printf("%s is idk\n\n", entry->d_name);
}
}
printf("\n");
printf("# of Directories: %d\n\n", directory_accum);
for(int i = 0; i < directory_accum; i++)
{
printf("Directories in array: %s\n", directories[i]);
}
printf("\n");
pthread_t tids[directory_accum];
// initialize data to NULL
data = NULL;
// start all of the threads ( AFTER SEEING HOW MANY DIRECTORIES YOU HAVE)
// FOR EXAMPLE ONLY START TWO THREADS FOR a AND b in top. c DIRECOTRY WILL BE
// HELD INSIDE THE b THREAD
for(i = 0; i < directory_accum; ++i)
{
int err = pthread_create(&tids[i], NULL, func, (void**) directories[i]);
if (err != 0)
{
fprintf(stderr, "! Couldn't create thread");
}
}
// wait for threads and join them
for(int i = 0; i < directory_accum; ++i)
{
pthread_join(tids[i], NULL);
}
// print out data
for(int i = 0; i < length; ++i)
{
printf("%s", data[i]);
free(data[i]);
}
free(data);
return 0;
}
// THREAD FUNCTION
// RECURSAL TRAVERSAL OF THE DIRECTORIES HAPPEN HERE OR MAYBE IN SECONDAY FUNCTION
//void *func(void *arg)
void *func(void *arg)
{
char *directory = arg;
// critical section : (LOCKS THIS SO ONLY ONE FUNCTION WILL RUN THIS AT THE TIME)
//pthread_mutex_lock(&lock);
printf("NEW THREAD\n");
listFilesRecursively(directory);
printf("\n\n");
//pthread_mutex_unlock(&lock);
return NULL;
}
void listFilesRecursively(char *basePath)
{
char path[1000];
DIR *current = opendir(basePath);
// Unable to open directory stream
if (!current)
return;
while ((entry = readdir(current)) != NULL)
{
if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0)
{
// Construct new path from our base path
strcpy(path, basePath);
strcat(path, "/");
strcat(path, entry->d_name);
printf("Path: %s\n", path);
if(entry->d_type == DT_REG)
{
pthread_mutex_lock(&lock);
printf("------------------\n");
printf("Writing %s to file\n", entry->d_name);
printf("------------------\n");
if(length < (num + 1))
{
length = num + 1;
}
data = realloc(data, sizeof(char*) * length);
data[num] = entry->d_name;
num++;
pthread_mutex_unlock(&lock);
}
listFilesRecursively(path);
}
}
closedir(current);
return;
}
Here are two problems in the program
First, is that when I uncomment the pthread_join() in the main function, there will be a seg fault, other wise the program will run...
Second, is that the output file will be missing the first letter of each word that has stored in the global variable words from last read file. So, for example, there are two files:
one has words "abc abc abc abc abc abc abc abc".
the second has words "def def"
if i input 5 for the second argument when calling a.out, the output in the output file will be
abc
abc
abc
abc
abc
bc
bc
bc
def
def
This is also a werid thing I could not figure out why.
/* main.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <ctype.h>
#include <pthread.h>
#include "hw3.h"
int index_;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
typedef struct files
{
char *inputfile;
FILE * outputfile;
} files;
void * readFile( void *arg ){
files *info = (files *)arg;
char fileName[80];
strncat(fileName, (info->inputfile), 79);
fileName[80] = '\0';
FILE *outputfd = info->outputfile;
FILE* fd;
fd = fopen(fileName, "r");
if ( fd == NULL) {
fprintf(stderr, "ERROR:<open() failed>\n");
}
printf("TID %d: Opened \"%s\"\n", (unsigned int)pthread_self(), fileName);
fflush(stdout);
int rc;
char ch[1] = {0};
char word[80] = {0};
ch[0] = fgetc(fd);
pthread_mutex_lock(&mutex);
while( ch[0] != EOF){
if( isalnum(ch[0]) ){
// char str = ch[0];
strncat(word, ch, 1);
}
else{//it's a word
if( strlen( word ) >= 2 ){
words[index_] = word;
printf("TID %d: Stored \"%s\" in shared buffer at index [%d]\n",(unsigned int)pthread_self(), word, index_ );
if( index_+ 1 == maxwords ){
index_ = 0;
printf("MAIN: Buffer is full; writing %d words to output file\n", maxwords);
for( unsigned int i = 0; i<maxwords; i++ ){
rc = fwrite( words[i], 1, sizeof(words[i]), outputfd );
fwrite( "\n", 1, sizeof("\n"), outputfd );
if( rc == -1 ){
fprintf(stderr, "ERRPR:<write() failed>\n");
//return EXIT_FAILURE;
}
}
}
else{
index_ ++;
}
}
for(int i = 0; i< strlen(word); i++){
word[i] = '\0';
}
}
ch[0] = fgetc(fd);
}
pthread_mutex_unlock(&mutex);
printf("TID %d: Closed \"%s\"; and exiting\n", (unsigned int)pthread_self(), fileName );
fclose(fd);
pthread_exit( NULL );
}
int main( int argc, char * argv[] ){
if(argc != 4){
fprintf(stderr, "ERROR: Invalid arguments\nUSAGE: ./a.out <input-directory> <buffer-size> <output-file>\n");
return EXIT_FAILURE;
}
//dynamically allocated words buffer with argument 2
maxwords = atoi(argv[2]);
words = (char**)calloc(maxwords, sizeof(char*) );
if ( words == NULL)
{
fprintf( stderr, "ERROR:<word calloc() failed\n>" );
return EXIT_FAILURE;
}
printf("MAIN: Dynamically allocated memory to store %d words\n", maxwords);
fflush(stdout);
//open/create output file of the third argument
FILE* outputfd = fopen (argv[3], "w");
if ( outputfd == NULL )
{
perror( "open() failed" );
return EXIT_FAILURE;
}
DIR * dir = opendir( argv[1] );
if(dir == NULL){
perror("ERRPR:<opendir() failed>");
return EXIT_FAILURE;
}
chdir(argv[1]);
printf("MAIN: Opened \"%s\" directory\n", argv[1]);
fflush(stdout);
pthread_t tid[10];
index_ = 0;
int i = 0;//files index
struct dirent * file;
//files allfiles[20];
char fileName[80];
int rc;
//-----------------------------------------------------------------------
// while loop reads all files in the directory
while ( ( file = readdir( dir ) ) != NULL )
{
struct stat buf;
rc = lstat( file->d_name, &buf ); /* e.g., "xyz.txt" */
/* ==> "assignments/xyz.txt" */
if ( rc == -1 ){
fprintf(stderr, "ERRPR:<lstat() failed>\n");
return EXIT_FAILURE;
}
if ( S_ISREG( buf.st_mode ) )
{
// printf( " -- regular file\n" );
// fflush(stdout);
strncpy(fileName, file->d_name, 79);
files info;
info.inputfile = fileName;
info.outputfile = outputfd;
//printf("%d",i);
printf("MAIN: Created child thread for \"%s\"\n",fileName);
rc = pthread_create( &tid[i], NULL, readFile,(void *)&info );
sleep(1);
i++
}
else if ( S_ISDIR( buf.st_mode ) )
{
// printf( " -- directory\n" );
// fflush(stdout);
}
else
{
// printf( " -- other file\n" );
// fflush(stdout);
}
}
closedir(dir);
printf("MAIN: Closed \"%s\" directory\n", argv[1]);
fflush(stdout);
printf("MAIN: Created \"%s\" output file\n",argv[3]);
fflush(stdout);
//-----------------------------------------------------------------------
for( int j = 0; j<i; j++){
printf( "MAIN: Joined child thread: %u\n", (unsigned int)tid[j] );
pthread_join(tid[i], NULL);
}
for( unsigned int i = 0; i<index_; i++ ){
int rc = fwrite( words[i], 1, sizeof(words[i]), outputfd );
if( rc == -1 ){
fprintf(stderr, "ERRPR:<write() failed>\n");
return EXIT_FAILURE;
}
}
printf( "MAIN: All threads are done; writing %d words to output file\n", index_);
fflush(stdout);
free( words );
fclose( outputfd );
return EXIT_SUCCESS;
}
This here is the whole program, and there is a header file which is just two global variab
char ** words = NULL;
/* global/shared integer specifying the size */
/* of the words array (from argv[2]) */
int maxwords;
Thanks to everyone for the help!
You need separate info objects for each thread. Right now, all of the threads get the same info object, which you change in between creating threads, and therefore, for most of them, by the time they get a chance to look at the name of the file they are supposed to process, it has been changed.
The segmentation fault is being caused by code you have not shown us, so I can't help you with that except to suggest that you apply valgrind.
Here are two more bugs:
char fileName[80];
strncat(fileName, (info->inputfile), 79);
You can only concatenate onto a string, not an unitialized array of characters that may or may not contain a valid string.
char ch[1] = {0};
char word[80] = {0};
ch[0] = fgetc(fd);
pthread_mutex_lock(&mutex);
while( ch[0] != EOF){
The fgets function returns an integer that will be EOF on end of file, otherwise it returns the character value. You convert it to a char and then compare the char to EOF. But that makes no sense since EOF is the integer value that represents end of file. Once cast to a character, it is a valid character that could have been read from the file since the file can contain any characters and "end of file" is not a character.
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.
This code receives a input file with 10 filenames, stores them into an 2d array and creates 10+1 threads: a requester and 10 converters. This is only a skeleton, so my threads only print their id, thei're not accessing any global or shared variable or something that requires a mutex. So, what should I do to avoid a segmentation fault here?
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <string.h>
#include <time.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/time.h>
#include <math.h>
#include <unistd.h>
#include <errno.h>
#define READ_BUFFER_LEN 16
//#define MAX_LENGTH 128
enum {
MAX_LENGTH = 512
};
typedef struct FileNameArray {
size_t nfiles; /* Number of file names allocated and in use */
size_t maxfiles; /* Number of entries allocated in array */
char **files; /* Array of file names */
} FileNameArray;
//GLOBAL vars
int num_images, *threads_ids /*threads ids*/;
pthread_t *threads;
void deallocate2D(FileNameArray *names) {
size_t i;
for (i = 0; i < names->nfiles; i++)
free(names->files[i]);
free(names->files);
names->nfiles = 0;
names->files = 0;
names->maxfiles = 0;
}
int readInputFile(FILE *fp, FileNameArray *names) {
num_images = names->nfiles;
int max_lines = names->maxfiles;
char **file_names = names->files;
char line[MAX_LENGTH];
char **final_filenames, **array;
while (fgets(line, sizeof line, fp) != NULL) {
if (line[0] != '\n') {
/* Remove newline from end of file name */
char *nl = strchr(line, '\n');
if (nl != 0)
*nl = '\0';
if (num_images >= max_lines) {
max_lines += 100;
array = realloc(file_names, max_lines * sizeof (char*));
if (array == NULL) {
fprintf(stderr, "Error reallocating space for 2d array: %s\n",
strerror(errno));
return -1;
}
names->maxfiles = max_lines;
names->files = array;
file_names = array;
}
if ((file_names[num_images] = malloc(strlen(line) + 1)) == NULL) {
fprintf(stderr, "Error allocating space for 2d array: %s\n",
strerror(errno));
return -1;
}
names->nfiles++;
strcpy(file_names[num_images], line);
printf("name of file %d is: %s \n", num_images, file_names[num_images]);
num_images++;
}
}
printf("Num_lines: %d\n", num_images);
//realloc to number of lines in the file, to avoid wasting memory
if ((final_filenames = realloc(file_names, num_images * sizeof (char*))) == NULL) {
fprintf(stderr, "Error reallocating space for 2d array: %s\n",
strerror(errno));
return -1;
}
names->maxfiles = num_images;
names->files = final_filenames;
return 0;
}
void* requester(void* arg) {
printf("This is the requester thread\n");
//sleep(1);
pthread_exit(NULL);
return NULL;
}
void* converter(void *id) {
int my_id = *((int*) id);
printf("Thread's id is: %d\n", my_id);
pthread_exit(NULL);
return NULL;
}
int main(int argc, char *argv[]) {
FileNameArray names = {0, 0, 0};
int i, rc;
pthread_attr_t attr;
//check parameters
if (argc < 4) {
fprintf(stderr, "Usage: %s input_filename.ppm charWidth charHeight\n",
argv[0]);
return -1;
}
printf("Opening input file [%s]\n", argv[1]);
FILE *fpin = fopen(argv[1], "r");
if (fpin == NULL) {
fprintf(stderr, "Could not open input file %s (%s)\n",
argv[1], strerror(errno));
return -1;
}
if ((names.files = malloc(10 * sizeof (char*))) == NULL) {
fprintf(stderr, "Error allocating initial space for 2d array: %s\n",
strerror(errno));
return -1;
}
names.maxfiles = 10;
if (readInputFile(fpin, &names) == -1) {
fprintf(stderr, "Error reading image filenames from input\n");
return -1;
}
threads = (pthread_t*) malloc(num_images + 1 * sizeof (pthread_t));
threads_ids = (int*) malloc(num_images + 1 * sizeof (int));
/* Initialize and set thread detached attribute */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
//creates requester thread, the 1st one on the list
pthread_create(&threads[0], &attr, requester, &threads_ids[0]);
threads_ids[0] = 0;
//creates so many converter threads as the number of images to convert
for (i = 1; i < num_images + 1; i++) {
threads_ids[i] = i;
rc = pthread_create(&threads[i], &attr, converter, &threads_ids[i]);
if (rc) {
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}
/* Free attribute and wait for the other threads */
pthread_attr_destroy(&attr);
for (i = 0; i < num_images + 1; i++) {
printf("Waiting for thread nr %d\n", i);
rc = pthread_join(threads[i], NULL);
if (rc) {
printf("ERROR; return code from pthread_join() is %d\n", rc);
exit(-1);
}
}
fclose(fpin);
free(threads);
free(threads_ids);
printf("###########\n");
deallocate2D(&names);
printf("Done!\n");
return 0;
}
This is one of the possible ouputs:
Num_lines: 10
This is the requester thread
Thread's id is: 1
Thread's id is: 2
Thread's id is: 3
Thread's id is: 5
Thread's id is: 4
Thread's id is: 6
Thread's id is: 7
Thread's id is: 8
Thread's id is: 9
Waiting for thread nr 0
Thread's id is: 10
Segmentation fault
and this is the result of gdb:
Program received signal SIGSEGV, Segmentation fault.
0x001bfea9 in *__GI___libc_free (mem=0x804c278) at malloc.c:3724
3724 malloc.c: No such file or directory.
in malloc.c
(gdb) where
#0 0x001bfea9 in *__GI___libc_free (mem=0x804c278) at malloc.c:3724
#1 0x00120802 in *__GI__dl_deallocate_tls (tcb=<value optimized out>,
dealloc_tcb=112) at dl-tls.c:487
#2 0x00133748 in __free_stacks (limit=41943040) at allocatestack.c:274
#3 0x00133849 in queue_stack (pd=0xb5fd9b70) at allocatestack.c:302
#4 __deallocate_stack (pd=0xb5fd9b70) at allocatestack.c:740
#5 0x00134b37 in pthread_join (threadid=3053296496, thread_return=0x0)
at pthread_join.c:110
#6 0x0804955c in main (argc=4, argv=0xbffff4a4) at main.c:261
threads = (pthread_t*) malloc(num_images + 1 * sizeof (pthread_t));
threads_ids = (int*) malloc(num_images + 1 * sizeof (int));
Certainly you must have intended to write
threads = malloc((num_images + 1) * sizeof (pthread_t));
threads_ids = malloc((num_images + 1) * sizeof (int));
or
threads = calloc(num_images + 1, sizeof (pthread_t));
threads_ids = calloc(num_images + 1, sizeof (int));
instead?