I'm trying to put a file in a struct but I'm having problems sharing the memory as I can access the fields in the created process where the mapping happens but cannot access the arrays(can only access the int) in the other processes. I tried a lot of different things but the way I present next is the one that makes more sense to me as I'm allocating memory the right way with shmget.
For clarity: The only thing that is being shared is the integer lim_thread. The other fields are in a area of memory that I can not access. Why?
As I see the pointers are pointing to a region of memory that is shared.
configs.txt:
Threads = 5
Domains = uc.pt; edu
LocalDomain = so.local
NamedPipeEstatisticas = statistics
struct:
typedef struct configs
{
int lim_thread;
char (*valid_domains)[MAX_DOMAIN_SIZE]; //max size for valid domains
char *domain;
char *stat_pipe;
sem_t sem;
} CONFIGS;
main.c:
/*Shared memory for configs*/
CONFIGS *_configs;
int _shmid_configs;
int main(int argc, char *argv[]) {
pid_t config_pid; //will hold the configuration process id
_shmid_configs = shmget(IPC_PRIVATE, sizeof(CONFIGS), IPC_CREAT|0666);
_configs = shmat(_shmid_configs, NULL, 0);
/*Semaphores*/
sem_init( &( _configs->sem), 1, 0);
//initializes processes
if( ( config_pid = fork() ) < 0) {
perror("Failed creating configuration manager process");
num_forks++;
}
else if( config_pid == 0 ) {
init_config();
exit(0);
}
sem_wait(&(_configs->sem));
/////////////////////////DEBUG////////////////////////////////
printf("%d\n", _configs->lim_thread);
printf("%s\n", *(_configs->valid_domains+1));
printf("%s\n", _configs->domain);
printf("%s\n", _configs->stat_pipe);
//////////////////////////////////////////////////////////////
return 0;
}
configs.c
#define MAX_LINE_SIZE 1000
int init_config() {
FILE *fp;
char domains[MAX_LINE_SIZE], line[MAX_LINE_SIZE], *saveptr, *aux_char;
int count = 0, aux;
int temp_shmid;
if( ( fp = fopen( "./configs.txt", "r")) == NULL) {
perror("Failed to open configs.txt");
return -1;
}
fscanf( fp,"Threads = %d\n", &(_configs->lim_thread));
//To start reading "Domains = "
fscanf(fp, "Domains = ");
fgets(domains, MAX_LINE_SIZE, fp);
domains[strlen(domains) -1] = '\0';
//counts the number of domains
for(aux = 0; aux < strlen(domains); aux++) {
if( domains[aux] == ';' ) {
count++;
}
}
//creates shared memory for the valid domains
temp_shmid = shmget(IPC_PRIVATE, (count+1) * sizeof( char[MAX_DOMAIN_SIZE]), IPC_CREAT|0666);
_configs->valid_domains = shmat( temp_shmid, NULL, 0);
//copies all the data to the struct
strcpy( *(_configs->valid_domains), strtok_r(domains, "; ", &saveptr) );
aux = 1;
while( ( aux_char = strtok_r( NULL, "; ", &saveptr) ) != NULL) {
strcpy( *(_configs->valid_domains + aux), aux_char);
aux++;
}
fscanf(fp, "LocalDomain = %s\n", line);
temp_shmid = shmget(IPC_PRIVATE, (strlen(line) + 1) * sizeof(char), IPC_CREAT|0660);
_configs->domain = (char*)shmat(temp_shmid, NULL, 0);
strcpy(_configs->domain, line);
fscanf(fp, "NamedPipeEstatisticas = %s\n", line);
temp_shmid = shmget( IPC_PRIVATE, (strlen(line) +1) * sizeof(char), IPC_CREAT|0660);
_configs->stat_pipe = (char*)shmat(temp_shmid, NULL, 0);
strcpy(_configs->stat_pipe, line);
fclose(fp);
sem_post( &(_configs->sem));
/////////////////////////DEBUG////////////////////////////////
printf("%d\n", _configs->lim_thread);
printf("%s\n", *(_configs->valid_domains+1));
printf("%s\n", _configs->domain);
printf("%s\n", _configs->stat_pipe);
//////////////////////////////////////////////////////////////
return 0;
}
As kaylum pointed out, each process might map a shared memory block to a different virtual address. Hence pointers cannot be shared, you need to work with offsets.
Allocate a single block of shared memory that you divide in two parts: a table of content and a data area. The table of content consists of variables that contain either a value or (instead of a pointer), the offset between the start of the shared memory block and the start of a data element inside the data area.
Then to obtain the address of a data element a process simply adds its offset to the address of the shared memory block in its address space.
You can do what you want. Just do all your shmget/shmat in your main process/thread before you create any threads.
The children will inherit the pointers and the values will be the same for all threads (e.g. the pointers are global. That is, they are not using _thread and are not in thread local storage).
This works fine. I've used it in systems with 50+ threads and it works great
Related
I am facing some issues regarding a realloc with a double pointer dynamic array.
What I would like to perform is to add 2 pointers of type Flight* inside the array schedule of type Flight **.
For that, I am relying on the function add_flight in the Functions.c file.
This function asks the user for the airline and flight number values and stores these data in a new Flight* f. If the schedule is null (no flight yet added) it allocates memory for the newly created flight otherwise it realloc the size of schedule in order the add the new flight.
Main.c file:
int main() {
int choice = 1;
Flight** schedule = NULL;
printf("---AIRPORT MANAGER---");
schedule = add_flight(schedule);
printf("\n%s : %d\n", (*schedule)->airline, (*schedule)->flightNumber);
schedule = add_flight(schedule);
printf("\n%s : %d\n", (*schedule + 1)->airline, (*schedule)->flightNumber);
return 0;
}
Functions.c file :
#include "Functions.h"
void mygets(char* s, int maxLength) {
fflush(stdout);
if (fgets(s, maxLength, stdin) != NULL) {
size_t lastIndex = strlen(s) - 1;
if (s[lastIndex] == '\n')
s[lastIndex] = '\0';
}
}
void flush() {
char buffer;
while ((buffer = getchar()) != EOF && buffer != '\n');
}
Flight** add_flight(Flight** schedule) {
Flight* f;
char buffer[100];
if ((f = (Flight*)malloc(sizeof(Flight*))) == NULL) {
exit(1);
}
printf("\n\n---FLIGHT CREATION---");
printf("\nAirline: ");
mygets(buffer, sizeof(buffer));
if ((f->airline = _strdup(buffer)) == NULL) {
exit(1);
}
memset(buffer, 0, 100);
printf("\nFlight number: ");
scanf("%d", &f->flightNumber);
flush();
if (schedule == NULL) {
if ((schedule = malloc(sizeof(Flight*))) == NULL) {
exit(1);
}
*schedule = f;
}
else {
int numberFlights = ((sizeof(*schedule)) / 4) + 1;
if ((schedule = realloc(schedule, numberFlights * sizeof(Flight*))) == NULL) {
exit(1);
}
*(schedule + numberFlights -1) = f;
}
return schedule;
}
The issue comes when the second call of add_flight is performed in the main.c
In the add_flight function, the data are indeed stored in the new Flight* f and then the else statement is considered: the variable numberFlights gets the value 2. However, the realloc doesn't work, the schedule is not enlarged and thus there is still only the first flight stored inside this schedule array. I can't figure out why the second flight is not added inside the schedule.
Can someone explain me why this realloc fails ?
Thanks for your help :)
The sizeof operator is evaluated at compile time. It cannot be used to determine the size of a dynamically allocated array.
C imposes the burden of keeping track of the actual size of an array onto the programmer. You could kee a separate count variable, but because the actual array and its size belong together, it is useful to store them alongside each other in a struct:
typedef struct Flight Flight;
typedef struct Flights Flights;
struct Flight {
char airline[4];
int number;
char dest[4];
};
struct Flights {
Flight *flight;
int count;
};
Instead of operating on the array, operate on the struct:
void add_flight(Flights *fl,
const char *airline, int number, const char *dest)
{
int n = fl->count++; // n is old count; fl->count is new count
fl->flight = realloc(fl->flight,
(fl->count + 1) * sizeof(*fl->flight));
snprintf(fl->flight[n].airline, 4, "%s", airline);
snprintf(fl->flight[n].dest, 4, "%s", dest);
fl->flight[n].number = number;
}
Intialize the flights struct with NULL and a count of zero and don't forget to release the used memory when you're done:
int main(void)
{
Flights fl = {NULL, 0};
add_flight(&fl, "AF", 5512, "CDG");
add_flight(&fl, "AA", 1100, "ATL");
add_flight(&fl, "LH", 6537, "FRA");
add_flight(&fl, "BA", 8821, "LHR");
add_flight(&fl, "IB", 1081, "EZE");
print_flights(&fl);
free(fl.flight);
return 0;
}
You can see it in action here. Some observations:
There is no need to distinguish between adding the first and subsequent flights, because realloc(NULL, size) behaves exactly like malloc(size).
It is not very efficient to reallocate the memory for each added item. Instead, you pick a suitable initial array size like 4 or 8, then double the size when you hit the limit. That means that the allocated size and the count may differ and you need an aditional memsize field in your flights struct.
The code above relies on manual initialization and destruction. Usually, you will write "constructor" and "destructor" functions to do that for you.
I tried to write a simple database using C. However, I tried to debug my segmentation faults and find the memory pointer obtained through malloc seems changing (name and email pointer seems pointing to different memory locations before and after the Database_load program executes). I have two questions:
Why the memory pointer (name and email) points to different locations before and after Database_load is executed?
Why the program generate a seg fault?
Here is the code related to the problem
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int MAX_DATA;
int MAX_ROWS;
struct Address {
int id;
int set;
//int MAX_DATA;
char *name;
char *email;
};
struct Database {
//int MAX_ROWS;
struct Address *rows;
};
struct Connection{
FILE *file;
struct Database *db;
};
void die(const char *message){
if(errno){
//perror(message);
printf("ERROR: %s\n", message);
}
else{
printf("ERROR: %s\n", message);
}
exit(1);
}
void Address_print(struct Address *addr){
printf("%d %s %s\n", addr->id, addr->name, addr->email);
}
void Database_load(struct Connection *conn){
int i;
int rc = fread(conn->db, sizeof(struct Database), 1, conn->file);
if(rc != 1) die("Failed to load database.");
for (i = 0; i < MAX_ROWS; i++) {
printf("test Database_load loop read rows %p\n", &conn->db->rows[i]);
printf("test Database_load loop read rows name %p\n", &conn->db->rows[i].name);
printf("test Database_load loop read rows email %p\n", &conn->db->rows[i].email);
printf("test Database_load loop read rows name %s\n", conn->db->rows[i].name);
printf("test Database_load loop start %d\n", i);
rc = fread(&conn->db->rows[i], sizeof(struct Address), 1, conn->file);
printf("test Database_load loop read rows %d\n", i);
rc = fread(&conn->db->rows[i].name, sizeof(MAX_DATA), 1, conn->file);
printf("test Database_load loop read name %d\n", i);
rc = fread(&conn->db->rows[i].email, sizeof(MAX_DATA), 1, conn->file);
printf("test Database_load loop read email %d\n", i);
if(rc != 1) die("Failed to load database.");
printf("test Database_load loop\n");
}
}
struct Connection *Database_open(const char *filename, char mode){
int i = 0;
struct Connection *conn = malloc(sizeof(struct Connection));
if(!conn) die("Memory error no connection");;
conn->db = malloc(sizeof(struct Database));
if(!conn->db) die("Memory error no database");
conn->db->rows = malloc(sizeof(struct Address) * MAX_ROWS);
if (conn->db->rows == NULL) die("No memory for rows");
for(i = 0; i < MAX_ROWS; i++){
// make a prototype to initialize it
//struct Address addr = {.id = i, .set = 0};
conn->db->rows[i].id = i;
conn->db->rows[i].set = 0;
conn->db->rows[i].name = malloc(sizeof(char) * MAX_DATA);
if (conn->db->rows[i].name == NULL) die("No memory for name");
conn->db->rows[i].email = malloc(sizeof(char) * MAX_DATA);
if (conn->db->rows[i].email == NULL) die("No memory for email");
// then just assign it
if (i == 0) {
printf("test set name = %p\n", &conn->db->rows[i].name);
printf("test set email = %p\n", &conn->db->rows[i].email);
}
}
if(mode == 'c'){
conn->file = fopen(filename, "w");
}
else{
conn->file = fopen(filename, "r+"); //r+?
if(conn->file){
Database_load(conn);
}
}
if(!conn->file) die("Failed to open the file");
return conn;
}
void Database_close(struct Connection *conn){
if(conn) {
if(conn->file) fclose(conn->file);
if(conn->db) free(conn->db);
free(conn);
}
}
void Database_write(struct Connection *conn){
int i = 0;
rewind(conn->file);
int rc = fwrite(conn->db, sizeof(struct Database), 1, conn->file);
if(rc != 1) die("Failed to write database.");
for (i = 0; i < MAX_ROWS; i++) {
rc = fwrite(&conn->db->rows[i], sizeof(struct Address), 1, conn->file);
if(rc != 1) die("Failed to write database.");
rc = fwrite(&conn->db->rows[i].name, sizeof(MAX_DATA), 1, conn->file);
if(rc != 1) die("Failed to write database.");
rc = fwrite(&conn->db->rows[i].email, sizeof(MAX_DATA), 1, conn->file);
if(rc != 1) die("Failed to write database.");
}
rc = fflush(conn->file);
if(rc == -1) die("Cannot flush database");
}
void Database_create(struct Connection *conn, int MAX_DATA, int MAX_ROWS){
int i = 0;
conn->db->rows = malloc(sizeof(struct Address) * MAX_ROWS);
if (conn->db->rows == NULL) die("No memory for rows");
for(i = 0; i < MAX_ROWS; i++){
// make a prototype to initialize it
struct Address addr = {.id = i, .set = 0};
addr.name = malloc(sizeof(char) * MAX_DATA);
if (addr.name == NULL) die("No memory for name");
addr.email = malloc(sizeof(char) * MAX_DATA);
if (addr.email == NULL) die("No memory for email");
// then just assign it
conn->db->rows[i] = addr;
}
}
void Database_set(struct Connection *conn, int id, const char *name, const char *email){
struct Address *addr = &conn->db->rows[id];
if(addr->set) die("Already set, delete it first");
addr->set = 1;
// warning: intentional bug, no relevant this question
char *res = strncpy(addr->name, name, MAX_DATA);
// demonstrate the strncpy bug
if(!res) die("Name copy failed");
res = strncpy(addr->email, email, MAX_DATA);
if(!res) die("Email copy failed");
}
void Database_get(struct Connection *conn, int id){
struct Address *addr = &conn->db->rows[id];
if(addr->set){
Address_print(addr);
}
else{
die("ID is not set");
}
}
void Database_delete(struct Connection *conn, int id){
struct Address addr = {.id = id, .set = 0};
conn->db->rows[id] = addr;
}
void Database_list(struct Connection *conn){
int i = 0;
struct Database *db = conn->db;
for(i = 0; i < MAX_ROWS; i++){
struct Address *cur = &db->rows[i];
if(cur->set) {
Address_print(cur);
}
}
}
int main(int argc, char *argv[]){
if(argc < 3) die("USAGE: ex17 <dbfile> <action> <MAX_ROWS> <MAX_DATA> [action params]");
char *filename = argv[1];
char action = argv[2][0];
MAX_DATA = atoi(argv[3]);
MAX_ROWS = atoi(argv[4]);
int id = 0;
if(argc > 5) id = atoi(argv[5]);
struct Connection *conn = Database_open(filename, action);
// legacy code, does not apply for create case
// if(argc > 3) id = atoi(argv[3]);
// if(id >= MAX_ROWS) die("There's not that many records.");
switch(action){
case 'c':
if(argc != 5) die("Need MAX_DATA and MAX_ROWS");
Database_create(conn, MAX_DATA, MAX_ROWS);
Database_write(conn);
break;
case 'g':
if(argc != 6) die("Need an id to get");
Database_get(conn, id);
break;
case 's':
if(argc != 8) die("Need id, name, email to set");
Database_set(conn, id, argv[6], argv[7]);
Database_write(conn);
break;
case 'd':
if(argc != 6) die("Need id to delete");
Database_delete(conn, id);
Database_write(conn);
break;
case 'l':
Database_list(conn);
break;
default:
die("Invalid action, only: c=create, g=get, s=set, d=del, l=list");
}
Database_close(conn);
return 0;
}
Here is the printf output after i execute the program
$./ex17_arbitrary db_arbitrary.dat c 512 100
$./ex17_arbitrary db_arbitrary.dat s 512 100 1 zed zed#zedshaw.com
test set name = 0x15ad058
test set email = 0x15ad060
test Database_load loop read rows (nil)
test Database_load loop read rows name 0x8
test Database_load loop read rows email 0x10
One thing I did notice is that these two lines never change across multiple executions with the same commands
test Database_load loop read rows name 0x8
test Database_load loop read rows email 0x10
UPDATE:
I also have some additional design questions. It looks like the design of the current data structure is problematic. I will elaborate on the design requirement here:
I dont need any extra functionality beyond the ones I have created. The size of the database (MAX_DATA and MAX_ROWS have to be variable). Right now I am feeding the MAX_DATA and MAX_ROWS everytime I call the program. Can this be improved? I am thinking may be just give MAX_DATA and MAX_ROWS when I need to use the Database_create method. This program is from an interesting exercise in (c.learncodethehardway.org/book/ex17.html), the original program has a fix size database. And the goal is to make it into variable size.
In Database_load, you are doing:
int rc = fread(conn->db, sizeof(struct Database), 1, conn->file);
But, conn->db is a pointer to the type struct Database and the only element of that is:
struct Address *rows;
In short, you're trying to initialize the pointer rows from an fread of a file. This is not the buffer/array that rows points to but the contents of the rows pointer variable itself (i.e. the address in memory that rows points to)
Since you already initialized rows in Database_open, the fread appears suspect because:
You've already set up rows
Doing (e.g.) void *ptr = ...; read(fd,&ptr,sizeof(ptr)); is almost never correct.
A more normal usage is: void *ptr = ...; read(fd,ptr,some_length);
You're overwriting [trashing] the rows value initialized in Database_open, by doing the equivalent of (2) above. It's just as [bad as] if you had written:
conn->db->rows = NULL;
Or:
conn->db->rows = (void *) 0x1234;
I'm not completely sure, because I can't test the program without data, but you may be able to simply remove the above fread. Or, it has to be replaced with something else if there truly is some sort of header in the database that precedes the actual row data.
But, if you take the fread out, rows remains intact, and what it points to will be populated in the for loop as you have now.
UPDATE:
I see the problem. I think it is more a bad design. Basically, I am storing pointers into the database and try to read it out and access the same pointer address across different program execution.
I mentioned that in my original post, but removed it in my edit because I assumed that you weren't trying to do that and the fread was more of a "typo".
But, it is a bad design to try to store persistent values of pointers within a file and restore them on the next invocation.
Particularly so if the pointers come from malloc. The old pointers could collide with malloc on the second invocation. And, how would you tell malloc that the old pointers are now somehow "reserved"?
For pointers that point to global/static memory, what happens if you rebuild your program and add a new variable [that changes the addresses and offsets of everything]?
In short, you can't do this [and that's the long answer, too :-)].
The solution I can think of is to only store struct Address, and name strings, address strings. Will that be a better design?
Yes, if you mean the following:
struct Address {
int id;
int set;
char name[MAX_DATA];
char email[MAX_DATA];
};
Now, struct Address can be read/written to a file because it has no pointers (i.e. that's the key)
As a further example, consider what happens if struct Address had an embedded linked list pointer:
struct Address {
struct Address *link;
int id;
int set;
char name[MAX_DATA];
char email[MAX_DATA];
};
// simple traversal
for (addr = addrlist; addr != NULL; addr = addr->next) {
// do stuff ...
// broken, because struct Address has a pointer
fwrite(addr,sizeof(struct Address),fout);
}
// fix for above
for (addr = addrlist; addr != NULL; addr = addr->next) {
// do stuff ...
fwrite(addr->id,sizeof(addr->id),fout);
fwrite(addr->set,sizeof(addr->set),fout);
fwrite(addr->name,sizeof(addr->name),fout);
fwrite(addr->email,sizeof(addr->email),fout);
}
Here's a more isolated way to do the list:
struct Address {
int id;
int set;
char name[MAX_DATA];
char email[MAX_DATA];
};
// NOTE: either of these works
#if 1
struct AddrLink {
struct AddrLink *link;
struct Address *data;
};
#else
struct AddrLink {
struct AddrLink *link;
struct Address data;
};
#endif
// indirect list traversal
for (link = addrlist; link != NULL; link = link->next) {
// do stuff ...
// works because struct Address does _not_ have a pointer
fwrite(link->data,sizeof(struct Address),fout);
}
In the general case, what you want to do is similar to serialization. Here's a link: C - serialization techniques Here's another: Serialize Data Structures in C
When I do this, I like to prefix the data with a standard "section" header. A similar technique is used in .mp4, .avi files:
struct section {
int type; // section type
int totlen; // total section length
int size; // sizeof of section element
int count; // number of array elements
};
#define TYPE_ADDRESS 1
#define TYPE_CITY 2
#define TYPE_STATE 3
#define TYPE_COUNTRY 4
That way, if your program doesn't understand a new type because it's an older rev, it can still copy or skip over the data it doesn't understand without harming it. (e.g.) That's required behavior when dealing with .mp4 files.
UPDATE #2:
I have posted the full code. Could you suggest a better way to design this? I dont have a specific constraint for formatting on the database file
Okay, working code below ...
I changed a few things around with the structs [and renamed them]. Notably, the master struct [that you called Connection is now called database_t]. Address is now address_t.
You were pretty close. What you were trying to do with your old struct Database, I replaced with dbheader_t. That is, these were the database header structs I was talking about. Your's just had the pointer in it. Mine records the max rows and max data as the first part of the database file before the row data starts
I moved the allocation code to a new function Database_alloc [because it now has to be called in two different places].
Database_open has to be slightly smarter. For the c action, it fills in the DB header. For all other actions, it has to open the DB file and read the on-disk header.
Also, instead of doing conn->db->rows everywhere, with the new struct organization this is now db->rows.
Overall, you were already quite close.
I also reworked main to be a bit more user friendly (i.e. you only have to enter MAX_DATA/MAX_ROWS on the c command.
Anyway, here it is [please pardon the gratuitous style cleanup]:
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
// database element
typedef struct _address {
int id; // ID/slot number
int set; // 1=active, 0=free/available
char *name; // person's name
char *email; // person's email address
} address_t;
// database on-disk header
typedef struct _header {
int max_rows; // maximum number of rows
int max_data; // maximum size of a field
} dbheader_t;
// NOTE: other stuff can be added (e.g. split max_data into max_name and
// max_email so that each field can have its own maximum length)
// database control
typedef struct _database {
FILE *file; // database I/O stream
dbheader_t header; // copy of on-disk header
address_t *rows; // database data
} database_t;
void
die(const char *message)
{
if (errno) {
// perror(message);
printf("ERROR: %s\n", message);
}
else {
printf("ERROR: %s\n", message);
}
exit(1);
}
void
Address_print(address_t *addr)
{
printf("%d %s %s\n",addr->id, addr->name, addr->email);
}
void
Database_load(database_t *db)
{
int i;
address_t *addr;
int rc;
// NOTE: database header has _already_ been read
#if 0
rc = fread(db->file, sizeof(dbheader_t), 1, db->file);
if (rc != 1)
die("Failed to write database.");
#endif
for (i = 0; i < db->header.max_rows; i++) {
addr = &db->rows[i];
rc = fread(&addr->id, sizeof(addr->id), 1, db->file);
if (rc != 1)
die("Failed to write database.");
rc = fread(&addr->set, sizeof(addr->set), 1, db->file);
if (rc != 1)
die("Failed to write database.");
rc = fread(addr->name, db->header.max_data, 1, db->file);
if (rc != 1)
die("Failed to write database.");
rc = fread(addr->email, db->header.max_data, 1, db->file);
if (rc != 1)
die("Failed to write database.");
}
}
void
Database_alloc(database_t *db)
{
address_t *addr;
db->rows = malloc(sizeof(address_t) * db->header.max_rows);
if (db->rows == NULL)
die("No memory for rows");
for (int i = 0; i < db->header.max_rows; i++) {
addr = &db->rows[i];
// NOTE: no need to do it this way
// make a prototype to initialize it
// struct Address addr = {.id = i, .set = 0};
addr->id = i;
addr->set = 0;
addr->name = calloc(db->header.max_data,sizeof(char));
if (addr->name == NULL)
die("No memory for name");
addr->email = calloc(db->header.max_data,sizeof(char));
if (addr->email == NULL)
die("No memory for email");
}
}
database_t *
Database_open(const char *filename, char mode, int max_rows, int max_data)
{
int rc;
database_t *db = calloc(1,sizeof(database_t));
if (!db)
die("Memory error no db pointer");
switch (mode) {
case 'c':
db->file = fopen(filename, "w");
if (!db->file)
die("Failed to open the file");
// set up a header [to write out]
db->header.max_rows = max_rows;
db->header.max_data = max_data;
Database_alloc(db);
break;
default:
db->file = fopen(filename, "r+"); // r+?
if (!db->file)
die("Failed to open the file");
// read in header so we know the number of rows and the max data size
rc = fread(&db->header,sizeof(dbheader_t),1,db->file);
if (rc != 1)
die("Failed to read header.");
Database_alloc(db);
Database_load(db);
}
return db;
}
void
Database_close(database_t *db)
{
address_t *addr;
if (db) {
if (db->file)
fclose(db->file);
db->file = NULL;
if (db->rows) {
for (int rowidx = 0; rowidx < db->header.max_rows; ++rowidx) {
addr = &db->rows[rowidx];
free(addr->name);
free(addr->email);
}
free(db->rows);
db->rows = NULL;
}
free(db);
}
}
void
Database_write(database_t *db)
{
int i;
int rc;
address_t *addr;
rewind(db->file);
// write out the DB header
rc = fwrite(&db->header, sizeof(dbheader_t), 1, db->file);
if (rc != 1)
die("Failed to write database.");
for (i = 0; i < db->header.max_rows; i++) {
addr = &db->rows[i];
rc = fwrite(&addr->id, sizeof(addr->id), 1, db->file);
if (rc != 1)
die("Failed to write database.");
rc = fwrite(&addr->set, sizeof(addr->set), 1, db->file);
if (rc != 1)
die("Failed to write database.");
rc = fwrite(addr->name, db->header.max_data, 1, db->file);
if (rc != 1)
die("Failed to write database.");
rc = fwrite(addr->email, db->header.max_data, 1, db->file);
if (rc != 1)
die("Failed to write database.");
}
rc = fflush(db->file);
if (rc == -1)
die("Cannot flush database");
}
void
Database_set(database_t *db, int id, const char *name, const char *email)
{
address_t *addr = &db->rows[id];
if (addr->set)
die("Already set, delete it first");
addr->set = 1;
// warning: intentional bug, no relevant this question
// demonstrate the strncpy bug
char *res = strncpy(addr->name, name, db->header.max_data);
if (!res)
die("Name copy failed");
addr->name[db->header.max_data - 1] = 0;
res = strncpy(addr->email, email, db->header.max_data);
if (!res)
die("Email copy failed");
addr->email[db->header.max_data - 1] = 0;
}
void
Database_get(database_t *db, int id)
{
address_t *addr = &db->rows[id];
if (addr->set) {
Address_print(addr);
}
else {
die("ID is not set");
}
}
void
Database_delete(database_t *db, int id)
{
// NOTE/BUG: this causes a memory leak because it overwrites the name and
// email fields without freeing them first
#if 0
struct Address addr = {.id = id,.set = 0 };
db->rows[id] = addr;
#else
address_t *addr = &db->rows[id];
addr->id = 0;
addr->set = 0;
memset(addr->name,0,db->header.max_data);
memset(addr->email,0,db->header.max_data);
#endif
}
void
Database_list(database_t *db)
{
int i;
for (i = 0; i < db->header.max_rows; i++) {
address_t *cur = &db->rows[i];
if (cur->set) {
Address_print(cur);
}
}
}
int
main(int argc, char *argv[])
{
int max_data = 0;
int max_rows = 0;
int id = -1;
if (argc < 3) {
printf("USAGE: ex17 <dbfile> <action> [action params]");
printf(" actions:\n");
printf(" c <MAX_DATA> <MAX_ROWS> -- create database\n");
printf(" g <id> -- get id and print\n");
printf(" s <id> <name> <email> -- set id\n");
printf(" d <id> -- delete id\n");
printf(" l -- list database\n");
die("aborting");
}
// skip over program name
--argc;
++argv;
--argc;
char *filename = *argv++;
--argc;
char action = argv[0][0];
++argv;
switch (action) {
case 'c':
if (argc != 2)
die("Need MAX_DATA and MAX_ROWS");
max_data = atoi(argv[0]);
max_rows = atoi(argv[1]);
break;
}
database_t *db = Database_open(filename, action, max_rows, max_data);
// legacy code, does not apply for create case
// if(argc > 3) id = atoi(argv[3]);
// if(id >= db->header.max_rows) die("There's not that many records.");
switch (action) {
case 'c':
Database_write(db);
break;
case 'g':
if (argc != 1)
die("Need an id to get");
id = atoi(argv[0]);
Database_get(db, id);
break;
case 's':
if (argc != 3)
die("Need id, name, email to set");
id = atoi(argv[0]);
Database_set(db, id, argv[1], argv[2]);
Database_write(db);
break;
case 'd':
if (argc != 1)
die("Need id to delete");
id = atoi(argv[0]);
Database_delete(db, id);
Database_write(db);
break;
case 'l':
Database_list(db);
break;
default:
die("Invalid action, only: c=create, g=get, s=set, d=del, l=list");
break;
}
Database_close(db);
return 0;
}
After searching through many threads on similar issues, I've been unable to determine why I've been getting a seg fault with my program. I have two files: buffer.c where I create a circular buffer and deposit/remove values from it and a main file where several threads call the operations on the circular buffer with user input. Semaphores are used to prevent concurrent access.
Here are the relevant parts of my main program:
int main (int argc, char const *argv[]) {
st_init();
Buffer *bufferA,*bufferB,*bufferC;
createBuffer(bufferA,128);
createBuffer(bufferB,128);
createBuffer(bufferC,128);
// Create the struct used to initialize threads.
ThreadInit initA = {
bufferA,
bufferA
};
ThreadInit initB = {
bufferA,
bufferB
};
ThreadInit initC = {
bufferB,
bufferC
};
ThreadInit initD = {
bufferC,
bufferC
};
// Create threads
if (st_thread_create(getInputStream, &initA, 0, 0) == NULL) {
perror("Thread a creation failure.");
exit(EXIT_FAILURE);
}
if (st_thread_create(convertCR, &initB, 0, 0) == NULL) {
perror("Thread b creation failure.");
exit(EXIT_FAILURE);
}
if (st_thread_create(squashChar, &initC, 0, 0) == NULL) {
perror("Thread c creation failure.");
exit(EXIT_FAILURE);
}
if (st_thread_create(printOutput, &initD, 0, 0) == NULL) {
perror("Thread d creation failure.");
exit(EXIT_FAILURE);
}
// Exit from main via ST.
st_thread_exit(NULL);
return 0;
}
void *getInputStream(void *state) {
ThreadInit *threadInit = state;
char inputChar = getchar();
while (inputChar != EOF) {
deposit(inputChar, threadInit->produceBuff); //where segfault occurs
inputChar = getchar();
st_usleep(SLEEP_TIME);
}
st_thread_exit(NULL);
}
and buffer.c
void createBuffer(Buffer *buff, int buffSize){
buff = (Buffer*) calloc(1, sizeof(Buffer));
semaphore mutex,emptyBuffers,fullBuffers;
buff->mutex = calloc(1,sizeof(semaphore));
buff->emptyBuffers = calloc(1,sizeof(semaphore));
buff->fullBuffers = calloc(1,sizeof(semaphore));
createSem(buff->mutex,1);
createSem(buff->emptyBuffers,buffSize);
createSem(buff->fullBuffers,0);
buff->charBuff = malloc(sizeof(char) * buffSize);
buff->nextIn = 0;
buff->nextOut = 0;
buff->buffSize = buffSize;
}
The seg fault occurs the first time an operation is done on the semaphores in my buffers, which leads me to believe their memory is improperly allocated, though I included the code from my main in case I'm wrong in that assumption. Also, in case it's not clear from my code, I'm quite new to C, so I'd appreciate any guidance. Thanks!
Here is the error
void createBuffer(Buffer *buff, int buffSize){
buff = (Buffer*) calloc(1, sizeof(Buffer));
you need to return the pointer of the buffer otherwise you are not returning the changed pointer to the caller
void createBuffer(Buffer **buff, int buffSize){
*buff = calloc(1, sizeof(Buffer));
a bit simplified : it is similar to
int foo(int a)
{
a = 1; // 1 not visible outside foo
}
and
int foo(int *a)
{
*a = 1; // 1 is visible outside foo
}
also in C you don't cast what is returned from calloc/malloc only if you are compiling with a C++ compiler but then you should use new instead
In c, function parameter is passed by value so your createBuffer() function didn't really create anything; it just leaked memory instead.
One easy fix is to allocate the memory in main():
bufferA = (Buffer*) calloc(1, sizeof(Buffer));
and remove this line:
buff = (Buffer*) calloc(1, sizeof(Buffer));
I don't see how your createSem() is implemented by you may want to check it too.
I've got two different arrays that I'm using. With one, I'm getting the exact results that I want, the other, not so much. I'm filing the arrays with by reading from a text file similar to this:
2597
283
4
723
21
82
426
The first five lines would be the customer IDs. There is always 5 lines but they don't always have a value. The next line is the number of vendors, then followed by the vendor ids.
void use_arrays()
{
int
i,
customer_count,
*customer_ids,
vendor_count,
*vendor_ids;
customer_ids = malloc(sizeof(int));
vendor_ids = malloc(sizeof(int));
fill_arrays(&customer_count, customer_ids, &vendor_count, vendor_ids);
for (i = 0; i < customer_count; i++)
{
printf("Customer[%d]: %d\n", i, customer_ids[i]);
}
for (i = 0; i < vendor_count; i++)
{
printf("Vendor[%d]: %d\n", i, vendor_ids[i]);
}
free(customer_ids);
free(vendor_ids);
}
void fill_arrays(int *customer_count, int *customer_ids, int *vendor_count, int *vendor_ids)
{
int
i,
*temp,
customer_id,
vendor_id,
num_cust = 0;
FILE
*inp_file;
char
*endptr = NULL,
buffer[500];
inp_file = fopen(g_filename, "r");
for (i = 0; i < 5; i++) /* Can't be more than 5 customers */
{
fgets(buffer, sizeof(buffer), inp_file);
customer_id = strtol(buffer, &endptr, 0);
if (customer_id != 0)
{
customer_ids[i] = customer_id;
temp = realloc(customer_ids, (i+2)*sizeof(int));
if (temp != NULL)
{
customer_ids = temp;
}
else
{
printf("Couldn't allocate memory\n");
}
num_cust++;
}
}
*customer_count = num_cust;
/* Next is number of vendor ids*/
fgets(buffer, sizeof(buffer), inp_file);
*vendor_count = strtol(buffer, &endptr, 0);
temp = realloc(vendor_ids, *vendor_count*sizeof(int));
if (temp != NULL)
{
vendor_ids = temp;
}
else
{
printf("Couldn't allocate memory\n");
}
for (i = 0; i < *vendor_count; i++)
{
fgets(buffer, sizeof(buffer), inp_file);
vendor_id = strtol(buffer, &endptr, 0);
if (vendor_id != 0)
{
vendor_ids[i] = vendor_id;
}
}
fclose(inp_file);
}
Once the arrays print out, customer_ids is showing the correct numbers but vendor_ids is printing out random numbers from memory. To be more frustrating, it prints the vendors correctly from inside fill_arrays.
If you want to modify vendor_ids the way you do in fill_arrays, then you have to pass it in as a pointer to a pointer:
fill_arrays(int *customer_count, int *customer_ids, int *vendor_count, int **vendor_ids)
Call it like this:
fill_arrays(&customer_count, customer_ids, &vendor_count, &vendor_ids);
Then you can realloc like so:
temp = realloc(*vendor_ids, *vendor_count*sizeof(int));
if (temp != NULL)
{
*vendor_ids = temp;
}
Also, at the end of your function:
vendor_ids[i] = vendor_id;
will have to change to
(*vendor_ids)[i] = vendor_id;
You will also have to make the same changes to customer_ids. The fact that customer_ids was working while vendor_ids wasn't was probably due to your use of realloc. If realloc decides that the memory block has to be reallocated in a new location, you'll run into these problems but if reallocates the memory in the same location, your pointer that you passed in is still pointing there. Since you never know if realloc is going to make that descision or not, both customer_ids and vendor_ids should be passed in as pointers to pointers.
You seem a bit confused how to return memory from a function.
if you have a function that looks like this
void foo(int a);
you can not change a inside of foo, foo only gets a copy of a.
void foo(int *a);
otoh gives foo the address of a, i.e. "i know where you live" it lets you change what a points to. if a points to an array then you can change the contents of that array, if a points to a single integer you can change that.
void foo(int **a);
lets you change not only what a points to, but also where it points. so if you want a to point to somewhere else you can. this is what you need in your function, if you do a malloc/calloc/realloc in your function to return the result you need this
I'm trying to write a program in c that uses threads to print out the contents of the current working directory. Currently the right number of files are printed, however, some files repeat multiple times. Which files repeat and how many times seems random every time I run it. I tried using a mutex lock on what I thought was the critical section, but to no success.
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
struct arg_struct
{
struct dirent *current;
struct stat buf;
};
void * mybackup (void *arguments)
{
pthread_mutex_lock( &mutex );
struct arg_struct *args = arguments;
printf( "[THREAD %u] ", (unsigned int)pthread_self());
printf( "%s ", args->current->d_name );
printf( "(%lld bytes)\n", (long long) args->buf.st_size );
total_bytes += args->buf.st_size;
pthread_mutex_unlock( &mutex );
return NULL;
}
int main (int argc, char **argv)
{
if (argc == 1) //For part 1, when no cmd line argument is given
{
int children = 0;
int thread, i;
pthread_t tid[100];
char * current_path = ".";
DIR * dir = opendir((char*)current_path); //Opens the current directory
if (dir == NULL) //Detects failure to open directory
{
perror("opendir() Failed");
return EXIT_FAILURE;
}
struct dirent *current;
int rc = 0;
while ((current = readdir(dir)) != NULL)
{
struct stat buf;
char new[10000]; //Used to create the complete path name
//Next few lines create the complete path name required for Stat()
strcpy(new, current_path);
strcat(new, "/");
strcat(new, (char*)current->d_name);
rc = stat(new, &buf);
//rc = stat(current -> d_name, &buf);
if (rc < 0) //Detects stat() failure
{
perror("stat() Failed");
return 1;
}
else
{
if (S_ISREG(buf.st_mode)) //If directory entry is a regular file
{
struct arg_struct args;
args.current = current;
args.buf = buf;
thread = pthread_create(&tid[children], NULL, mybackup, (void *)&args);
if ( thread != 0 )
{
perror( "MAIN: Could not create child thread" );
}
children++;
}
}
}
for (i = 0; i < children; i++)
{
pthread_join(tid[i], NULL);
}
printf("Total bytes: %lld\n", total_bytes);
}
return 0;
}
{
struct arg_struct args;
...
thread = pthread_create(&tid[children], NULL, mybackup, (void *)&args);
This can't be right. The (stack-based) object you pass to the to the thread function it is very likely to be overwritten before the created thread has had a chance to do anything with it.
You'll need to malloc that structure (and deal with freeing), or use an array (on stack or static) of such structs that is large enough so that you can assign one to each of your threads.
In fact, just allocating that structure's not enough, you can't pass the dirent* current as a pointer - you need to copy the data you need from it to your per-thread structure, or possibly use readdir_r instead (read the example in the man page carefully).