Linux +xenomai 下sendto()系统调用过程 --WT

本文详细分析了在Linux 4.9.38 + Xenomai 3.0.5实时系统中,sendto()系统调用在CAN通信中的实现过程。通过加载rtcan_adv_pci和rtcan_sja1000驱动,sendto调用经过rtdm_sendto、rtdm_sendmsg等函数,最终由rtcan_sja_start_xmit完成硬件发送,涉及内核缓冲区复制、中断信号等待和PCI驱动的io操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一 : sendto()系统调用

本文基于 linux-4.9.38+Xenomai 3.0.5 的实时系统
加载用的是xenomai下pci总线驱动rtcan_adv_pci,以及rtcan_sja1000的can控制器驱动。
Xenomai 3 的rtdm驱动更像一般的Linux驱动,device会在/dev/rtdm/xxx创建一个设备文件。而用户空间使用时,写得来也和Linux的一般char设备相似,使用sendto/recvfrom/open/close/read/write/ioctl等系统调用,实际上只不过在link的时候这些函数都被做了手脚,替换成了libcobalt.so中的函数(参见 /usr/xenomai/lib/cobalt.wrappers)。

二:代码分析

在应用程序中通过 sendto()传入套接字生成的文件描述符,将会产生如下的调用过程。
rtdm_sendto()->rtdm_sendmsg()->rtdm_fd_sendmsg()->rtcan_raw_sendmsg()->rtcan_sja_start_xmit()->chip->write_reg()->iowrite8()
将应用程序中的数据内容复制到内核发送缓冲区中,再进行对硬件的发送:
rtdm.h中

static inline ssize_t rtdm_sendto(int s, const void *buf, size_t len,
				  int flags, const struct sockaddr *to,
				  socklen_t tolen)
{
   
   
	struct user_msghdr msg;
	struct iovec iov;

	iov.iov_base = (void *)buf;
	iov.iov_len = len;
	msg.msg_name = (struct sockaddr *)to;
	msg.msg_namelen = tolen;
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	msg.msg_control = NULL;
	msg.msg_controllen = 0;

	return rtdm_sendmsg(s, &msg, flags);
}
static inline ssize_t rtdm_sendmsg(int s, const struct user_msghdr *msg, int flags)
{
   
   
	return rtdm_fd_sendmsg(s, msg, flags);
}

在fd.c中

ssize_t rtdm_fd_sendmsg(int ufd, const struct user_msghdr *msg, int flags)
{
   
   
	struct rtdm_fd *fd;
	ssize_t ret;

	fd = get_fd_fixup_mode(ufd);
	if (IS_ERR(fd)) {
   
   
		ret = PTR_ERR(fd);
		goto out;
	}

	set_compat_bit(fd);

	trace_cobalt_fd_sendmsg(current, fd, ufd, flags);

	if (ipipe_root_p)
		ret = fd->ops->sendmsg_nrt(fd, msg, flags);
	else
		ret = fd->ops->sendmsg_rt(fd, msg, flags);

	if (!XENO_ASSERT(COBALT, !spltest()))
		splnone();

	rtdm_fd_put(fd);
out:
	if (ret < 0)
		trace_cobalt_fd_sendmsg_status(current, fd, ufd, ret);

	return ret;
}

Xenomai 3.x protocol device description
(https://gitlab.denx.de/Xenomai/xenomai/-/wikis/Migrating_From_Xenomai_2_To_3)
rtcan_raw.c中
函数 fd->ops->sendmsg_rt(fd, msg, flags) = rtcan_raw_sendmsg()

static struct rtdm_driver rtcan_driver = {
   
   
	.profile_info		= RTDM_PROFILE_INFO(rtcan,
						    RTDM_CLASS_CAN,
						    RTDM_SUBCLASS_GENERIC,
						    RTCAN_PROFILE_VER),
	.device_flags		= RTDM_PROTOCOL_DEVICE,
	.device_count		= 1,
	.context_size		= sizeof(struct rtcan_socket),
	.protocol_family	= PF_CAN,
	.socket_type		= SOCK_RAW,
	.ops = {
   
   
		.socket		= rtcan_raw_socket,
		.close		= rtcan_raw_close,
		.ioctl_nrt	= rtcan_raw_ioctl,
		.recvmsg_rt	= rtcan_raw_recvmsg,
		.sendmsg_rt	= rtcan_raw_sendmsg,
	},
};

static struct rtdm_device rtcan_device = {
   
   
	.driver = &rtcan_driver,
	.label = "rtcan",
};

rtcan_raw.c中的
rtcan_raw_sendmsg()

ssize_t rtcan_raw_sendmsg(struct rtdm_fd *fd,
			  const struct user_msghdr *msg, int flags)
{
   
   
    struct rtcan_socket *sock = rtdm_fd_to_private(fd);
    struct sockaddr_can *scan = (struct sockaddr_can *)msg->msg_name;
    struct sockaddr_can scan_buf;
    struct iovec *iov = (struct iovec *)msg->msg_iov;
    struct iovec iov_buf;
    can_frame_t *frame;
    can_frame_t frame_buf;
    rtdm_lockctx_t lock_ctx;
    nanosecs_rel_t timeout = 0;
    struct tx_wait_queue tx_wait;
    struct rtcan_device *dev;
    int ifindex = 0;
    int ret  = 0;
    spl_t s;


    if (flags & MSG_OOB)   /* Mirror BSD error message compatibility */
	return -EOPNOTSUPP;

    /* Only MSG_DONTWAIT is a valid flag. */
    if (flags & ~MSG_DONTWAIT)
	return -EINVAL;

    /* Check msg_iovlen, only one buffer allowed */
    if (msg->msg_iovlen != 1)
	
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值