IGKBoard(imx6ull)-Linux下TTY串口编程

news2025/1/26 15:51:54

文章目录

    • 一、TTY介绍
      • (1)理解tty
      • (2)tty设备节点
    • 二、tty串口应用编程
      • (1)串口基本操作
        • 【1】打开串口
        • 【1】读写数据
        • 【1】关闭串口
      • (2)termios 结构体(配置)
      • (3)终端控制API函数
      • (4)终端的三种工作模式
    • 三、tty串口应用编程实现
      • (1)硬件连接
      • (2)应用编程代码
      • (3)运行结果
        • 【1】发送数据
        • 【2】接收数据
    • 四、Linux下异步I/O:O_ASYNC标志(信号驱动I/O)
      • (1)信号驱动I/O
      • (2)信号处理函数


一、TTY介绍

(1)理解tty

简单来说,tty 是 Teletype / Teletypewriter 的缩写。而 Teletype / Teletypewriter 的中文意思则是电传打字机。
大家可以参考这篇文章来理解:点击打开(对终端、命令行和shell等的理解)

终端 = tty = Teletype / Teletypewriter = 电传打字机(可以这样理解)

简单来说,tty 是终端的统称。早期的终端是电传字打印机(Teletype / Teletypewriter),英文缩写就是 tty。虽然终端设备已经不再限制于电传打字机了,但是 tty 这个名称还是就这么保留了下来。

(2)tty设备节点

我们到/dev/路径下看一下tty的设备节点,可以看见很多:

root@igkboard:~# ls /dev                   
autofs           gpiochip1     loop0    mmcblk1boot0  pps1   ram14   rtc     tty10  tty20  tty30  tty40  tty50  tty60    ttymxc6             vcs6   vcsu3
block            gpiochip2     loop1    mmcblk1boot1  ptmx   ram15   rtc0    tty11  tty21  tty31  tty41  tty51  tty61    ubi_ctrl            vcsa   vcsu4
bus              gpiochip3     loop2    mmcblk1p1     ptp0   ram2    rtc1    tty12  tty22  tty32  tty42  tty52  tty62    udev_network_queue  vcsa1  vcsu5
char             gpiochip4     loop3    mmcblk1p2     ptp1   ram3    shm     tty13  tty23  tty33  tty43  tty53  tty63    urandom             vcsa2  vcsu6
console          hwrng         loop4    mmcblk1rpmb   pts    ram4    snd     tty14  tty24  tty34  tty44  tty54  tty7     v4l                 vcsa3  vga_arbiter
cpu_dma_latency  i2c-1         loop5    mqueue        ram0   ram5    stderr  tty15  tty25  tty35  tty45  tty55  tty8     vcs                 vcsa4  vhci
disk             initctl       loop6    mxc_asrc      ram1   ram6    stdin   tty16  tty26  tty36  tty46  tty56  tty9     vcs1                vcsa5  video0
fd               input         loop7    net           ram10  ram7    stdout  tty17  tty27  tty37  tty47  tty57  ttymxc0  vcs2                vcsa6  watchdog
full             kmsg          mapper   null          ram11  ram8    tty     tty18  tty28  tty38  tty48  tty58  ttymxc1  vcs3                vcsu   watchdog0
fuse             log           mem      port          ram12  ram9    tty0    tty19  tty29  tty39  tty49  tty59  ttymxc2  vcs4                vcsu1  zero
gpiochip0        loop-control  mmcblk1  pps0          ram13  random  tty1    tty2   tty3   tty4   tty5   tty6   ttymxc3  vcs5                vcsu2

这些节点的总结如下(注:表格中的 X 代表数字编号)
在这里插入图片描述


二、tty串口应用编程

(1)串口基本操作

【1】打开串口

打开串口连接的时候,程序在open函数中除了Read+Write模式以外还需要指定O_NOCTTY选项:

fd=open("/dev/ttymxc0",O_RDWR|O_NOCTTY);

标志O_NOCTTY告诉系统这个程序不会成为这个端口上的“控制终端”。如果不这样做的话,所有的输入,比如键盘上过来的Ctrl+C中止信号等等,会影响到你的进程。

【1】读写数据

