当前位置:首页 > 嵌入式 > 嵌入式教程
[导读]ARM的嵌入式Linux移植体验之应用实例

应用实例的编写实际上已经不属于Linux操作系统移植的范畴,但是为了保证本系列文章的完整性,这里提供一系列针对嵌入式Linux开发应用程序的实例。

编写Linux应用程序要用到如下工具:

(1)编译器:GCC

GCC是Linux平台下最重要的开发工具,它是GNU的C和C++编译器,其基本用法为:gcc[options][filenames]。

我们应该使用arm-linux-gcc。

(2)调试器:GDB

gdb是一个用来调试C和C++程序的强力调试器,我们能通过它进行一系列调试工作,包括设置断点、观查变量、单步等。

我们应该使用arm-linux-gdb。

(3)Make

GNUMake的主要工作是读进一个文本文件,称为makefile。这个文件记录了哪些文件由哪些文件产生,用什么命令来产生。Make依靠此makefile中的信息检查磁盘上的文件,如果目的文件的创建或修改时间比它的一个依靠文件旧的话,make就执行相应的命令,以便更新目的文件。

Makefile中的编译规则要相应地使用arm-linux-版本。

(4)代码编辑

可以使用传统的vi编辑器,但最好采用emacs软件,它具备语法高亮、版本控制等附带功能。

在宿主机上用上述工具完成应用程序的开发后,可以通过如下途径将程序下载到目标板上运行:

(1)通过串口通信协议rz将程序下载到目标板的文件系统中(感谢Linux提供了rz这样的一个命令);

(2)通过ftp通信协议从宿主机上的ftp目录里将程序下载到目标板的文件系统中;

(3)将程序拷入U盘,在目标机上mountU盘,运行U盘中的程序;

(4)如果目标机Linux使用NFS文件系统,则可以直接将程序拷入到宿主机相应的目录内,在目标机Linux中可以直接使用。

1.文件编程

Linux的文件操作API涉及到创建、打开、读写和关闭文件。

创建

intcreat(constchar*filename,mode_tmode);

参数mode指定新建文件的存取权限,它同umask一起决定文件的最终权限(mode&umask),其中umask代表了文件在创建时需要去掉的一些存取权限。umask可通过系统调用umask()来改变:

intumask(intnewmask);

该调用将umask设置为newmask,然后返回旧的umask,它只影响读、写和执行权限。

打开

intopen(constchar*pathname,intflags);

intopen(constchar*pathname,intflags,mode_tmode);

读写

在文件打开以后,我们才可对文件进行读写了,Linux中提供文件读写的系统调用是read、write函数:

intread(intfd,constvoid*buf,size_tlength);

intwrite(intfd,constvoid*buf,size_tlength);

其中参数buf为指向缓冲区的指针,length为缓冲区的大小(以字节为单位)。函数read()实现从文件描述符fd所指定的文件中读取length个字节到buf所指向的缓冲区中,返回值为实际读取的字节数。函数write实现将把length个字节从buf指向的缓冲区中写到文件描述符fd所指向的文件中,返回值为实际写入的字节数。

以O_CREAT为标志的open实际上实现了文件创建的功能,因此,下面的函数等同creat()函数:

intopen(pathname,O_CREAT|O_WRONLY|O_TRUNC,mode);

定位

对于随机文件,我们可以随机的指定位置读写,使用如下函数进行定位:

intlseek(intfd,offset_toffset,intwhence);

lseek()将文件读写指针相对whence移动offset个字节。操作成功时,返回文件指针相对于文件头的位置。参数whence可使用下述值:

SEEK_SET:相对文件开头

SEEK_CUR:相对文件读写指针的当前位置

SEEK_END:相对文件末尾

offset可取负值,例如下述调用可将文件指针相对当前位置向前移动5个字节:

lseek(fd,-5,SEEK_CUR);

由于lseek函数的返回值为文件指针相对于文件头的位置,因此下列调用的返回值就是文件的长度:

lseek(fd,0,SEEK_END);

关闭

只要调用close就可以了,其中fd是我们要关闭的文件描述符:

intclose(intfd);

下面我们来编写一个应用程序,在当前目录下创建用户可读写文件"example.txt",在其中写入"HelloWorld",关闭文件,再次打开它,读取其中的内容并输出在屏幕上:

#include

#include

#include

#include

#defineLENGTH100

main()

