串口系统调用(read,write)中的一些注意事项和优化

前言

在使用涉及到系统调用的编程中,操作的文件描述符诸如管道之类的,获设备节点之类的(比如串口,USB口中讲usb作为camera的),涉及到内核态和用户态的数据读写,这时候经常遇到读写达不到理想,本文编写方式先列出问题点,后面给出对应的解决方案。

该文章主要针对系统调用注意事项,未完待续…


昨天(2022年7月7日)一位上海的朋友找我咨询串口的问题, 他采用的接收端模型为select+read方式, 发送端是RFID一直发送数据,发送频率10hz, 接收没有采用缓存起来然后解析, 导致read的数据产生黏包。

问题和处理

在串口编程中, 即使使用select复用IO方式获取到内核态有数据可读,也可能无法满足你想的结果。比如算出需要读10个字节(理论按照私有协议有10字节才是一个完整包),有一定概率出现读小于10个字节的数据, 原因为

内核中用于套接字的缓冲区可能已达到了极限。,此时所需的是调用者再次调用read或write函数,输入或输出剩余的字节。

因此针对该问题使用如下替代方案:
通过该方式可以满足读出想读的数据大小,如果读的数据小于n,那么说明串口通信另外一端丢包了,
只能将该包丢掉。
读:

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 */

写:

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 */

使用recv函数替代readn函数

未完待续…

思考

为什么readn和writenn函数都将void转为char指针?

在ANSI C标准中,不允许对void指针进行算术运算如pvoid++或pvoid+=1等,需要转换为char类型指针才能对指针进行加减操作。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);

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值