读数据的时候需要找准时机,需要知道串口何时有数据,可以使用linux下的轮询机制进行监控串口的文件描述符:

rv = read(fd, buf, 1024);

Linux下一切皆文件,写数据直接使用write、fputs等函数即可直接向串口发送数据:

rv= write(fd, buf, sizeof(buf));

【1】关闭串口

可以使用close系统调用关闭串口:

close(fd);

(2)termios 结构体(配置)

termios是面向所有终端设备的。termios 结构体:

struct termios
{
	tcflag_t c_iflag; /* input mode flags */
	tcflag_t c_oflag; /* output mode flags */
	tcflag_t c_cflag; /* control mode flags */
	tcflag_t c_lflag; /* local mode flags */
	cc_t c_line; /* line discipline */
	cc_t c_cc[NCCS]; /* control characters */
	speed_t c_ispeed; /* input speed */
	speed_t c_ospeed; /* output speed */
};

c_iflag

输入模式标志,控制终端输入方式。
在这里插入图片描述

c_oflag
输出模式标志,控制终端输出方式。
在这里插入图片描述

c_cflag
控制模式标志,指定终端硬件控制信息.。
在这里插入图片描述
c_lflag
本地模式标志,控制终端编辑功能。
在这里插入图片描述
c_cc[NCCS]
​ ​控制字符​​,用于保存终端驱动程序中的​ 特殊字符​​,如输入结束符等。
在这里插入图片描述

(3)终端控制API函数

具体函数参数等可以自行man看看,后面的代码中涉及到的也可以拿来参考。

tcgetattr      取属性(termios结构)
tcsetattr      设置属性(termios结构)
cfgetispeed    得到输入速度
cfgetospeed    得到输出速度
cfsetispeed    设置输入速度
cfsetospeed    设置输出速度
tcdrain        等待所有输出都被传输
tcflow         挂起传输或接收
tcflush        刷清未决输入和/或输出
tcsendbreak    送BREAK字符
tcgetpgrp      得到前台进程组ID
tcsetpgrp      设置前台进程组ID
cfmakeraw      将终端设置成原始模式
cfsetspeed     设置输入输出速度

其中设置输入输出波特率的时候需要注意,波特率可以设置如下:
B0​、B50​、B75​、B110​、B134​、B150​、B200​、B300​、B600​、B1200​、B1800​、B2400​、B4800​、B9600​、B19200​、B38400​、 B57600、 B115200、​ B230400

struct termios uart_cfg;memset(&uart_cfg,0,sizeof(struct termios));cfsetispeed(&uart_cfg,B115200); //设置输入波特率​
cfsetospeed(&uart_cfg,B115200); //设置输出波特率

(4)终端的三种工作模式

终端的三种工作模式,分别是规范模式 canonical mode,非规范模式 non-canonical mode 和原始模式 raw mode。

通过设置 c_lflag 设置 ICANNON 标志来定义终端是以规范模式还是非规范模式工作,默认为规范模式。

  • 规范模式

所有输入基于行进行处理。在用户输入一个行结束符(回车符、EOF【End Of File】等)之前,系统调用read()函数读不到用户输入的任何字符。其次,除了EOF之外的行结束符与普通字符一样会被read()函数读取到缓冲区中。一次调用read()只能读取一行数据。

  • 原始模式

是一种特殊的非规范模式,所有的输入数据以字节为单位被处理。即有一个字节输入时,触发输入有效。

  • 非规范模式

所有输入时即时有效的,不需要用户另外输入行结束符。
在非规范模式下,对参数 MIN(c_cc[VMIN])和 TIME(c_cc[VTIME])的设置决定 read 函数的调用方式,MIN 和 TIME 的取值不同,会有以下四种不同的情况:
在这里插入图片描述


三、tty串口应用编程实现

(1)硬件连接

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

(2)应用编程代码

/*********************************************************************************
 *      Copyright:  (C) 2023 WangDengtao<1799055460@qq.com>
 *                  All rights reserved.
 *
 *       Filename:  uart_test.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(2023年04月08日)
 *         Author:  WangDengtao <1799055460@qq.com>
 *      ChangeLog:  1, Release initial version on "2023年04月08日 14时45分32秒"
 *                 
 ********************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <libgen.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <signal.h>

#define	READ_FLAG 0			/*读标志*/
#define	WRITE_FLAG 1		/*写标志*/