{

intfd,len;

charstr[LENGTH];

fd=open("hello.txt",O_CREAT|O_RDWR,S_IRUSR|S_IWUSR);/*创建并打开文件*/

if(fd)

{

write(fd,"Hello,SoftwareWeekly",strlen("Hello,softwareweekly"));

/*写入Hello,softwareweekly字符串*/

close(fd);

}

fd=open("hello.txt",O_RDWR);

len=read(fd,str,LENGTH);/*读取文件内容*/

str[len]='';

printf("%sn",str);

close(fd);

}

2.进程控制/通信编程进程控制中主要涉及到进程的创建、睡眠和退出等,在Linux中主要提供了fork、exec、clone的进程创建方法,sleep的进程睡眠和exit的进程退出调用,另外Linux还提供了父进程等待子进程结束的系统调用wait。

fork

对于没有接触过Unix/Linux操作系统的人来说,fork是最难理解的概念之一,因为它执行一次却返回两个值,以前"闻所未闻"。先看下面的程序:

intmain()

{

inti;

if(fork()==0)

{

for(i=1;i<3;i++)

printf("Thisischildprocessn");

}

else

{

for(i=1;i<3;i++)

printf("Thisisparentprocessn");

}

}

[!--empirenews.page--]

执行结果为:

Thisischildprocess

Thisischildprocess

Thisisparentprocess

Thisisparentprocess

fork在英文中是"分叉"的意思,一个进程在运行中,如果使用了fork,就产生了另一个进程,于是进程就"分叉"了。当前进程为父进程,通过fork()会产生一个子进程。对于父进程,fork函数返回子程序的进程号而对于子程序,fork函数则返回零,这就是一个函数返回两次的本质。

exec

在Linux中可使用exec函数族,包含多个函数(execl、execlp、execle、execv、execve和execvp),被用于启动一个指定路径和文件名的进程。exec函数族的特点体现在:某进程一旦调用了exec类函数,正在执行的程序就被干掉了,系统把代码段替换成新的程序(由exec类函数执行)的代码,并且原有的数据段和堆栈段也被废弃,新的数据段与堆栈段被分配,但是进程号却被保留。也就是说,exec执行的结果为:系统认为正在执行的还是原先的进程,但是进程对应的程序被替换了。

fork函数可以创建一个子进程而当前进程不死,如果我们在fork的子进程中调用exec函数族就可以实现既让父进程的代码执行又启动一个新的指定进程,这很好。fork和exec的搭配巧妙地解决了程序启动另一程序的执行但自己仍继续运行的问题,请看下面的例子:

charcommand[MAX_CMD_LEN];

voidmain()

{

intrtn;/*子进程的返回数值*/

while(1)

{

/*从终端读取要执行的命令*/

printf(">");

fgets(command,MAX_CMD_LEN,stdin);

command[strlen(command)-1]=0;

if(fork()==0)

{

/*子进程执行此命令*/

execlp(command,command);

/*如果exec函数返回,表明没有正常执行命令,打印错误信息*/

perror(command);

exit(errorno);

}

else

{

/*父进程,等待子进程结束,并打印子进程的返回值*/

wait(&rtn);

printf("childprocessreturn%dn",rtn);

}

}

}

这个函数实现了一个shell的功能,它读取用户输入的进程名和参数,并启动对应的进程。

clone

clone是Linux2.0以后才具备的新功能,它较fork更强(可认为fork是clone要实现的一部分),可以使得创建的子进程共享父进程的资源,并且要使用此函数必须在编译内核时设置clone_actually_works_ok选项。

clone函数的原型为:

intclone(int(*fn)(void*),void*child_stack,intflags,void*arg);

此函数返回创建进程的PID,函数中的flags标志用于设置创建子进程时的相关选项。

来看下面的例子:

intvariable,fd;

intdo_something(){

variable=42;

close(fd);

_exit(0);

}

intmain(intargc,char*argv[]){

void**child_stack;

chartempch;

variable=9;

fd=open("test.file",O_RDONLY);

child_stack=(void**)malloc(16384);

printf("Thevariablewas%dn",variable);

clone(do_something,child_stack,CLONE_VM|CLONE_FILES,NULL);

sleep(1);/*延时以便子进程完成关闭文件操作、修改变量*/

printf("Thevariableisnow%dn",variable);

if(read(fd,&tempch,1)<1){

perror("FileReadError");

exit(1);

}

printf("Wecouldreadfromthefilen");

return0;

}

运行输出:

