Management of Unix Shared Memory - unix

We know that, shared memory in Windows is implemented via memory mapped files backed by system pagefile, and it is always managed in a reference counted way (http://msdn.microsoft.com/en-us/library/windows/desktop/aa366537(v=vs.85).aspx).
I wonder whether Unix does it in a similar way. Someone (http://stackoverflow.com/q/4278391/939142) says that Unix also manages shm in a reference counted way. I tried the an experiment on Mac OS X, and found that it was not the case.
Using the code supplied in the end, compile two programs: host and client. Type
./host
which creates a shm, writes (int) 10 at its first address, and quits. Then type
./client <shmid>
to examine the shm segment, which attaches to the shm created, prints the first int, and quits.
Note that we used shmat and shmdt to connect to / disconnect from the shm, and shmget to create the shm.
To destroy the shm, we have to do it manually using shmctl.
Conclusion:
shm is not managed as reference counting in Unix, at least for Mac OS X
/************************** host.c ****************************/
#include <stdio.h>
#include <sys/shm.h>
#include <mach/vm_param.h>
#include <assert.h>
int
main(int argc, char** argv)
{
int value = 10;
int id;
void* ptr;
/* create a new shm */
id = shmget(IPC_PRIVATE, PAGE_SIZE, IPC_CREAT | 0666);
assert(id != -1);
/* attach to the new shm */
ptr = shmat(id, (const void*) NULL, 0);
assert((int*) ptr != -1);
/* print the id so that client can use */
printf("shm id = %ld\n", id);
printf("address of id = %ld, length = %ld\n", ptr, PAGE_SIZE);
((int*) ptr)[0] = value;
printf("value at address %ld = %ld\n", ptr, *(int*) ptr);
/* detach from the shm and exit */
shmdt((const void*) ptr);
return 0;
}
/************************** host.c ****************************/
#include <stdio.h>
#include <stdlib.h>
#include <sys/shm.h>
#include "assert.h"
#include <mach/vm_param.h>
int
main(int argc, char** argv)
{
void* ptr;
int id = atoi(argv[1]);
assert(id != -1);
/* attach to the shm */
ptr = shmat(id, NULL, 0);
assert(ptr != -1);
printf("value at ptr = %ld = %ld\n", ptr, *(int*) ptr);
shmdt((const void*) ptr);
return 0;
}

It not pure reference counting. According to shmctl(2):
IPC_RMID: Mark the segment to be destroyed. The segment will only
actually be destroyed after the last process detaches it
(i.e., when the shm_nattch member of the associated structure
shmid_ds is zero). The caller must be the owner or creator,
This means: IPC_RMID will not delete immediately but only after the reference count drops to zero the next time.
This allows you to achieve several goals with the same tool:
Either a server/client approach where the server creates, attaches and immediately sets RMID. Then clients can connect as long as the server is here. If the server goes down, the clients should disconect and the resource is cleaned up by the OS.
Or the "message" approach: Someone writes a message, pins it to a known location. After that someone else can come, look for a message and act accordingly. This is what you have done. This approach is more open to resource garbage of course. But there are usecases for this.

Related

How to use read() to get the adress of a pointer?

I have 2 programs communicating with each other through a fifo, one's the writer the other's the reader.
The writer sends a pointer to a struct containing information.
The reader should receive the pointer and be able to see the information inside the struct.
Header file:
typedef struct req{
int _code;
char _client_pipe[PIPENAME];
char _box_name[BOXNAME];
} request;
/*writes to pipe tx a pointer with information*/
void send_request(int tx, request *r1) {
ssize_t ret = write(tx, &r1, sizeof(r1));
if (ret < 0) {
fprintf(stdout, "ERROR: %s\n", ERROR_WRITING_PIPE);
exit(EXIT_FAILURE);
}
}
/*Returns a pointer to a struct containing the request*/
request *serialize(int code, char* client_pipe, char* box_name){
request *r1 = (request*) malloc(sizeof(request));
r1->_code = code;
strcpy(r1->_client_pipe, client_pipe);
strcpy(r1->_box_name, box_name);
return r1;
}
Program writer:
int main(int argc, char **argv){
(void *) argc; // in my program i used argc, but for this problem it's not important hence why the //typecast to void
char register_pipe[PIPENAME];
char personal_pipe[PIPENAME];
char box_name[BOXNAME];
strcpy(register_pipe, argv[1]);
strcpy(personal_pipe, argv[2]);
strcpy(box_name, argv[3]);
int reg_pipe = open(register_pipe, O_WRONLY);
if (reg_pipe == -1) {
fprintf(stdout, "ERROR: %s\n", UNEXISTENT_PIPE);
return -1;
}
send_request(reg_pipe, serialize(1, personal_pipe, box_name));
}
Program reader:
char register_pipe[PIPENAME];
strcpy(register_pipe, argv[1]);
if(mkfifo(register_pipe, 0644) < 0)
exit(EXIT_FAILURE);
if ((reg_pipe = open(register_pipe, O_RDONLY)) < 0){
exit(EXIT_FAILURE);
}
if ((reg_pipe = open(register_pipe, O_RDONLY)) < 0){
exit(EXIT_FAILURE);
}
request* buffer = (request*) malloc(sizeof(request)); //this might be the issue but not sure
ssize_t broker_read= read(reg_pipe, buffer, 256); //is not reading correctly
printf("%d, %s, %s\n", buffer->_code, buffer->_client_pipe, buffer->_box_name);
So if i start program reader and set register pipe as "reg", this will create the register pipe and wait for someone to join it.
Then if i start the program writer like ./writer reg personal box
this will open the reg pipe correctly, create a struct of type request and then sent it to the reader.
The reader should receive a pointer to a struct req set like:
_code = 1;
_client_pipe[PIPENAME] = "personal";
_box_name[BOXNAME] = "box";
The reader is in fact receiving but for some reason it's not receiving correctly.
If i try to print like in the last line, it will output some random numbers and letters.
How can i fix this?
You would need to have that structure exist inside a shared memory region that you have arranged to be mapped into both processes at the same address.
Without some such arrangement, each process has a private address space, so an address known to process A is meaningless to process B.
How to make such an arrangement is very much dependent upon you operating system, and perhaps even variant of said operating system.
You will likely find it easier to just copy the structure, as opposed to its address, via the fifo.

basic pointer use: segmentation fault, core dumped [duplicate]

This question already has answers here:
Crash or "segmentation fault" when data is copied/scanned/read to an uninitialized pointer
(5 answers)
Closed 5 years ago.
I've spent hours scouring the internet for help. I'm very much a beginner at pointer use, and I've already come up against a wall: I keep getting the error Segmentation fault (core dumped). I'm trying to make a simple version of strncpy() using pointers:
int main(int argc, char *argv[]) {
char *x = "hello"; /* string 1 */
char *y = "world"; /* string 2 */
int n = 3; /* number of characters to copy */
for (int i=0; i<=n; i++) {
if(i<n) {
*x++ = *y++; /* equivalent of x[i] = y[i] ? */
printf("%s\n", x); /* just so I can see if something goes wrong */
} else {
*x++ = '\0'; /* to mark the end of the string */
}
}
}
(Edit: I initialized x and y, but still got the same fault.)
While on the quest to figure out what part of this was wrong, I tried another simple pointer thing:
int main(int argc, char *argv[]) {
char *s;
char *t;
int n; /* just initilaizing everything I need */
printf("Enter the string: ");
scanf("%s", s); /* to scan in some phrase */
printf("%s", s); /* to echo it back to me */
}
And lo and behold, I got another Segmentation fault (core dumped)! It let me scan in "hello", but replied with the fault. This code is so simple. What's wrong with my pointer use here?
In your second example, you don't actually allocate any memory. char *s only allocates a pointer to a char. You need to allocate memory somehow:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
char s[100];
printf("Enter the string: ");
scanf("%s", s); /* to scan in some phrase */
printf("%s", s); /* to echo it back to me */
}
char s[100] declares memory on the stack, which will be deallocated automatically. If you'd like to allocate on the heap, use malloc / free:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
char *s = malloc(100 * sizeof(char));
printf("Enter the string: ");
scanf("%s", s); /* to scan in some phrase */
printf("%s", s); /* to echo it back to me */
free(s);
}
Of course these simple examples assume your string will never be longer than 100 characters.
Your first example fails too, for a different reason.
char *x = "hello";
char *y = "world";
Those statements allocate strings in read-only memory, and thus you cannot modify it.
When you are using pointer to string, always rember that you cant modify it. It means you cant change the string characters. In Pointer to string, string always goes to read only memory.It mean memory can only be read not to modify.
This statement is causing segment fault;-
*x++ = *y++;
you cant do this also;-
int *p="cool";
*p="a"; //dereferencing
printf("%s",p); //segment fault