struct uart_parameter {
    unsigned int    baudrate;       // 波特率 
    unsigned char   dbit;           // 数据位 
    char            parity;         // 奇偶校验 
    unsigned char   sbit;           // 停止位 
};

static struct termios   oldtio;     // 用于保存终端的配置参数
static int              fd_uart;  	// 串口终端对应的文件描述符

static int uart_init(const char *device);//串口初始化
static int uart_configuration(const struct uart_parameter *para);//串口配置
static void async_io_init(void);//异步i/o初始化函数
static void io_handler(int sig, siginfo_t *info, void *context);//信号处理函数,当串口有数据可读时,会跳转到该函数执行
static void usage(char *progname);//提示信息

int main(int argc, char *argv[])
{
    struct uart_parameter   uart_para;
    char                    device[64];
    int                     rw_flag = -1;
    unsigned char           write_buf[10] = {0x11, 0x22, 0x33, 0x44,0x55, 0x66, 0x77, 0x88};    
    int                     n;
    int                     opt;
	char					*progname=NULL;

    memset(&uart_para, 0x0, sizeof(struct uart_parameter));
    memset(device, 0x0, sizeof(device));

    struct option           long_options[] = {
        {"device", required_argument, NULL, 'D'},
        {"type", required_argument, NULL, 'T'},
        {"brate", no_argument, NULL, 'b'},
        {"dbit", no_argument, NULL, 'd'},
        {"parity", no_argument, NULL, 'p'},
        {"sbit", no_argument, NULL, 's'},
        {"help", no_argument, NULL, 'h'},
        {NULL, 0, NULL, 0}
    };

    memset(&uart_para, 0x0, sizeof(struct uart_parameter));
	progname = (char *)basename(argv[0]);

    while((opt = getopt_long(argc, argv, "D:T:b:d:p:s:h", long_options, NULL)) != -1)
    {
        switch(opt)
        {
            case'D':
                strcpy(device, optarg);
                break;
                
            case'T':
                if (!strcmp("read", optarg))
                {
                    rw_flag = READ_FLAG;   
                }       
                else if (!strcmp("write", optarg))
                {
                    rw_flag = WRITE_FLAG;   
                }
                break;
                
            case'b':
                uart_para.baudrate = atoi(optarg);
                break;

            case'd':
                uart_para.dbit = atoi(optarg);
                break;

            case'p':
                uart_para.parity = *optarg;
                break;

            case's':
                uart_para.sbit = atoi(optarg);
                break;

            case'h':
                usage(progname);
                return 0;

            default:
                break;
        }
    }

    if (NULL == device || -1 == rw_flag) 
    {
        usage(progname);
        return -1;
    }

    /* 串口初始化 */ 
    if (uart_init(device))
    {
        printf("fail to execute uart_init\n");
        return -2;
    }

    /* 串口配置 */ 
    if (uart_configuration(&uart_para)) 
    {
        /* 恢复之前的配置 */
        tcsetattr(fd_uart, TCSANOW, &oldtio);   
        return -3;
    }

    /* 通过读写标志判断读写,然后进行读写 */
    switch (rw_flag) 
    {
        case 0:  // 读串口数据
            async_io_init();	// 我们使用异步 i/o 方式读取串口的数据,调用该函数去初始化串口的异步 i/o
            for ( ; ; )         // 进入休眠,等待有数据可读,有数据可读之后就会跳转到 io_handler() 函数
            {
                sleep(1);
            }
            break;
        case 1:   // 向串口写入数据
            for ( ; ; ) 
            {   		
                write(fd_uart, write_buf, 8); 	
                sleep(1);       	
            }
            break;
    }

    tcsetattr(fd_uart, TCSANOW, &oldtio);  
    close(fd_uart);
    
    return 0;
}

