




已阅读5页,还剩23页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第7章 嵌入式Linux操作系统实验7.2 Linux应用程序编写实验7.2.1 实验目的1. 熟悉JXARM9-2410教学系统中的Linux开发环境;2. 掌握简单的Linux应用程序helloworld的编译;3. 掌握JXARM9-2410教学系统中Linux应用程序的调试。7.2.2 实验内容1. 编写helloworld应用程序;2. 编写Makefile文件;3. 编译helloworld应用程序;4. 下载并调试helloworld应用程序。7.2.3 预备知识1. C语言的基础知识;2. 程序调试的基础知识和方法;3. Linux的基本操作。7.2.4 实验设备1. 硬件:JXARM9-2410嵌入式实验箱、PC机Pentumn500以上,硬盘10G以上;2. 软件:PC机操作系统 redhat linux 9.0 Linux开发环境。7.2.5 基础知识helloworld程序是一个只在输出控制台(计算机屏幕或者串口控制台)上打印出“Hello, World!”(英语,意为“你好,世界!”)字串的程序。该程序通常是计算机程序设计语言的初学者所要学习编写的第一个程序。它还可以用来确定该语言的编译器、程序开发环境以及运行环境已经正确安装。本实验也将helloworld程序作为第一个学写的程序,并通过实际的动作让学生了解嵌入式Linux应用程序开发和PC机中Linux应用程序开发的异同。1. 交叉编译通常,程序是在一台计算机上编译,然后再分布到将要使用的其他计算机上。当主机系统(运行编译器的系统)和目标系统(产生的程序将在其上运行的系统)不兼容时,该过程就叫做交叉编译。除了兼容性这个明显的好处之外,交叉编译还由于以下两个原因而非常重要:1、当目标系统对其可用的编译工具没有本地设置时;2、当主机系统比目标系统要快得多,或者具有多得多的可用资源时。本实验所使用的开发系统是x86体系结构的Linux系统(RedHat)。而我们的目的是要开发能够运行在JXARM9-2410教学实验箱中的Linux应用程序。由于JXARM9-2410教学实验箱中的Linux本身不具有自己的编译工具,因此我们必须在RedHat中进行交叉编译,编译完成后将执行码下载到JXARM9-2410教学实验箱中的Linux,然后运行或者调试。这样做的另外一个好处是,在采用RedHat的主机系统通常其CPU速度、接口等软硬件资源都比JXARM9-2410教学实验箱中的Linux要丰富得多,因此在其上进行交叉编译效率要高得多。在同一平台编译能够运行在不同平台上运行的程序的最主要差别在于所采用的编译器不同。在Redhat中编译x86平台的采用gcc编译器,而编译ARM平台的采用arm-elf-gcc或者arm-linux-gcc编译器。在本实验箱中,所有Linux实验均采用arm-linux-gcc编译器编译。2. helloworld的编译helloworld可以说是最简单的应用程序,通过如下命令进行编译:gcc -o helloworld helloworld.c其中-o指定输出文件到helloworld,helloworld.c为编译的源文件。该命令执行后,将对helloworld.c文件进行编译,并将生成helloworld可执行文件。这个文件就是在指定平台上可以运行的执行程序,如果使用gcc进行编译即为可在x86平台上运行的程序,如果使用arm-linux-gcc进行编译则为可以在ARM平台上运行的程序。3. Makefile文件Makefile文件的作用有点类似于DOS下的批处理文件,通过编写Makefile文件,用户可以将一个很复杂的程序(可能包含上百个甚至更多的源文件或者目录)通过简单的make命令进行编译。7.2.6 实验步骤1. 建立工作目录注:本实验以及后续的所有实验中用“$”符号表示在Linux控制台上输入的命令行。$cd /home/cvtech/jx2410/examples$mkdir helloworld$cd helloworld2. 编写程序源代码在Linux下的文本编辑器有许多,常用的是vim, Xwindow界面下的gedit等,我们在开发过程中推荐使用vi,用户需要学习vi的操作方法,请参考附录中的关于vi的操作指南。实际的源代码较简单,如下:#include int main() printf(Hello, World!n);3. 编译并运行x86平台的helloworld程序$gcc -o helloworld helloworld.c$./helloworld正确的结果将在主机的显示器上打印如下字符串:Hello, World!4. 编译并运行ARM平台的helloworld程序$arm-linux-gcc -o helloworld helloworld.c由于编译器采用的是arm-linux-gcc编译器,因此使用上述命令编译出来的程序只能在ARM处理器上运行,不能在x86平台下运行,如果在RedHat中运行该程序将出现如下错误结果。$./helloworldbash: ./helloworld: cannot execute binary file5. 下载helloworld程序到JXARM9-2410中调试通过ftp或者nfs将第四步编译的程序helloworld下载到JXARM9-2410的Linux的/bin目录下,JXARM9-2410中Linux应用程序的下载和调试方法请参见用户手册。下载完成后,可以使用ls命令查看该文件是否存在,如果存在,然后在控制台(MiniCom)输入如下命令:$cd /bin$chmod 777 helloworld$./helloworld正确的结果将在MiniCom上打印如下字符串:Hello, World!6. 编写Makefile文件使用vi编辑工具编辑Makefile,请注意文件名的M必须大写,其余为小写,如下所示,。注意其中每行前面的空格位置必须使用Tab键。CC = arm-linux-gccLD = arm-linux-ldEXEC = helloworldOBJS = helloworld.oCFLAGS +=LDFLAGS += all: $(EXEC)$(EXEC): $(OBJS) $(CC) $(LDFLAGS) -o $ $(OBJS) $(LDLIBS$(LDLIBS_$)clean: -rm -f $(EXEC) *.elf *.gdb *.o上述为一个典型的Makefile脚本文件的格式。下面简单介绍一下各个部分的含义:1) 所采用的编译器和链接器CC = arm-linux-gccLD = arm-linux-ld2) 生成的执行文件和链接过程中的目标文件EXEC = helloworldOBJS = helloworld.o3) 编译和链接的参数$(EXEC): $(OBJS)CFLAGS +=LDFLAGS += 4) 编译命令,执行完成将生成helloworld映像文件 $(CC) $(LDFLAGS) -o $ $(OBJS) $(LDLIBS$(LDLIBS_$)5) 清除clean: -rm -f $(EXEC) *.elf *.gdb *.o $(OBJS):6)使用make进行编译使用如下命令编译ARM平台的helloworld程序。$make clean$makearm-linux-gcc -c -o helloworld.o helloworld.carm-linux-gcc -o helloworld helloworld.o使用如下命令编译x86平台的helloworld程序。$make clean$make CC=gccgcc -c -o helloworld.o helloworld.cgcc -o helloworld helloworld.o分别参照步骤3和步骤5运行两种不同版本的程序,将得到相同的结果。7.2.7 实验报告要求1. 简述交叉编译的基本概念,简述x86平台和ARM平台编译环境的异同;2. 简述Makefile文件的作用和基本组成;3. JXARM9-2410中怎样将编写的应用程序下载到Linux中,怎样在Linux中运行该程序?7.3 Linux驱动程序编写实验7.3.1 实验目的1. 掌握Linux驱动程序leddrv的编程;2. 掌握Linux应用程序加载驱动程序的方法;3. 掌握Linux动态加载驱动程序模块的方法。7.3.2 实验内容1. 编写leddrv驱动程序;2. 编写Makefile文件;3. 编写ledtest应用程序;4. 编译leddrv和ledtest应用程序;5. 下载并调试leddrv和ledtest应用程序。7.3.3预备知识1. C语言的基础知识;2. 软件调试的基础知识和方法;3. Linux的基本操作;4. Linux应用程序的编写。7.3.4 实验设备1. 硬件:JXARM9-2410嵌入式实验箱、PC机Pentumn500以上,硬盘10G以上;2. 软件:PC机操作系统 redhat linux 9.0 Linux开发环境。7.3.5 基础知识1. Linux驱动程序Linux是Unix操作系统的一种变种,在Linux下编写驱动程序的原理和思想完全类似于其他的Unix系统,但它dos或window环境下的驱动程序有很大的区别。在Linux环境下设计驱动程序,思想简洁,操作方便,功能也很强大。系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以操作普通文件一样对硬件设备进行操作。设备驱动程序是内核的一部分,它完成以下的功能:1) 对设备初始化和释放;2) 把数据从内核传送到硬件和从硬件读取数据;3) 读取应用程序传送给设备文件的数据和回送应用程序请求的数据;4) 检测和处理设备出现的错误。在Linux操作系统下有三类主要的设备文件类型,字符设备、块设备和网络设备。字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了,块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求能满足用户的要求,就返回请求的数据,如果不能,就调用请求函数来进行实际的I/O操作。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待。用户进程是通过设备文件来与实际的硬件打交道.每个设备文件都都有其文件属性(c/b),表示是字符设备还是块设备,另外每个文件都有两个设备号,第一个是主设备号,标识驱动程序,第二个是从设备号,标识使用同一个设备驱动程序的不同的硬件设备,比如有两个软盘,就可以用从设备号来区分他们。设备文件的的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序。2. 编写简单的驱动程序本实验将编写一个简单的字符设备驱动程序。虽然它的功能很简单,但是通过它可以了解Linux的设备驱动程序的工作原理。该程序leddrv实现对JXARM9-2410中的跑马灯进行控制。它主要包含如下几个部分:1) 包含文件#include #include #include #include /* for -EBUSY */#include /* for verify_area */#include /* for module_init */#include /* for get_user and put_user */#include #include leddrv.h2) 模块初始化由于用户进程是通过设备文件同硬件打交道,对设备文件的操作方式不外乎就是一些系统调用,如open,read,write,close.,注意,不是fopen,fread,但是如何把系统调用和驱动程序关联起来呢?这需要了解一个非常关键的数据结构: struct file_operations int (*seek) (struct inode * ,struct file *, off_t ,int); int (*read) (struct inode * ,struct file *, char ,int); int (*write) (struct inode * ,struct file *, off_t ,int); int (*readdir) (struct inode * ,struct file *, struct dirent * ,int); int (*select) (struct inode * ,struct file *, int ,select_table *); int (*ioctl) (struct inode * ,struct file *, unsined int ,unsigned long); int (*mmap) (struct inode * ,struct file *, struct vm_area_struct *); int (*open) (struct inode * ,struct file *); int (*release) (struct inode * ,struct file *); int (*fsync) (struct inode * ,struct file *); int (*fasync) (struct inode * ,struct file *,int); int (*check_media_change) (struct inode * ,struct file *); int (*revalidate) (dev_t dev); 这个结构的每一个成员的名字都对应着一个系统调用。用户进程利用系统调用在对设备文件进行诸如read/write操作时,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数。这是linux的设备驱动程序工作的基本原理。既然是这样,则编写设备驱动程序的主要工作就是编写子函数,并填充file_operations的各个域。如下所示,leddrv实现了read、write、open、release和ioctl的文件操作,其处理函数分别为device_read、device_write、device_open、device_release和device_ioctl。并在模块初始化函数led_module_init中调用register_chrdev函数进行注册。struct file_operations Fops = .read= device_read, .write= device_write, .open= device_open, .release= device_release, .ioctl= device_ioctl ;/* Initialize the module - Register the character device */static int _init led_module_init( void ) int ret_val; /* 申请LED显示端口 */if(check_region(LED_BASE, 12) printk(%x is used by another modulen, LED_BASE); return -1;request_region(LED_BASE, 12, DEVICE_NAME); /* 在申请了I/O端口之后,就可以如下几个函数来访问I/O端口: #include inline unsigned int inb(unsigned short port); inline unsigned int inb_p(unsigned short port); inline void outb(char value, unsigned short port); inline void outb_p(char value, unsigned short port); */ /* Register the character device (atleast try) */ ret_val = register_chrdev(MAJOR_NUM, DEVICE_NAME, &Fops); /* Negative values signify an error */ if (ret_val 0) printk (%s failed with %dn, Sorry, registering the character device , ret_val); return ret_val; Led_Port_init (); printk (%s The major device number is %d.n,Registeration is a success, MAJOR_NUM); printk (If you want to talk to the device driver,n); printk (youll have to create a device file. n); printk (We suggest you use:n); printk (mknod %s c %d 0n, DEVICE_FILE_NAME,MAJOR_NUM); printk (The device file name is important, becausen); printk (the ioctl program assumes thats then); printk (file youll use.n); return 0;3) 设备文件操作函数实现open函数实现,当设备文件被打开时调用该函数/* This function is called whenever a process attempts * to open the device file */static int device_open(struct inode *inode, struct file *file) DbgPrintk(Device Openn); /* We dont want to talk to two processes at the * same time */ if (Device_Open) return -EBUSY; Device_Open+; MOD_INC_USE_COUNT; /* Initialize the message */ Message_Ptr = Message; return 0;release函数实现,当设备文件被关闭时调用。static int device_release(struct inode *inode, struct file *file) DbgPrintk(device_releasen); /* Were now ready for our next caller */ Device_Open -; MOD_DEC_USE_COUNT; return 0;read函数实现,读取设备状态到buffer中。当调用read时,device_read ()被调用,它读取跑马灯的状态,并将其放入用户的缓冲区中。buffer是read调用的一个参数,它是用户进程空间的一个地址。但是在device_read被调用时,系统进入核心态。所以不能使用buf这个地址,必须用put_user系统调用向用户传送数据。同样的道理,从用户态缓冲区获取数据使用get_user系统调用,见device_write函数。static ssize_t device_read( struct file *file, char *buffer, /* The buffer to fill with the data */ size_t length, /* The length of the buffer */ loff_t *offset) /* offset to the file */ /* Number of bytes actually written to the buffer */ int bytes_read = 4; int* data = (int*)buffer; DbgPrintk(device_read(%p,%p,%d)n, file, buffer, length); if (data = 0) return 0; put_user(Led_Get(), data); /* Read functions are supposed to return the number * of bytes actually inserted into the buffer */ return bytes_read;write函数实现,设备写操作。static ssize_t device_write(struct file *file, const char *buffer, size_t length, loff_t *offset) int value; int bytes_writes = 4; int* data = (int*)buffer; DbgPrintk(device_write(%p,%s,%d), file, buffer, length); get_user(value, data); Led_Put(value); /* Again, return the number of input characters used */ return bytes_writes;ioctl操函数实现。int device_ioctl( struct inode *inode, struct file *file, unsigned int ioctl_num,/* The number of the ioctl */ unsigned long ioctl_param) /* The parameter to it */ inttemp; int* data; DbgPrintk(Device_ioctln); /* Switch according to the ioctl called */ switch (ioctl_num) case IOCTL_SET_MSG: /* Receive a pointer to a message (in user space) * and set that to be the devices message. */ /* Get the parameter given to ioctl by the process */ data = (int*) ioctl_param; get_user(temp, data); DbgPrintk(Device_ioctl SET_MSG %dn, temp); Led_Put(temp); break; case IOCTL_GET_MSG: temp = Led_Get(); DbgPrintk(Device_ioctl PUT_MSG %dn, temp); put_user(temp, (int *) ioctl_param); break; return 0;4) 模块退出操作模块退出时,必须删除设备驱动程序并释放占用的资源,使用unregister_chrdev删除设备驱动程序。代码如下所示:static void _exit led_module_cleanup(void) int ret; release_region(LED_BASE, 12); /* Unregister the device */ ret = unregister_chrdev(MAJOR_NUM, DEVICE_NAME); /* If theres an error, report it */ if (ret 0) printk(Error in module_unregister_chrdev: %dn, ret);3. 设备驱动程序模块的动态加载在Linux系统中,驱动程序可以采用两种方式加载:1) 可以和内核一起编译,在内核启动时自动加载该驱动;2) 驱动程序模块动态加载方式,使用insmod和rmmod加载和卸载驱动程序模块。本实验使用动态加载方式进行,在用insmod命令将编译好的模块调入内存时,init_module 函数被调用。在用rmmod卸载模块时,cleanup_module函数被调用。在Linux中使用如下命令安装驱动程序:$ insmod leddrv.o 在Linux中使用如下命令安装驱动程序:$ rmmod leddrv 为了正确使用设备驱动程序,必须先创建设备文件:mknod /dev/led c major minor c 是指字符设备,major是主设备号,就是在/proc/devices里看到的。 4. 设备驱动程序中的一些具体问题1) I/O Port. 和硬件打交道离不开I/O Port,老的ISA设备经常是占用实际的I/O端口,在linux下,操作系统没有对I/O口屏蔽,也就是说,任何驱动程序都可对任意的I/O口操作,这样就很容易引起混乱。每个驱动程序应该自己避免误用端口。有两个重要的内核函数可以保证驱动程序做到这一点。check_region(int io_port, int off_set) 这个函数察看系统的I/O表,看是否有别的驱动程序占用某一段I/O口。 参数1:io端口的基地址, 参数2:io端口占用的范围。 返回值:0 没有占用, 非0,已经被占用。 request_region(int io_port, int off_set,char *devname) 如果这段I/O端口没有被占用,在我们的驱动程序中就可以使用它。在使用之前,必须向系统登记,以防止被其他程序占用。登记后,在/proc/ioports文件中可以看到你登记的io口。参数1:io端口的基地址。 参数2:io端口占用的范围。 参数3:使用这段io地址的设备名。 在对I/O口登记后,就可以放心地用inb(), outb()之类的函来访问了。 在一些pci设备中,I/O端口被映射到一段内存中去,要访问这些端口就相当于访问一段内存。经常性的,我们要获得一块内存的物理地址。在dos环境下,(之所以不说是dos操作系统是因为我认为DOS根本就不是一个操作系统,它实在是太简单,太不安全了)只要用段:偏移就可以了。在window95中,95ddk提供了一个vmm 调用 _MapLinearToPhys,用以把线性地址转化为物理地址。但在Linux中是怎样做的呢?2) 内存操作在设备驱动程序中动态开辟内存,不是用malloc,而是kmalloc,或者用get_free_pages直接申请页。释放内存用的是kfree,或free_pages. 请注意,kmalloc等函数返回的是物理地址!而malloc等返回的是线性地址!关于kmalloc返回的是物理地址这一点本人有点不太明白:既然从线性地址到物理地址的转换是由386cpu硬件完成的,那样汇编指令的操作数应该是线性地址,驱动程序同样也不能直接使用物理地址而是线性地址。但是事实上kmalloc返回的确实是物理地址,而且也可以直接通过它访问实际的RAM,我想这样可以由两种解释,一种是在核心态禁止分页,但是这好像不太现实;另一种是linux的页目录和页表项设计得正好使得物理地址等同于线性地址。我的想法不知对不对,还请高手指教。 言归正传,要注意kmalloc最大只能开辟128k-16,16个字节是被页描述符结构占用了。kmalloc用法参见khg。内存映射的I/O口,寄存器或者是硬件设备的RAM(如显存)一般占用F0000000以上的地址空间。在驱动程序中不能直接访问,要通过kernel函数vremap获得重新映射以后的地址。另外,很多硬件需要一块比较大的连续内存用作DMA传送。这块内存需要一直驻留在内存,不能被交换到文件中去。但是kmalloc最多只能开辟128k的内存。这可以通过牺牲一些系统内存的方法来解决。具体做法是:比如说你的机器由32M的内存,在lilo.conf的启动参数中加上mem=30M,这样linux就认为你的机器只有30M的内存,剩下的2M内存在vremap之后就可以为DMA所用了。请记住,用vremap映射后的内存,不用时应用unremap释放,否则会浪费页表。3) 中断处理同处理I/O端口一样,要使用一个中断,必须先向系统登记。int request_irq(unsigned int irq , void(*handle)(int,void *,struct pt_regs *), unsigned int long flags, const char *device); irq: 是要申请的中断。 handle:中断处理函数指针。 flags:SA_INTERRUPT 请求一个快速中断,0 正常中断。 device:设备名。如果登记成功,返回0,这时在/proc/interrupts文件中可以看你请求的中断。7.3.6 实验步骤1. 建立工作目录$cd /home/cvtech/jx2410/examples$mkdir leddrv$cd leddrv2. 编写leddrv驱动程序源代码由系统安装目录的examples下的leddrv目录下拷贝leddrv.c、ledtest.c和Makefile三个文件到leddrv目录下,leddrv.c为驱动程序源代码,ledtest.c为应用程序源代码。参照上一节,阅读并理解这两个文件。3. 编译leddrv驱动程序$make clean$make如果正确将出现leddrv.o文件,这个文件就是leddrv驱动模块文件。4. 编译ledtest应用程序$arm-linux-gcc -o ledtest ledtest.c如果正确将生成ledtest文件,这个文件就是测试leddrv的应用程序。5. 下载leddrv.o和ledtest两个文件到到JXARM9-2410中通过ftp或者nfs将第三步和第四步编译的程序leddrv.o和ledtest下载到JXARM9-2410的Linux的/bin目录下。下载完成后,可以使用ls命令查看该文件是否存在,如果存在,然后在控制台(MiniCom)输入如下命令安装驱动程序leddrv:$cd /bin$insmod leddrv.oRegisteration is a success The major device number is 100.If you want to talk to the device driver,youll have to create a device file. We suggest you use:mknod led_drv c 100 0The device file name is important, becausethe ioctl program assumes thats thefile youll use.然后运行ledtest测试程序:$./ledtestCant open出现错误,这是由于没有注册设备文件造成的,使用mknod命令注册设备文件,设备名为led,字符设备,主设备号为100,从设备号可以随意取。$mknod /dev/led c 100 0$./ledtestDevice Openopen OK 3出现正确的结果,此时可以通过输入数字键对跑马灯进行控制。7.3.7 实验报告要求1. 简述Linux设备驱动程序的基本概念和编写方法;2. 简述Linux应用程序怎样访问设备驱动程序;7.4 Linux多线程应用程序设计实验7.4.1 实验目的1. 了解Linux下多线程程序设计的基本原理;2. 学习ptread库函数的使用。7.4.2 实验内容1. 编写thread应用程序;2. 编写Makefile文件;3. 下载并调试thread应用程序。7.4.3 预备知识1. C语言的基础知识;2. 程序调试的基础知识和方法;3. Linux的基本操作;4. 掌握Linux下的程序编译与交叉编译过程。7.4.4 实验设备1. 硬件:JXARM9-2410嵌入式实验箱、PC机Pentumn500以上,硬盘10G以上。2. 软件:PC机操作系统 redhat linux 9.0 Linux开发环境7.4.5 基础知识1. 多线程在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种昂贵的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。另外线程间具有非常方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。2. 多线程程序设计Linux系统下的多线程遵循POSIX线程接口,称为pthread。编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a。顺便说一下,Linux下pthread的实现是通过系统调用clone()来实现的。Clone()是Linux所特有的系统调用,它的使用方式类似fork。下面是主要的多线程API函数:1、pthread_create函数pthread_create用来创建一个线程,它的原型为:extern int pthread_create _P (pthread_t *_thread, _const pthread_attr_t *_attr,void *(*_start_routine) (void *), void *_arg);第一个参数为一个pthread_t类型的指向线程标识符的指针,pthread_t定义如下:typedef unsigned long int pthread_t;第二个参数用来设置线程属性。第三个参数是线程运行函数的起始地址最后一个参数是运行函数的参数。如果不需要参数,该参数可以设为空指针。当创建线程成功时,函数返回0,若不为0则说明创建线程失败,常见的错误返回代码为EAGAIN和EINVAL。前者表示系统限制创建新的线程,例如线程数目过多了;后者表示第二个参数代表的线程属性值非法。创建线程成功后,新创建的线程则运行参数三和参数四确定的函数,原来的线程则继续运行下一行代码。2、pthread_join函数pthread_join用来等待一个线程的结束。函数原型为:extern int pthread_join _P (pthread_t _th, void *_thread_return);第一个参数为被等待的线程标识符,第二个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值。这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。3、pthread_exit一个线程的结束有两种途径,一种是象我们上面的例子一样,函数结束了,调用它的线程也就结束了;另一种方式是通过函数pthread_exit来实现。它的函数原型为:extern void pthread_exit _P (void *_retval) _attribute_ (_noreturn_);唯一的参数是函数的返回代码,只要pthread_join中的第二个参数thread_return不是NULL,这个值将被传递给thread_return。最后要说明的是,一个线程不能被多个线程等待,否则第一个接收到信号的线程成功返回,其余调用pthread_join的线程则返回错误代码ESRCH。7.4.6 实验步骤1. 建立工作目录注:本实验以及后续的所有实验中用 $符号表示在Linux控制台上输入的命令行。$cd /home/cvtech/jx2410/examples$mkdir thread$cd thread2. 编写thread程序源代码实际的源代码较简单,如下:下面的代码为一个简单的多线程应用程序。/*thread.c*/#include #include void thread(void) int i; for(i=0;i3;i+) printf(This is a pthread.n);int main(void) pthread_t id; int i,ret; ret=pthread_create(&id,NULL,(void *) thread,NULL); if(ret!=0) printf (Create pthread error!n); exit (1); for(i=0;i3;i+) printf(This is the main process.n); pthread_join(id,NULL); return (0);3. 编写Makefile文件4. 编译thread程序$make clean$make正确的话将生成thread程序5. 下载helloworld程序到JXARM9-2410中调试通过ftp或者nfs将第四步编译的程序thread下载到JXARM9-2410的Linux的/bin目录下。下载完成后,可以使用ls命令查看该文件是否存在,如果存在,然后在控制台(MiniCom)输入如下命令:$cd /bin$chmod 777 thread$./threadThis is the main process.This is a pthread.This is the main process.This is the main process.This is a pthread.This is a pthread.再次运行,我们可能得到如下结果:This is a pthread.This is the main process.This is a pthread.This is the main process.This is a pthread.This is the main proce
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025合同范本知识产权质押反担保合同模板
- 项目融资保函担保合同
- 建筑物生命周期中的环境管理
- 北京辅警招聘试题及答案
- 租用铺面合同协议书范本
- 提前赎回合同协议书
- 广艺书法复试题目及答案
- 初一语文试题卷及答案
- 小学五年奥数试题及答案
- 精加工试题及答案
- 形势与政策补考2-国开(XJ)-参考资料
- 高中英语-人教-选修二-单词默写
- 江苏省苏州市(2024年-2025年小学四年级语文)部编版质量测试(下学期)试卷及答案
- 高等职业学校铁道机车车辆制造与维护专业岗位实习标准
- 炸药成型与装药的制备-性能关系
- 2024年山东省德州经开区小升初数学试卷
- 剧毒易制爆化学品防盗、防抢、防破坏及技术防范系统发生故障等状态下的应急处置预案
- HY/T 0409-2024近岸海域水质浮标实时监测技术规范
- 《正常分娩》课件
- JGJ25-2010 档案馆建筑设计规范
- 医之有“道”告别难“咽”之隐-基于5A护理模式在脑卒中恢复期患者改善吞咽障碍中的应用
评论
0/150
提交评论