Article directory
foreword
In the programming involving system calls, the operating file descriptors, such as pipes, obtain device nodes and the like (such as serial ports, USB ports that use usb as a camera), and involve data reading in kernel mode and user mode. Writing. At this time, it is often encountered that reading and writing cannot meet the ideal. The writing method of this article first lists the problem points, and then gives the corresponding solutions.
This article mainly focuses on system call precautions, to be continued...
Yesterday (July 7, 2022) a friend from Shanghai asked me about the serial port. The receiving end model he used was select+read. The sending end used RFID to send data all the time, and the sending frequency was 10hz. The receiving end was not cached. Then parse it, resulting in sticky packets for the read data.
1. Problems and handling
In serial port programming, even if you use select复用IO
the method to obtain data readable in the kernel state, it may not be able to meet the results you want. For example, it is calculated that 10 bytes need to be read (theoretically, according to the private protocol, 10 bytes is a complete package), and there is a certain probability that data less than 10 bytes will be read. The reason is that
内核中用于套接字的缓冲区可能已达到了极限。
, all that is required at this point is for the caller to call the read or write function again to input or output the remaining bytes.
Therefore use the following alternatives for this problem:
by this method, the data size that you want to read can be satisfied. If the data read is less than n, it means that the other end of the serial port communication has lost a packet, and the
packet can only be discarded.
read:
ssize_t /* Read "n" bytes from a descriptor. */
readn(int fd, void *vptr, size_t n)
{
size_t nleft;
ssize_t nread;
char *ptr;
ptr = vptr;
nleft = n;
while (nleft > 0) {
if ( (nread = read(fd, ptr, nleft)) < 0) {
if (errno == EINTR)
nread = 0; /* and call read() again */
else
return(-1);
} else if (nread == 0)
break; /* EOF */
nleft -= nread;
ptr += nread;
}
return(n - nleft); /* return >= 0 */
}
/* end readn */
Write:
ssize_t /* Write "n" bytes to a descriptor. */
writen(int fd, const void *vptr, size_t n)
{
size_t nleft;
ssize_t nwritten;
const char *ptr;
ptr = vptr;
nleft = n;
while (nleft > 0) {
if ( (nwritten = write(fd, ptr, nleft)) <= 0) {
if (nwritten < 0 && errno == EINTR)
nwritten = 0; /* and call write() again */
else
return(-1); /* error */
}
nleft -= nwritten;
ptr += nwritten;
}
return(n);
}
/* end writen */
2. Use the recv function instead of the readn function
To be continued...
3. think
Why both the readn and writenn functions convert void to char pointers?
In the ANSI C standard, it is not allowed to perform arithmetic operations on void pointers, such as pvoid++ or pvoid+=1, etc. It needs to be converted to a char type pointer to perform addition and subtraction operations on pointers.
2.c:17:8: error: invalid use of void expression *p = *pa;
root@ubuntu:/opt/socket/unpv13e/intro# cat 2.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char a[10] = {0};
memcpy(a, "12345", strlen("12345"));
char *pa = a;
void *p = (void*)calloc(1, 10);
void *p1 = p; // notice
if(p)
{
for(; *pa != '\0'; pa++, p++) {
*p = *pa;
}
printf("p1:%s\n", p1);
free(p1); p1 = 0;
}
return 0;
}
root@ubuntu:/opt/socket/unpv13e/intro# gcc -o readn 2.c
2.c: In function ‘main’:
2.c:17:5: warning: dereferencing ‘void *’ pointer
*p = *pa;
^~
2.c:17:8: error: invalid use of void expression
*p = *pa;
^
2.c:23:15: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘void *’ [-Wformat=]
printf("p1:%s\n", p1);