static int uart_init(const char *device)
{
    fd_uart = open(device, O_RDWR | O_NOCTTY);
    if (0 > fd_uart)
    {
        printf("fail to open uart file\n");
        return -1;
    }

    /* 获取串口当前的配置参数 */
    if (0 > tcgetattr(fd_uart, &oldtio))
    {
        printf("fail to get old attribution of terminal\n");
        close(fd_uart);
        return -2;
    }

    return 0;
}


static int uart_configuration(const struct uart_parameter *para)
{
    struct termios newtio;
    speed_t speed;

    /* 设置为原始模式
     * 配置为原始模式相当于已经对 newtio 做了如下配置
     * IGNBRK 忽略输入终止条件,BRKINT 检测到终止条件发送 SIGINT 信号,PARMRK 对奇偶校验做出标记
     * ISTRIP 裁剪数据位为 7 bit,去掉第八位,INLCR 换行符转换为回车符,IGNCR 忽略回车符
     * ICRNL 将回车符转换为换行符,IXON 启动输出流控
     * OPOST 启用输出处理功能
     * ECHO 使能回显,ICANON 规范模式,ISIG 收到信号产生相应的信号,IEXTEN 输入处理
     * CSIZE 数据位掩码,PARENB 使能校验,CS8 8 个数据位
     * termios_p->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP| INLCR | IGNCR | ICRNL | IXON);
     * termios_p->c_oflag &= ~OPOST;
     * termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
     * termios_p->c_cflag &= ~(CSIZE | PARENB);
     * termios_p->c_cflag |= CS8;
     */
    memset(&newtio, 0x0, sizeof(struct termios));
    cfmakeraw(&newtio);

    /* CREAD 使能接受 */
    newtio.c_cflag |= CREAD;

    /* 设置波特率 */
    switch (para->baudrate)
    {
        case 1200:
            speed = B1200;
            break;
        case 1800:
            speed = B1800;
            break;
        case 2400:
            speed = B2400;
            break;
        case 4800:
            speed = B4800;
            break;
        case 9600:
            speed = B9600;
            break;
        case 19200:
            speed = B19200;
            break;
        case 38400:
            speed = B38400;
            break;
        case 57600:
            speed = B57600;
            break;
        case 115200:
            speed = B115200;
            break;
        case 230400:
            speed = B230400;
            break;
        case 460800:
            speed = B460800;
            break;
        case 500000:
            speed = B500000;
            break;
        default:
            speed = B115200;
            printf("default baud rate is 115200\n");
            break;
    }

    /* cfsetspeed 函数,设置波特率 */
    if (0 > cfsetspeed(&newtio, speed))
    {
        printf("fail to set baud rate of uart\n");
        return -1;
    }

	/* 设置数据位大小
     * CSIZE 是数据位的位掩码,与上掩码的反,就是将数据位相关的比特位清零
     * CSX (X=5,6,7,8) 表示数据位位数
     */
    newtio.c_cflag &= ~CSIZE;
    switch (para->dbit)
    {
        case 5:
            newtio.c_cflag |= CS5;
            break;
        case 6:
            newtio.c_cflag |= CS6;
            break;
        case 7:
            newtio.c_cflag |= CS7;
            break;
        case 8:
            newtio.c_cflag |= CS8;
            break;
        default:
            newtio.c_cflag |= CS8;
            printf("default data bit size is 8\n");
            break;
   	}

	/* 设置奇偶校验
     * PARENB 用于使能校验
     * INPCK 用于对接受的数据执行校验
 	 * PARODD 指的是奇校验
     */
    switch (para->parity)
    {
        case 'N':   //无校验
            newtio.c_cflag &= ~PARENB;
            newtio.c_iflag &= ~INPCK;
            break;
        case 'O':   //奇校验
            newtio.c_cflag |= (PARODD | PARENB);
            newtio.c_iflag |= INPCK;
            break;
        case 'E':   //偶校验
            newtio.c_cflag |= PARENB;
            newtio.c_cflag &= ~PARODD;
            newtio.c_iflag |= INPCK;
            break;
        default:    //默认配置为无校验
            newtio.c_cflag &= ~PARENB;
            newtio.c_iflag &= ~INPCK;
            printf("default parity is N (no check)\n");
            break;
    }

	/* 设置停止位
     * CSTOPB 表示设置两个停止位
     */
    switch (para->sbit)
    {
        case 1:     //1个停止位
            newtio.c_cflag &= ~CSTOPB;
            break;
        case 2:     //2个停止位
            newtio.c_cflag |= CSTOPB;
            break;
        default:    //默认配置为1个停止位
            newtio.c_cflag &= ~CSTOPB;
            printf("default stop bit size is 1\n");
            break;
   	}

	/* 将 MIN 和 TIME 设置为 0,通过对 MIN 和 TIME 的设置有四种 read 模式
     * read 调用总是会立即返回,若有可读数据,则读数据并返回被读取的字节数,否则读取不到数据返回 0
     */
    newtio.c_cc[VTIME] = 0;
    newtio.c_cc[VMIN] = 0;

    /* 清空输入输出缓冲区 */
    if (0 > tcflush(fd_uart, TCIOFLUSH))
    {
        printf("fail to flush the buffer\n");
        return -3;
    }

    /* 写入配置,使配置生效 */
    if (0 > tcsetattr(fd_uart, TCSANOW, &newtio))
    {
        printf("fail to set new attribution of terminal\n");
        return -4;
    }

    return 0;
}