Trouble reading/writing internal EEPROM PIC24F16KA101

I am trying to get interact with the internal memory of the PIC24F16KA101 MCU. After reading the data-sheet and the discussion on this site (which offer a pretty helpful sample code)used in the project
Now if I put the code below the program work just fine, since I am able to read successfully the same value that I wrote previously. However if after writing I unplug the MCU and perform only a read of the EEPROOM it is not going to return the value written. What could be the problem here?. Why can I write and then read successfully but can not read after a power off?.
Thanks in advance to all for the help
Damian
int __attribute__ ((space(eedata))) ee_addr;
void EepSetup();
void EepErase(void);
int EepWrite(int index, int data);
int EepRead(int index);
int main(int argc, char** argv)
{
unsigned int data = 123;
unsigned int data_read = 0;
Init_UART1();
UART1WriteString("START EEPROM PROGRAM \n");
EepSetup();
UART1WriteString("WRITING DATA TO MEMORY \n");
EepWrite(1,data);
//if the code works, just comment the upper section and read eeprom after
//disconecting the power source
UART1WriteString("READING DATA FROM MEMORY \n");
data_read = EepRead(1);
UART1WriteString("Value Read: ");
UART1WriteInt(data_read,16);
UART1WriteString("\n");
__delay_ms(1000);
return (EXIT_SUCCESS);
}
void EepSetup(){
//Disable Interrupts For 5 instructions
asm volatile("disi #5");
//Issue Unlock Sequence
asm volatile("mov #0x55, W0 \n"
"mov W0, NVMKEY \n"
"mov #0xAA, W1 \n"
"mov W1, NVMKEY \n");
}
void EepErase(void) {
NVMCON = 0x4050; // Set up NVMCON to bulk erase the data EEPROM
asm volatile ("disi #5"); // Disable Interrupts For 5 Instructions
__builtin_write_NVM(); // Issue Unlock Sequence and Start Erase Cycle
while(_WR)
;
}
int EepRead(int index){
unsigned int offset;
TBLPAG = __builtin_tblpage(&ee_addr); // Initialize EE Data page pointer
offset = __builtin_tbloffset(&ee_addr); // Initizlize lower word of address
offset += index * sizeof(int);
return __builtin_tblrdl(offset); // read EEPROM data
}
int EepWrite(int index, int data){
unsigned int offset;
NVMCON = 0x4004; // Set up NVMCON to erase one word of data EEPROM
TBLPAG = __builtin_tblpage(&ee_addr); // Initialize EE Data page pointer
offset = __builtin_tbloffset(&ee_addr); // Initizlize lower word of address
offset += index * sizeof(int);
__builtin_tblwtl(offset, data);
asm volatile ("disi #5"); // Disable Interrupts For 5 Instructions
__builtin_write_NVM(); // Issue Unlock Sequence and Start Erase Cycle
while(_WR);
return (EXIT_SUCCESS);
}
I just figured out what the problem was, it happens that if you use the PICkit 3 with MPLABX you have to check an option in the programmer to preserve the EEPROM memory,so the code was functional, you just need to check the option of "Preserve EEPROM Memory" in the programmer settings. I hope this help others.
Cheers, Damian

