Some considerations and optimizations in serial port system calls (read, write)

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复用IOthe 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);

Guess you like

Origin blog.csdn.net/nc_linux/article/details/125077719