/* 异步 i/o 初始化函数 */
static void async_io_init(void)
{
    struct sigaction    sigatn;
    int                 flag;

    /* 使能异步 i/o,获取当前进程状态,并开启当前进程异步通知功能 */
    flag = fcntl(fd_uart, F_GETFL);
    flag |= O_ASYNC;
    fcntl(fd_uart, F_SETFL, flag);

    /* 设置异步 i/o 的所有者,将本应用程序进程号告诉内核 */
    fcntl(fd_uart, F_SETOWN, getpid());

    /* 指定实时信号 SIGRTMIN 作为异步 i/o 通知信号 */
    fcntl(fd_uart, F_SETSIG, SIGRTMIN);

    /* 为实时信号 SIGRTMIN 注册信号处理函数
     * 当串口有数据可读时,会跳转到 io_handler 函数
     */
    sigatn.sa_sigaction = io_handler;
    sigatn.sa_flags = SA_SIGINFO;

    /* 初始化信号集合为空 */
    sigemptyset(&sigatn.sa_mask);

    /* sigaction 的功能是为信号指定相关的处理程序,但是它在执行信号处理程序时
     * 会把当前信号加入到进程的信号屏蔽字中,从而防止在进行信号处理期间信号丢失
     */
    sigaction(SIGRTMIN, &sigatn, NULL);
}

/* 信号处理函数,当串口有数据可读时,会跳转到该函数执行 */
static void io_handler(int sig, siginfo_t *info, void *context)
{
    unsigned char   buf[10];
    int             ret;
    int             n;

    memset(buf, 0x0, sizeof(buf));

    if(SIGRTMIN != sig)
    {
        return;
    }

    /* 判断串口是否有数据可读 */
    if (POLL_IN == info->si_code)
    {
        ret = read(fd_uart, buf, 8);
        printf("[ ");
        for (n = 0; n < ret; n++)
        {
            printf("0x%hhx ", buf[n]);
        }
        printf("]\n");
    }
}

static void usage(char *progname)
{
	printf("Usage: %s [OPTION]...\n", progname);

	printf("The usage has been configured\n");
	printf("%s -D device -T write/read\n", progname);
	printf("If you want a different configuration, see below:\n");
    printf("-b brate\n");
    printf("-d dbit\n");
    printf("-p parity\n");
    printf("-s sbit\n");
    printf("-h help\n");

    return;
}

Makefile:

CC=arm-linux-gnueabihf-gcc
APP_NAME=uart_test

all:clean
	@${CC} ${APP_NAME}.c -o ${APP_NAME} -D_GNU_SOURCE

clean:
	@rm -f ${APP_NAME}

然后将生成的可执行文件下载到我们的开发板上。

如果开发板上没有使能我们的设备树插件,需要使能,然后重启即可。

root@igkboard:/run/media/mmcblk1p1# cat config.txt 
#Enble UART overlays
dtoverlay_uart=2 3 4 7

(3)运行结果