How can I find who or which process sent signals to my process on Solaris

I have JBoss running on Sun Java 1.5.
From time to time, it unexpectedly shuts down (in orderly fashion).
I suspect some other process is sending it kill or CTRL+C signals.
Is there a way on Unix/Solaris to trace who sent the signal?
On Solaris, you can use a simple dtrace script to find who is killing your process (assuming its name is java, adjust it otherwise):
dtrace -qn '
proc:::signal-send
/ args[1]->pr_fname == "java" /
{
printf("Process %d (%s by UID %d) sending signal %d to java (pid=%d)\n",
pid,execname,uid,arg1,args[1]->pr_pid);
}'
You can use sigaction to determine the source of the signal. pid may be zero as the signal was send from the kernel or via some user interaction (pressing ctrl+c)
#include <signal.h>
#include <string.h>
#include <stdio.h>
static void signal_handler(int sig, siginfo_t *info, void *data) {
printf ("signal: [%d], pid: [%d], uid: [%d]\n", sig,
info->si_pid,
info->si_uid );
}
int main(int argc, char *argv[]) {
struct sigaction sa;
memset ( &sa, '\0', sizeof ( struct sigaction ) );
sa.sa_sigaction = &signal_handler;
sa.sa_flags |= SA_SIGINFO;
sigemptyset ( &sa.sa_mask );
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGQUIT, &sa, NULL);
while ( 1 ) {
sleep (1);
}
return 0;
}