Thevariableisnow42

FileReadError

程序的输出结果告诉我们,子进程将文件关闭并将变量修改(调用clone时用到的CLONE_VM、CLONE_FILES标志将使得变量和文件描述符表被共享),父进程随即就感觉到了,这就是clone的特点。

sleep

函数调用sleep可以用来使进程挂起指定的秒数,该函数的原型为:

unsignedintsleep(unsignedintseconds);

该函数调用使得进程挂起一个指定的时间,如果指定挂起的时间到了,该调用返回0;如果该函数调用被信号所打断,则返回剩余挂起的时间数(指定的时间减去已经挂起的时间)。

exit

系统调用exit的功能是终止本进程,其函数原型为:

void_exit(intstatus);

_exit会立即终止发出调用的进程,所有属于该进程的文件描述符都关闭。参数status作为退出的状态值返回父进程,在父进程中通过系统调用wait可获得此值。

wait

wait系统调用包括:

pid_twait(int*status);

pid_twaitpid(pid_tpid,int*status,intoptions);

wait的作用为发出调用的进程只要有子进程,就睡眠到它们中的一个终止为止;waitpid等待由参数pid指定的子进程退出。

Linux的进程间通信(IPC,InterProcessCommunication)通信方法有管道、消息队列、共享内存、信号量、套接口等。套接字通信并不为Linux所专有,在所有提供了TCP/IP协议栈的操作系统中几乎都提供了socket,而所有这样操作系统,对套接字的编程方法几乎是完全一样的。管道分为有名管道和无名管道,无名管道只能用于亲属进程之间的通信,而有名管道则可用于无亲属关系的进程之间;消息队列用于运行于同一台机器上的进程间通信,与管道相似;共享内存通常由一个进程创建,其余进程对这块内存区进行读写;信号量是一个计数器,它用来记录对某个资源(如共享内存)的存取状况。

下面是一个使用信号量的例子,该程序创建一个特定的IPC结构的关键字和一个信号量,建立此信号量的索引,修改索引指向的信号量的值,最后清除信号量:

#include

#include

#include

#include

[!--empirenews.page--]

voidmain()

{

key_tunique_key;/*定义一个IPC关键字*/

intid;

structsembuflock_it;

unionsemunoptions;

inti;

unique_key=ftok(".",'a');/*生成关键字,字符'a'是一个随机种子*/

/*创建一个新的信号量集合*/

id=semget(unique_key,1,IPC_CREAT|IPC_EXCL|0666);

printf("semaphoreid=%dn",id);

options.val=1;/*设置变量值*/

semctl(id,0,SETVAL,options);/*设置索引0的信号量*/

/*打印出信号量的值*/

i=semctl(id,0,GETVAL,0);

printf("valueofsemaphoreatindex0is%dn",i);

/*下面重新设置信号量*/

lock_it.sem_num=0;/*设置哪个信号量*/

lock_it.sem_op=-1;/*定义操作*/

lock_it.sem_flg=IPC_NOWAIT;/*操作方式*/

if(semop(id,&lock_it,1)==-1)

{

printf("cannotlocksemaphore.n");

exit(1);

}

i=semctl(id,0,GETVAL,0);

printf("valueofsemaphoreatindex0is%dn",i);

/*清除信号量*/

semctl(id,0,IPC_RMID,0);

}

2.进程控制/通信编程进程控制中主要涉及到进程的创建、睡眠和退出等,在Linux中主要提供了fork、exec、clone的进程创建方法,sleep的进程睡眠和exit的进程退出调用,另外Linux还提供了父进程等待子进程结束的系统调用wait。

fork

对于没有接触过Unix/Linux操作系统的人来说,fork是最难理解的概念之一,因为它执行一次却返回两个值,以前"闻所未闻"。先看下面的程序:

intmain()

{

inti;

if(fork()==0)

{

for(i=1;i<3;i++)

printf("Thisischildprocessn");

}

else

{

for(i=1;i<3;i++)

printf("Thisisparentprocessn");

}

}

执行结果为:

Thisischildprocess

Thisischildprocess

Thisisparentprocess

Thisisparentprocess

fork在英文中是"分叉"的意思,一个进程在运行中,如果使用了fork,就产生了另一个进程,于是进程就"分叉"了。当前进程为父进程,通过fork()会产生一个子进程。对于父进程,fork函数返回子程序的进程号而对于子程序,fork函数则返回零,这就是一个函数返回两次的本质。