root@igkboard:~# tftp -gr uart_test 192.168.137.91
root@igkboard:~# chmod a+x uart_test 
root@igkboard:~# ./uart_test -h
Usage: uart_test [OPTION]...
The usage has been configured
uart_test -D device -T write/read
If you want a different configuration, see below:
-b brate
-d dbit
-p parity
-s sbit
-h help

打开我们的串口调试助手,连接上与转换器的接口。

串口调试助手中需要选择HEX(16进制)显示,不然会显示乱码:串口的Hex/AscII发送与显示

在这里插入图片描述

【1】发送数据

在命令上执行:

./uart_test -D /dev/ttymxc1 -T write

在这里插入图片描述

【2】接收数据

在命令上执行:

./uart_test -D /dev/ttymxc1 -T read

在这里插入图片描述


四、Linux下异步I/O:O_ASYNC标志(信号驱动I/O)

在上面的代码中,我们用到了 O_ASYNC 标志来完成I/O异步通信。什么为异步I/O,假设两个设备通信,其中一个设备正在接受发送过来的数据,但是同时也需要自己发送出去,这样就叫异步I/O,如果同步的话,这个设备发送完数据之后去接受,数据已经错过了。

(1)信号驱动I/O

在IO多路复用中,进程是通过系统调用(select、epoll)来检测文件描述符上是否可以执行IO。而在信号驱动IO中,进程请求内核当文件描述符上可执行IO操作时为自己发送一个信号。之后进程就可以执行任何其他的任务直到IO就绪为止。下面来说明一下上述代码中所使用信号驱动IO步骤:

1、得到文件描述符的状态标志集,为该状态标志集添加一个 O_ASYNC 属性:

flag = fcntl(fd_uart, F_GETFL);
/*
后面两步可以简化为一步:
fcntl(fd_uart, F_SETFL, flag | O_ASYNC);
*/
flag |= O_ASYNC;
fcntl(fd_uart, F_SETFL, flag);

2、用fcntl函数来设置一个用来接受信号的进程:

fcntl(fd_uart, F_SETOWN, getpid());

3、指定实时信号 SIGRTMIN 作为异步 i/o 通知信号,默认情况下,这个通知信号为SIGIO:

sigatn.sa_sigaction = io_handler;

4、为信号设置一个处理函数,用来读取并处理位于输入缓存中的数据:

sigatn.sa_sigaction = io_handler;

(2)信号处理函数

/* 信号处理函数,当串口有数据可读时,会跳转到该函数执行 */
static void io_handler(int sig, siginfo_t *info, void *context)
{
    unsigned char   buf[10];
    int             ret;
    int             n;

    memset(buf, 0x0, sizeof(buf));

    if(SIGRTMIN != sig)
    {
        return;
    }

    /* 判断串口是否有数据可读 */
    if (POLL_IN == info->si_code)
    {
        ret = read(fd_uart, buf, 8);
        printf("[ ");
        for (n = 0; n < ret; n++)
        {
            printf("0x%hhx ", buf[n]);
        }
        printf("]\n");
    }
}

相信大家看不懂的也只有这一行:POLL_IN == info->si_code

我们来看一下 siginfo_t 结构体:

siginfo_t {
    int si_signo; /* Signal number */
    int si_errno; /* An errno value */
    int si_code; /* Signal code */
    int si_trapno; /* Trap number that caused hardware-generated signal(unused on most
    architectures) */
    pid_t si_pid; /* Sending process ID */
    uid_t si_uid; /* Real user ID of sending process */
    int si_status; /* Exit value or signal */
    clock_t si_utime; /* User time consumed */
    clock_t si_stime; /* System time consumed */
    sigval_t si_value; /* Signal value */
    int si_int; /* POSIX.1b signal */
    void *si_ptr; /* POSIX.1b signal */
    int si_overrun; /* Timer overrun count; POSIX.1b timers */
    int si_timerid; /* Timer ID; POSIX.1b timers */
    void *si_addr; /* Memory location which caused fault */
    long si_band; /* Band event (was int in glibc 2.3.2 and earlier) */
    int si_fd; /* File descriptor */
    short si_addr_lsb; /* Least significant bit of address(since Linux 2.6.32) */
    void *si_call_addr; /* Address of system call instruction(since Linux 3.5) */
    int si_syscall; /* Number of attempted system call(since Linux 3.5) */
    unsigned int si_arch; /* Architecture of attempted system call(since Linux 3.5) */
}

