How to mmap character device file? - c

How can I use mmap to map a character device file to my application? I've tried the following code which fails because the size of the file is zero (sb.st_size == 0). Is there another way or is this impossible all together? Thanks.
#include <fcntl.h>
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int init_module(void *module_image, unsigned long len,
const char *param_values);
int main() {
int res = 0;
void *buf = 0;
struct stat sb;
int rc = 0;
int fd = open("/dev/nvidia0", O_RDWR);
if (fd < 0) {
perror("open");
exit(EXIT_FAILURE);
}
res = fstat(fd, &sb);
if (res == -1) {
perror("fstat");
exit(EXIT_FAILURE);
}
buf = mmap(0, sb.st_size, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_SHARED, fd, 0);
if (buf == MAP_FAILED) {
perror("mmap");
exit(EXIT_FAILURE);
}
munmap(buf, sb.st_size);
close(fd);
return EXIT_SUCCESS;
}

Related

named mmap without Disk IO

I wanted to use the POSIX standard mmap as if shmat had an id, they share the same shared memory.
mmap seems to normally use fd=-1 when using MAP_ANON, and in this case it is said to be valid when inheriting from a child process.
I wanted to ensure the same behavior in the spawn method. Then I can use named shared memory, but I wish it didn't do DISK IO at all.
I think MAP_ANON is essential because I want to always guarantee the speed of RAM without leaving any files on the DISK in case of an unexpected shutdown.
So is it reasonable to provide fd!=-1 while being MAP_ANON|MAP_SHARED?
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
int main (int argc, char *argv[])
{
struct stat sb;
char *p;
int fd;
fd = shm_open("test", O_RDWR | O_CREAT, 0600);
if (fd == -1) {
perror("open");
return 1;
}
size_t len = 128;
if (ftruncate(fd, len) == -1) {
perror("ftruncate");
return 1;
}
p = (char*)mmap(0, len, PROT_WRITE|PROT_READ, MAP_ANON|MAP_SHARED, fd, 0);
if (p == MAP_FAILED){
perror("mmap");
return 1;
}
if (close(fd)==-1) {
perror("close");
return 1;
}
for (int i = 0; i < len; i++) {
putchar(p[i]);
}
if (munmap(p, len) == -1) {
perror("munmap");
return 1;
}
if(shm_unlink("test")) {
perror("unlink");
return 1;
}
fprintf(stderr,"\n");
return 0;
}

sending array of integers via shared memory

I've written a producer and consumer code wherein a character sequence written to shared memory from producer.c is read from consumer.c
But the problem occured when I tried to send integer array from producer.c via shared memory and read from consumer.c
producer.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/mman.h>
int main(){
const int SIZE = 4096;
const char *Obj = "Shm";
int shm_fd;
void *ptr;
shm_fd = shm_open(Obj, O_CREAT | O_RDWR, 0666);
ftruncate(shm_fd, SIZE);
ptr = mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (ptr == MAP_FAILED)
{
printf("Map failed\n");
return -1;
}
fgets(ptr, SIZE, stdin);
printf("Producer: Writing the sequence to the shared-memory object is done! \n");
return 0;
}
consumer.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/mman.h>
int main()
{
const int SIZE = 4096;
const char *Obj = "Shm";
int shm_fd;
void *ptr;
shm_fd = shm_open(Obj, O_RDONLY, 0666);
if (shm_fd == -1)
{
printf("Shared memory failed\n");
exit(-1);
}
ptr = mmap(0, SIZE, PROT_READ, MAP_SHARED, shm_fd, 0);
if (ptr == MAP_FAILED)
{
printf("Map failed\n");
exit(-1);
}
printf("Consumer: The output sequence is: %d", (int *)ptr);
if (shm_unlink(Obj) == -1)
{
printf("Error removing the shared memory object %s\n", Obj);
exit(-1);
}
return 0;
}

About memory alignment with Direct IO

The linux kernel version is 4.15.0-47-generic and the Block Size is 4096 via command sudo /sbin/tune2fs -l /dev/sda1|grep "Block size".
I just wrote a DIRECT IO demo, code as follows:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#define BUF_SIZE 1024
int main(int argc, char * argv[])
{
int fd;
int ret;
unsigned char *buf;
ret = posix_memalign((void **)&buf, 512, BUF_SIZE);
if (ret) {
perror("posix_memalign failed");
exit(1);
}
memset(buf, 'c', BUF_SIZE);
fd = open("./direct_io.data", O_WRONLY | __O_DIRECT | O_CREAT, 0755);
if (fd < 0){
perror("open ./direct_io.data failed");
exit(1);
}
do {
ret = write(fd, buf, BUF_SIZE);
if (ret < 0) {
perror("write ./direct_io.data failed");
}
} while (0);
free(buf);
close(fd);
}
My question:
The Block Size is 4096, why the above demo in which buf address is 512 alignment, and buf size is multiple of 512 byte could run correctly. I have searched many information which say that the address and buffer size must be multiple of Block Size.

Named Semaphore just not working

