Austin
Austin

Reputation: 7319

What am I doing wrong with read() and write()?

I'm trying to do some beginner kernel module/user level program communications. I got the suggestion earlier to use fdopen() which worked well, but I've found out I'm required to use open(), read() and write() instead. I read the man pages for these and thought I converted the fopen,fgets,fputs to these correctly and my program compiles, but I'm not getting the desired output.

I have a timer where if I enter ./userprogram -s (int) (name) e.g. ./userprogram -s 5 hello, after 5 seconds it will print hello to the console by communicating with my kernel module. After I've switched to these new functions it instead prints /lib/ld-uClibc.so.0 (and seems to wait ~5 seconds regardless now). Do I also need to change how my kernel level code works? I thought I'd just be able to change the user level program and the kernel module would continue to work as it did before. Here's what I tried wit the original code that worked commented out:

// open file
int pFile;
pFile = open("/dev/mytimer", O_RDWR);
if (pFile < 0) {
    fprintf (stderr, "mytimer module isn't loaded\n");
    return 1;
}

// Check if timer set
if (argc >= 4 && strcmp(argv[1], "-s") == 0) {
    lenNum = strlen(argv[2]);
    lenName = strlen(argv[3]);
    char *ptr = malloc(lenNum+lenName+4);
    strncat(ptr, argv[1], 2);//flag
    strncat(ptr," ", 1);
    strncat(ptr, argv[2], lenNum);//timer length
    strncat(ptr," ", 1);
    strncat(ptr, argv[3], lenName);//message

    /* fputs(ptr, pFile); */
    write(pFile, ptr, sizeof(ptr));
    /*
    while (fgets(line, 256, pFile) != NULL) {
        printf("%s", line);
    } */
    while (read(pFile, ptr, sizeof(ptr)) != 0) {
        printf("%s", line);
    }   

Any suggestions are appreciated.

Upvotes: 1

Views: 113

Answers (2)

John Bollinger
John Bollinger

Reputation: 180103

[I] thought I converted the fopen,fgets,fputs to these correctly

... but you were mistaken. There is no one-line write()-based equivalent to fputs(), and no one-line read()-based equivalent to fgets(). The stream-based I/O functions do a lot of work that you have to do yourself when you use low-level read()s and write()s.

Some of the differences are:

  • read() does not supply string termination. You have to do that yourself.
  • write() does not pay attention to string termination. If you want it to stop at string terminators then you have to control that via the number of bytes you ask it to transfer.
  • read() and write() are neither one guaranteed to transfer the full number of bytes requested in one call. If you want to transfer a specific number of bytes then you have to be prepared to loop.
  • read() does not automatically stop at any particular character, including newline
  • Most streams provide for buffering, invisibly to you. If you want to buffer your read()s and write()s then you have to handle it yourself (but those functions are well suited for it).

In your particular case, however, you have also committed a semantic error. The third argument to both write() and read() is the the maximum number of bytes to transfer -- often the size of the buffer -- but sizeof(ptr) is the size of your pointer itself, not the size of the space to which it points.

Upvotes: 4

Jens
Jens

Reputation: 72629

sizeof(ptr) is probably 4 or 8 (i.e. sizeof(char *)), not the length of the data you want to write.

And the way you construct the data is extremely cumbersome. Why not use a single sprintf(), or, if you have it, asprintf(), which even mallocs the memory:

char *ptr;
int i;

i = asprintf (&ptr, "%s %s %s", argv[1], argv[2], argv[3]);
write (pFile, ptr, i);
free(ptr);

Ain't that sweet and concise?

Upvotes: 3

Related Questions