我们着重看 si_code
在这里插入图片描述


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/413554.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何处理后端返回的复杂数据

将接口的复杂数据结构映射成简单的数据结构 假设我们有一个API&#xff0c;返回以下数据&#xff1a; {"id": 1,"name": "Example API","process_params": {"param1": {"name": "Parameter 1","…

企业数字化转型有哪些好处?

从马车到汽车&#xff0c;从写信到智能手机&#xff0c;每一次技术革新所带来的都是生产力大发展&#xff0c;小到个人、家庭&#xff0c;大到企业、国家&#xff0c;都在每一轮技术革新中获得了好处。 可以说&#xff0c;现在的企业数字化转型&#xff0c;就像是企业管理“工具…

从0学习stm32

1.STM32介绍&#xff1a; ST&#xff1a;指的是意法半导体&#xff1b; M&#xff1a;指定微处理器 使用的是ARMCortex-M3 ARM分成三个系列&#xff1a; Cortex-A: 针对多媒体应用(手机) Cortex-R:针对对实时性和性能有一定要求的场景 Cortex-M&#xff1a;针对低功耗高性…

基于html+css的自适应展示4

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

【案例实践】R语言多元数据统计分析在生态环境中的实践应用

查看原文>>>R语言生物群落分析绘图、多元统计分析、CMIP6、遥感碳储量、GEE林业、InVEST等 生态环境领域研究中常常面对众多的不同类型的数据或变量&#xff0c;当要同时分析多个因变量&#xff08;y&#xff09;时需要用到多元统计分析&#xff08;multivariate sta…

vite+vue3+ts搭建项目八(打包性能优化三:使用CDN)

vitevue3ts搭建项目八&#xff08;打包性能优化三&#xff1a;使用CDN&#xff09; 本文目录vitevue3ts搭建项目八&#xff08;打包性能优化三&#xff1a;使用CDN&#xff09;使用vite-plugin-cdn-import下载npm包在vite.config.ts中通过importToCDN引入CDN参数获取方式可用的…

【事务】本地事务和分布式事务的区别

整理下事务相关的知识点&#xff1a; 文章目录一、事务MySQL事务事务的实现事务的ACID特性事务的隔离级别二、本地事务三、分布式事务认识分布式事务CAP理论BASE理论一、事务 先复习下数据库中学到的事务&#xff1a; MySQL事务 一个事务即一个完整的业务逻辑&#xff0c;如…

计算机组成原理——第三章存储系统(下)

提示&#xff1a;年年岁岁花相似&#xff0c;岁岁年年人不同 文章目录3.4.1 磁盘存储器3.4.2 固态硬盘SSD3.5.1 Cache的基本概念和原理3.5.2 Cache和主存的映射方式3.5.3 Cache替换算法3.5.4 Cache写策略3.6.1 页式存储3.6.2 虚拟存储器3.4.1 磁盘存储器 我们在磁性盘面上涂磁…

【hello Linux】Linux下 gitee 的使用

目录 1. 安装 git 2. gitee 的使用 2.1 注册 gitee 账号 2.2 创建项目&#xff1a;也就是仓库 2.3 下载项目到本地 3. 上传gitee三步走 3.1 三板斧第一招&#xff1a;git add 3.2 三板斧第二招&#xff1a;git commit 3.3 三板斧第三招&#xff1a;git push Linux&#x1f337…

【JavaEE】Spring项目的创建与使用

目录 创建Spring项目 创建Maven项目 引入依赖 使用镜像下载 下载依赖 添加启动类 使用Spring项目 创建Spring配置文件 存储Bean 取出并使用Bean(通过ApplicationContext接口) 通过id 通过class 通过classid(推荐) 取出并使用Bean(通过BeanFactory接口) Applicat…

从零入门激光SLAM(六)——ROS常用工具箱