UNIX FIFO: How to allow only one writer/reader pair to use a FIFO?

I've written two programs: the first, the "writer", creates a FIFO and writes data into it. The second one, the "reader" runs in background and looks for data in the FIFO. Once data is there, the reader reads it out.
If I start e.g. two writers and two readers, they all can write/read into/from the same FIFO. How can I restrict it for 3rd and 4th readers/writers to use the FIFO and allow only one writer and one reader to use the FIFO?
My code:
FIFO Writer:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#define BUFFERSIZE 50
#define CHMOD 0777
int main(int argc, char **argv)
{
char outbuf[BUFFERSIZE]; // outbuffer
int fifo, j, anzahl;
// fifo - pipe file deskriptor, j - counter, anzahl - Parameter.
if(argc!=2) // Check if parameter is ok
{
printf("Ungültiger Parameter! Bsp.: ./fifow 10\n");
return 1;
}
anzahl=atoi(argv[1]); // convert paramter to integer
mkfifo("namedpipe4", CHMOD); // make FIFO "namedpipe4"
fifo = open("namedpipe4",O_WRONLY); // open FIFO
//
for(j=0;j<anzahl;j++)
{
printf("Writer PID: %d writes record nr. %6d\n", getpid(), j+1);
sprintf(outbuf, "Writer PID: %d writes record nr. %6d\n", getpid(), j+1);
write(fifo, outbuf, BUFFERSIZE);
remove("namedpipe4"); // removing the fifo
sleep(1); // Wait 1 sec
}
close(fifo); //
exit(0);
}
FIFO Reader:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#define BUFFERSIZE 50
int main(void)
{
char inbuf[BUFFERSIZE]; // inbuffer
int fifo, var;
printf("\n Waiting for a Pipe....\n");
while((fifo = open("namedpipe4",O_RDONLY)) == -1) // while "there is no such pipe"
{
remove("namedpipe4");
sleep(1);
}
while((var = read(fifo, inbuf, BUFFERSIZE)) > 0) // while "i can read"
{
printf("Reader PID: %d reads record: %s\n", getpid(), inbuf);
sleep(1);
}
close(fifo); //
printf("\n EOF..\n");
exit(0);
}
Given the code you posted in a separate answer, here is a modified version that fixes the problems you were having. See the comments for details, but in a nutshell:
The writer checks the return value of mkfifo is checked to see if another writer already created the pipe.
The reader gets an exclusive advisory lock on the pipe (via flock) after opening it, to avoid the race condition where a second reader could have opened the pipe before the first reader deleted it.
Writer:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h> /* needed for mkfifo */
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#define BUFFERSIZE 50
#define CHMOD 0777
int
main (int argc, char **argv)
{
char outbuf[BUFFERSIZE];
int fifo, j, anzahl;
if (argc != 2)
{
printf("Ungültiger Parameter! Bsp.: ./fifow 10\n");
return 1;
}
anzahl=atoi(argv[1]);
/* mkfifo fails if the file already exists, which means there's a
* writer waiting for a reader. This assures that only one writer
* will write to the pipe, since it only opens the pipe if it was
* the one who created it.
*/
if (mkfifo("namedpipe4", CHMOD) == -1)
{
printf("namedpipe4 already exists\n");
return 1;
}
fifo = open("namedpipe4", O_WRONLY);
for (j = 0; j < anzahl; j++)
{
printf("Writer PID: %d writes record nr. %6d\n", getpid(), j + 1);
sprintf(outbuf, "Writer PID: %d writes record nr. %6d\n", getpid(), j + 1);
write(fifo, outbuf, BUFFERSIZE);
remove("namedpipe4");
sleep(1);
}
close(fifo);
exit(0);
}
Reader:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/file.h> /* for flock */
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#define BUFFERSIZE 50
int
main (int argc, char **argv)
{
char inbuf[BUFFERSIZE];
int fifo, var;
printf("\n Waiting for a Pipe....\n");
/* There are *two* ways the open can fail: the pipe doesn't exist
* yet, *or* it succeeded, but a different writer already opened
* it but didn't yet remove it.
*/
while (1)
{
while ((fifo = open("namedpipe4", O_RDONLY)) == -1)
{
/* Since you didn't specify O_CREAT in the call to open, there
* is no way that namedpipe4 would have been created by the
* reader. If there *is* now a namedpipe4, a remove here
* would delete the one the writer created!
*/
sleep(1);
}
/* Get an exclusive lock on the file, failing if we can't get
* it immediately. Only one reader will succeed.
*/
if (flock (fifo, LOCK_EX | LOCK_NB) == 0)
break;
/* We lost the race to another reader. Give up and wait for
* the next writer.
*/
close (fifo);
}
/* We are definitely the only reader.
*/
/* *Here* we delete the pipe, now that we've locked it and thus
* know that we "own" the pipe. If we delete before locking,
* there's a race where after we opened the pipe, a different
* reader also opened, deleted, and locked the file, and a new
* writer created a new pipe; in that case, we'd be deleting the
* wrong pipe.
*/
remove("namedpipe4");
while ((var = read(fifo, inbuf, BUFFERSIZE)) > 0)
{
printf("Reader PID: %d reads record: %s\n", getpid(), inbuf);
/* No need to sleep; we'll consume input as it becomes
* available.
*/
}
close(fifo);
printf("\n EOF..\n");
exit(0);
}
Create the FIFO using pipe(2), and only give the file descriptors for each end of the FIFO to the appropriate process when they get forked from the parent process. (Alternatively, have the reader call pipe(2) and fork the writer, or vice versa.) Since the FIFO never lives on the filesystem, it's impossible for any other process to access it.
If you must use a named FIFO, delete the FIFO after the reader and writer have opened it. The underlying FIFO will still exist as long as the reader and writer have it open, but no new processes will be able to open it. However, there will be a race condition where a second reader or writer could open the FIFO before you've deleted it.

Resources