exec

在Linux中可使用exec函数族,包含多个函数(execl、execlp、execle、execv、execve和execvp),被用于启动一个指定路径和文件名的进程。exec函数族的特点体现在:某进程一旦调用了exec类函数,正在执行的程序就被干掉了,系统把代码段替换成新的程序(由exec类函数执行)的代码,并且原有的数据段和堆栈段也被废弃,新的数据段与堆栈段被分配,但是进程号却被保留。也就是说,exec执行的结果为:系统认为正在执行的还是原先的进程,但是进程对应的程序被替换了。

fork函数可以创建一个子进程而当前进程不死,如果我们在fork的子进程中调用exec函数族就可以实现既让父进程的代码执行又启动一个新的指定进程,这很好。fork和exec的搭配巧妙地解决了程序启动另一程序的执行但自己仍继续运行的问题,请看下面的例子:

charcommand[MAX_CMD_LEN];

voidmain()

{

intrtn;/*子进程的返回数值*/

while(1)

{

/*从终端读取要执行的命令*/

printf(">");

fgets(command,MAX_CMD_LEN,stdin);

command[strlen(command)-1]=0;

if(fork()==0)

{

/*子进程执行此命令*/

execlp(command,command);

/*如果exec函数返回,表明没有正常执行命令,打印错误信息*/

perror(command);

exit(errorno);

}

else

{

/*父进程,等待子进程结束,并打印子进程的返回值*/

wait(&rtn);

printf("childprocessreturn%dn",rtn);

}

}

}

这个函数实现了一个shell的功能,它读取用户输入的进程名和参数,并启动对应的进程。

clone

clone是Linux2.0以后才具备的新功能,它较fork更强(可认为fork是clone要实现的一部分),可以使得创建的子进程共享父进程的资源,并且要使用此函数必须在编译内核时设置clone_actually_works_ok选项。

clone函数的原型为:

intclone(int(*fn)(void*),void*child_stack,intflags,void*arg);

此函数返回创建进程的PID,函数中的flags标志用于设置创建子进程时的相关选项。

来看下面的例子:

intvariable,fd;

intdo_something(){

variable=42;

close(fd);

_exit(0);

}

intmain(intargc,char*argv[]){

void**child_stack;

chartempch;

variable=9;

fd=open("test.file",O_RDONLY);

child_stack=(void**)malloc(16384);

printf("Thevariablewas%dn",variable);

clone(do_something,child_stack,CLONE_VM|CLONE_FILES,NULL);

sleep(1);/*延时以便子进程完成关闭文件操作、修改变量*/

printf("Thevariableisnow%dn",variable);

if(read(fd,&tempch,1)<1){

perror("FileReadError");

exit(1);

}

printf("Wecouldreadfromthefilen");

return0;

}

运行输出:

Thevariableisnow42

FileReadError

程序的输出结果告诉我们,子进程将文件关闭并将变量修改(调用clone时用到的CLONE_VM、CLONE_FILES标志将使得变量和文件描述符表被共享),父进程随即就感觉到了,这就是clone的特点。[!--empirenews.page--]

sleep

函数调用sleep可以用来使进程挂起指定的秒数,该函数的原型为:

unsignedintsleep(unsignedintseconds);

该函数调用使得进程挂起一个指定的时间,如果指定挂起的时间到了,该调用返回0;如果该函数调用被信号所打断,则返回剩余挂起的时间数(指定的时间减去已经挂起的时间)。

exit

系统调用exit的功能是终止本进程,其函数原型为:

void_exit(intstatus);

_exit会立即终止发出调用的进程,所有属于该进程的文件描述符都关闭。参数status作为退出的状态值返回父进程,在父进程中通过系统调用wait可获得此值。

wait

wait系统调用包括:

pid_twait(int*status);

pid_twaitpid(pid_tpid,int*status,intoptions);

wait的作用为发出调用的进程只要有子进程,就睡眠到它们中的一个终止为止;waitpid等待由参数pid指定的子进程退出。

Linux的进程间通信(IPC,InterProcessCommunication)通信方法有管道、消息队列、共享内存、信号量、套接口等。套接字通信并不为Linux所专有,在所有提供了TCP/IP协议栈的操作系统中几乎都提供了socket,而所有这样操作系统,对套接字的编程方法几乎是完全一样的。管道分为有名管道和无名管道,无名管道只能用于亲属进程之间的通信,而有名管道则可用于无亲属关系的进程之间;消息队列用于运行于同一台机器上的进程间通信,与管道相似;共享内存通常由一个进程创建,其余进程对这块内存区进行读写;信号量是一个计数器,它用来记录对某个资源(如共享内存)的存取状况。