we had an exam today and we had a task to implement a "train-handler".
There are 7 trains represented by one process each. Each train arrives after a couple of seconds, checks if 1 of our 3 traintracks is available. If not, wait...
If track is free, enter it and lock it.
Stay for at the train station for a few seconds, leave and unlock it.
Me and a few friends are trying to make our program run but we just can't get it done. It seems to be the problem that our shared memory is not synchronized properly (semaphore). Using a mac, so I have to use named semaphores.
compiled with: "gcc -Wall -Werror -std=gnu99 -lpthread process_trains.c -o test"
CODE:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <time.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <semaphore.h>
#include <sys/wait.h>
#include <errno.h>
sem_t *sem;
int *shm_ptr;
int *initShm (int size) {
int shm_fd = 0;
if((shm_fd = shm_open("/shm", O_CREAT | O_RDWR, 0777)) == -1) {
perror("Error creating shared memory segment!");
}
if ((ftruncate(shm_fd, size)) == -1) {
perror("Error sizing shared memory segment!");
}
return (int*) mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
}
int trainAboutToArrive(int arrive, int stay, int Y){
int Z=0;
//Zug kommt in "arrive" Sekunden an
sleep(arrive);
while (shm_ptr[Z]!=0) {
Z++;
if(Z==3){
Z=0;
}
}
sem_wait(sem);
shm_ptr[Z]=1;
sem_post(sem);
printf("Zug %d ist auf Gleis %d eingefahren\n", Y, 1+Z);
//Zug hat einen Aufenthalt von "stay" Sekunden
sleep(stay);
sem_wait(sem);
shm_ptr[Z]=0;
sem_post(sem);
sem_close(sem);
printf("Zug %d verlässt Gleis %d\n", Y, 1+Z);
return EXIT_SUCCESS;
}
int main(int argc, char const *argv[]) {
shm_unlink("shm");
int i=0, tracks=3, trains=7, status;
int arrival[]={0,0,3,2,5,4,2};
int stay[]={2,3,7,2,1,4,3};
off_t size = sizeof(int)*tracks;
shm_ptr = initShm(size);
if((sem = sem_open("/semap",O_CREAT,0644,1)) == SEM_FAILED) {
perror("client sem_open");
}
for (i=0; i < tracks; i++) {
shm_ptr[i]= 0;
}
pid_t pids[trains];
for (i = 0; i < trains; i++) {
pids[i] = fork();
if(pids[i] == -1) {
perror("Error creating train-process!!");
} else if (pids[i] == 0) {
trainAboutToArrive(arrival[i], stay[i], 1+i);
exit(0);
}else if (pids[i] > 0) {
}
}
for(i=0; i < trains; i++){
waitpid(pids[i], &status, 0);
}
shm_unlink("shm");
return EXIT_SUCCESS;
}
Link with -pthread!!!! man page of all used semaphore functions tells us >.<
Thanks for everyones help!!
And for everyone who's interested, this is my code now. I improved a lot of things I didn't have time for in the exam. This runs perfectly and in my "beginners-eyes" this is not improvable by using the given functions (semaphores, shared mem...). If it is, I'd be grateful for tips & tricks ;)
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <semaphore.h>
#include <sys/wait.h>
#include <errno.h>
int *shm_ptr;
int *initShm (off_t size) {
int shm_fd = 0;
if((shm_fd = shm_open("/shm", O_CREAT | O_RDWR, 0777)) == -1) {
perror("Error creating shared memory segment!");
}
if ((ftruncate(shm_fd, size)) == -1) {
perror("Error sizing shared memory segment!");
}
return (int*) mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
}
void initSem(sem_t **plats) {
if((plats[0] = sem_open("/one",O_CREAT,0644,1)) == SEM_FAILED) {
perror("client sem_open");
}
if((plats[1] = sem_open("/two",O_CREAT,0644,1)) == SEM_FAILED) {
perror("client sem_open");
}
if((plats[2] = sem_open("/three",O_CREAT,0644,1)) == SEM_FAILED) {
perror("client sem_open");
}
}
int trainAboutToArrive(int arrive, int stay, int train, sem_t **plats){
srand(getpid());
int platform = rand()%3;
sleep(arrive);
while (3) {
sem_wait(plats[platform]);
if(shm_ptr[platform]==0){
shm_ptr[platform]=1;
break;
}
sem_post(plats[platform]);
platform = rand() % 3;
}
printf("Train %d enters platform %d\n", train, 1+platform);
sleep(stay);
shm_ptr[platform]=0;
printf("Train %d leaves platform %d\n", train, 1+platform);
sem_post(plats[platform]);
sem_close(plats[platform]);
return EXIT_SUCCESS;
}
int main(int argc, char const *argv[]) {
shm_unlink("/shm");
sem_unlink("/one");
sem_unlink("/two");
sem_unlink("/three");
int i=0, tracks=3, trains=7, status;
int arrival[]={0,0,3,2,5,4,2};
int stay[]={2,3,7,2,1,4,3};
sem_t *plats[3];
off_t size = sizeof(int)*tracks;
shm_ptr = initShm(size);
initSem(plats);
for (i=0; i < tracks; i++) {
shm_ptr[i]= 0;
}
pid_t pids[trains];
for (i = 0; i < trains; i++) {
pids[i] = fork();
if(pids[i] == -1) {
perror("Error creating train-process!!");
} else if (pids[i] == 0) {
trainAboutToArrive(arrival[i], stay[i], 1+i, plats);
exit(0);
}else if (pids[i] > 0) {
}
}
for(i=0; i < trains; i++){
waitpid(pids[i], &status, 0);
}
shm_unlink("/shm");
sem_unlink("/one");
sem_unlink("/two");
sem_unlink("/three");
return EXIT_SUCCESS;
}

Read last 20 bytes from a file

int fd, read_byte;
char *c;
fd = open("foo.txt", O_RDONLY);
read_byte = read(fd, c, 20);
printf("");
How to read last 20 bytes from a file and print the read_byte to the screen.
Use lseek(2)
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main()
{
int fd, read_byte;
char c[21];
fd = open("foo.txt", O_RDONLY);
if (fd == -1) {
printf("Error opening file\n");
return -1;
}
// reposition fd to position `-20` from the end of file.
lseek(fd, -20L, SEEK_END);
read_byte = read(fd, c, 20); // Read 20 bytes
c[read_byte] = '\0';
printf("%s\n", c);
close(fd);
return 0;
}

Resources