大家好呀&#xff0c;我是一个SLAM方向的在读博士&#xff0c;深知SLAM学习过程一路走来的坎坷&#xff0c;也十分感谢各位大佬的优质文章和源码。随着知识的越来越多&#xff0c;越来越细&#xff0c;我准备整理一个自己的激光SLAM学习笔记专栏&#xff0c;从0带大家快速上手激…

【react 全家桶】组合组件

本人大二学生一枚&#xff0c;热爱前端&#xff0c;欢迎来交流学习哦&#xff0c;一起来学习吧。 <专栏推荐> &#x1f525;&#xff1a;js专栏 &#x1f525;&#xff1a;vue专栏 &#x1f525;&#xff1a;react专栏 文章目录09 【组合组件】1.包含关系2.特例关系问题…

P3369 【模板】普通平衡树(FHQ Treap树构建和解析)

题目描述 您需要写一种数据结构&#xff08;可参考题目标题&#xff09;&#xff0c;来维护一些数&#xff0c;其中需要提供以下操作&#xff1a; 插入 x 数删除 x 数(若有多个相同的数&#xff0c;应只删除一个)查询 x 数的排名(排名定义为比当前数小的数的个数 1 )查询排名…

【Kafka-Kerberos下执行shell命令】Kafka在Kerberos环境下如何操作shell命令

【Kafka-Kerberos下执行shell命令】Kafka在Kerberos环境下如何操作shell命令1&#xff09;jaas.conf2&#xff09;client.properties3&#xff09;执行命令当大数据集群部署了 Kerberos 认证操作之后&#xff0c;在服务器上操作 kafka shell 命令就会出现认证相关的异常&#x…

【hello Linux】Linux开发工具

目录 1. vim&#xff1a;文本编辑器 1.1 各种模式的切换 补充&#xff1a;ctrl r命令 1.2 命令模式的操作 1.3 插入模式的操作 1.4 底行模式的操作 1.5 配置vim环境 1.6 配置亲属关系 2. gcc/g&#xff1a;编译器 2.1 预处理&#xff1a; 2.2 编译&#xff1a; 2.3 汇编&#x…

Gsum: A General Framework for Guided Neural Abstractive Summarization 论文笔记

Gsum: A General Framework for Guided Neural Abstractive Summarization 论文笔记 Year: 2021 Venue: NAACL Institution: CMU Code: https://github.com/neulab/guided_summarization Overview 这篇文章力求解决的问题是如何控制文本摘要生成&#xff0c;尤其注重可信度方…

floyed 4.9

今天是周末,也就不为难自己了,学习了一下floyed算法(确实简单哈哈),这个算法也是最短路的一种 在一副图中,a到b的距离可能不是直接的ab边最短,这时候就要用到floyed的思想了,可能是a到c到b比直接a到b最短.所以我们要枚举一边才可以知道a到b的最短的路线 这就是floyed的大概 …

7年测试,从功能测试到测试开发,我总算证明了自己

我感觉我是一个比较有发言权的人吧&#xff0c;我在测试行业爬模滚打7年&#xff0c;从点点点的功能测试到现在成为测试开发工程师&#xff0c;工资也翻了几倍&#xff1b; 个人觉得&#xff0c;测试的前景并不差&#xff0c;只要自己肯努力&#xff1b;我刚出来的时候是在鹅厂…

FL Studio21中文版D编曲数字音乐工作软件

随着现在人们的生活水平不断提高&#xff0c;我们的精神生活也越来越丰富&#xff0c;对于现在的年轻人来说&#xff0c;DJ舞曲是一个较受欢迎的领域&#xff0c;有许多年轻人对DJ这个职业感兴趣&#xff0c;想要深入了解DJ编曲这份工作&#xff0c;那么今天我们就来说一说DJ编…

聊聊ChatGPT无法取代的7个工作

ChatGPT——全世界都在谈论的非常流行的人工智能工具。自从 2022 年 11 月 30 日推出以来&#xff0c;ChatGPT 就被证明是执行复杂任务并简化它们的一体式工具。无论是通过 UPenn 的沃顿商学院 MBA 考试&#xff0c;撰写常春藤盟校入学论文&#xff0c;还是完成简单的任务&…