下面是一个使用信号量的例子,该程序创建一个特定的IPC结构的关键字和一个信号量,建立此信号量的索引,修改索引指向的信号量的值,最后清除信号量:

#include

#include

#include

#include

voidmain()

{

key_tunique_key;/*定义一个IPC关键字*/

intid;

structsembuflock_it;

unionsemunoptions;

inti;

unique_key=ftok(".",'a');/*生成关键字,字符'a'是一个随机种子*/

/*创建一个新的信号量集合*/

id=semget(unique_key,1,IPC_CREAT|IPC_EXCL|0666);

printf("semaphoreid=%dn",id);

options.val=1;/*设置变量值*/

semctl(id,0,SETVAL,options);/*设置索引0的信号量*/

/*打印出信号量的值*/

i=semctl(id,0,GETVAL,0);

printf("valueofsemaphoreatindex0is%dn",i);

/*下面重新设置信号量*/

lock_it.sem_num=0;/*设置哪个信号量*/

lock_it.sem_op=-1;/*定义操作*/

lock_it.sem_flg=IPC_NOWAIT;/*操作方式*/

if(semop(id,&lock_it,1)==-1)

{

printf("cannotlocksemaphore.n");

exit(1);

}

i=semctl(id,0,GETVAL,0);

printf("valueofsemaphoreatindex0is%dn",i);

/*清除信号量*/

semctl(id,0,IPC_RMID,0);

}

3.线程控制/通信编程Linux本身只有进程的概念,而其所谓的"线程"本质上在内核里仍然是进程。大家知道,进程是资源分配的单位,同一进程中的多个线程共享该进程的资源(如作为共享内存的全局变量)。Linux中所谓的"线程"只是在被创建的时候"克隆"(clone)了父进程的资源,因此,clone出来的进程表现为"线程"。Linux中最流行的线程机制为LinuxThreads,它实现了一种Posix1003.1c"pthread"标准接口。

线程之间的通信涉及同步和互斥,互斥体的用法为:

pthread_mutex_tmutex;

pthread_mutex_init(&mutex,NULL);//按缺省的属性初始化互斥体变量mutex

pthread_mutex_lock(&mutex);//给互斥体变量加锁

…//临界资源

phtread_mutex_unlock(&mutex);//给互斥体变量解锁

同步就是线程等待某个事件的发生。只有当等待的事件发生线程才继续执行,否则线程挂起并放弃处理器。当多个线程协作时,相互作用的任务必须在一定的条件下同步。Linux下的C语言编程有多种线程同步机制,最典型的是条件变量(conditionvariable)。而在头文件semaphore.h中定义的信号量则完成了互斥体和条件变量的封装,按照多线程程序设计中访问控制机制,控制对资源的同步访问,提供程序设计人员更方便的调用接口。下面的生产者/消费者问题说明了Linux线程的控制和通信:

#include

#include

#defineBUFFER_SIZE16

structprodcons

{

intbuffer[BUFFER_SIZE];

pthread_mutex_tlock;

intreadpos,writepos;

pthread_cond_tnotempty;

pthread_cond_tnotfull;

};

/*初始化缓冲区结构*/

voidinit(structprodcons*b)

{

pthread_mutex_init(&b->lock,NULL);

pthread_cond_init(&b->notempty,NULL);

pthread_cond_init(&b->notfull,NULL);

b->readpos=0;

b->writepos=0;

}

/*将产品放入缓冲区,这里是存入一个整数*/

voidput(structprodcons*b,intdata)

{

pthread_mutex_lock(&b->lock);

/*等待缓冲区未满*/

if((b->writepos+1)%BUFFER_SIZE==b->readpos)

{

pthread_cond_wait(&b->notfull,&b->lock);

}

/*写数据,并移动指针*/

b->buffer[b->writepos]=data;[!--empirenews.page--]

b->writepos++;

if(b->writepos>=BUFFER_SIZE)

b->writepos=0;

/*设置缓冲区非空的条件变量*/

pthread_cond_signal(&b->notempty);

pthread_mutex_unlock(&b->lock);

}

/*从缓冲区中取出整数*/

intget(structprodcons*b)

{

intdata;

pthread_mutex_lock(&b->lock);

/*等待缓冲区非空*/

if(b->writepos==b->readpos)

{

pthread_cond_wait(&b->notempty,&b->lock);

}

/*读数据,移动读指针*/

data=b->buffer[b->readpos];

b->readpos++;

if(b->readpos>=BUFFER_SIZE)

b->readpos=0;

/*设置缓冲区未满的条件变量*/

pthread_cond_signal(&b->notfull);

pthread_mutex_unlock(&b->lock);

returndata;

}

/*测试:生产者线程将1到10000的整数送入缓冲区,消费者线

程从缓冲区中获取整数,两者都打印信息*/

#defineOVER(-1)

structprodconsbuffer;

void*producer(void*data)

{

intn;

for(n=0;n<10000;n++)

{

printf("%d--->n",n);

put(&buffer,n);

}put(&buffer,OVER);

returnNULL;

}

void*consumer(void*data)

{

intd;

while(1)

{

d=get(&buffer);

if(d==OVER)

break;

printf("--->%dn",d);

}

returnNULL;

}

intmain(void)

{

pthread_tth_a,th_b;

void*retval;

init(&buffer);

/*创建生产者和消费者线程*/

pthread_create(&th_a,NULL,producer,0);

pthread_create(&th_b,NULL,consumer,0);

/*等待两个线程结束*/

pthread_join(th_a,&retval);

pthread_join(th_b,&retval);

return0;

}

4.小结

本章主要给出了Linux平台下文件、进程控制与通信、线程控制与通信的编程实例。至此,一个完整的,涉及硬件原理、Bootloader、操作系统及文件系统移植、驱动程序开发及应用程序编写的嵌入式Linux系列讲解就全部结束了。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

据《日经新闻》报道,软银旗下英国芯片制造商Arm计划到2025年销售AI芯片。该公司专门成立一个AI芯片部门,并将在2025年春季之前制造出原型产品,并于秋季开始大规模生产。

关键字: 软银 AGI ARM AI芯片

业内消息,近日日本软件银行集团(SoftBank Group)旗下安谋国际科技公司(Arm)计划研发人工智能(AI)芯片,先成立一个AI芯片部门,目标是明年春季建立AI芯片原型产品,然后将量产工作交由代工厂制造,预估20...

关键字: ARM AI芯片

Arm CPU正在从根本上推动AI变革,并造福地球。Arm架构是未来AI计算的基石。​

关键字: ARM AI

近日,Arm推出了Arm® Ethos™-U85神经网络处理器(NPU)和Arm Corstone™-320物联网参考设计平台,旨在满足海量的数据处理和大规模计算,加速推进边缘AI的发展进程。

关键字: ARM

为了赶超云计算市场上的竞争对手,谷歌正试图通过定制的Arm服务器芯片降低云计算服务成本。

关键字: 谷歌 ARM 定制芯片

嵌入式开发作为一个融合了计算机软硬件和系统工程的综合性领域,其成功与否往往取决于三个核心要素的有效整合与协调。这三个要素分别是:硬件平台的选择与设计、软件开发及其优化、以及系统级的设计与集成。深入理解并熟练掌握这三个方面...

关键字: 嵌入式开发 ARM

随着汽车软件数量爆发式的增长,整个行业都需要重新思考汽车产品的开发流程。为此,Arm推出了丰富的硬件IP、新的系统IP,以及全新的汽车计算与计算子系统产品路线图,旨在为各种汽车应用实现性能、功能安全、可扩展等方面的支持。

关键字: ARM 汽车电子

知名移动芯片设计公司ARM最近迈出重要一步,它正式推出汽车芯片设计。ARM推出的芯片设计方案名叫Neoverse,随同芯片一起推出的还有面向汽车制造商、汽车供应商的新系统。

关键字: ARM 汽车芯片 芯片

随着通用人工智能的发展,数据中心的计算需求逐步提高。针对多模态数据、大模型的推理和训练需要更高的算力支持,而随着算力提升与之而来的还需更关注在功耗方面的优化。对于头部云计算和服务厂商而言,针对专门用例提高每瓦性能变得至关...

关键字: ARM 服务器 AI Neoverse CSS

一直以来,riscv架构都是大家的关注焦点之一。因此针对大家的兴趣点所在,小编将为大家带来riscv架构的相关介绍,详细内容请看下文。

关键字: riscv ARM riscv架构
关闭
关闭