Linux系统下的多线程遵循POSIX线程接口
- 格式:doc
- 大小:48.00 KB
- 文档页数:9
posix标准linuxPOSIX标准是一种操作系统接口标准,它定义了操作系统应该提供哪些服务以及应该如何提供这些服务。
而Linux是一种免费的开放源代码操作系统,它遵循POSIX标准,因此在Linux系统上可以使用POSIX接口来进行开发和编程。
POSIX标准的出现,是为了解决不同操作系统之间的兼容性问题。
由于不同厂商开发的操作系统接口不同,导致了软件在不同系统上无法通用。
为了解决这个问题,IEEE组织制定了POSIX标准,以便使得不同操作系统上的软件能够具有一定的可移植性。
在Linux系统中,POSIX标准的实现对于开发者来说有着重要的意义。
首先,POSIX标准定义了文件系统的接口,包括文件的创建、打开、读写、关闭等操作。
这使得开发者可以使用统一的接口来处理文件,而不需要关心底层文件系统的具体实现。
其次,POSIX标准定义了进程管理的接口,包括进程的创建、终止、信号处理等操作。
这使得开发者可以编写与操作系统无关的进程管理代码,从而实现跨平台的进程管理功能。
另外,POSIX标准还定义了线程管理、信号处理、定时器、网络接口等一系列接口,这些接口的统一定义为开发者提供了方便,使得他们可以更加高效地开发应用程序。
总的来说,POSIX标准在Linux系统中的实现,为开发者提供了一套统一的接口,使得他们可以编写与操作系统无关的代码。
这不仅提高了软件的可移植性,也为开发者提供了更加方便、高效的开发环境。
在实际的开发过程中,开发者可以通过调用Linux系统提供的POSIX接口来实现各种功能。
比如,通过调用标准的文件操作接口来读写文件,通过调用标准的进程管理接口来创建和管理进程,通过调用标准的网络接口来进行网络通信等等。
需要注意的是,虽然POSIX标准在一定程度上提高了软件的可移植性,但是在实际开发过程中,仍然需要考虑到不同操作系统之间的差异。
因为即使不同操作系统都遵循了POSIX标准,但是在具体的实现上仍然可能存在差异,这就需要开发者在编写跨平台代码时进行一定的适配。
Linux系统编程中的POSIX标准Linux系统编程在很长一段时间内一直在不断发展,随着POSIX标准的出现,Linux系统在该方面得到了全面的支持。
POSIX标准使得不同的Unix系统和其他操作系统之间的可移植性得到了很大的提高,这是Linux系统编程所必需的。
什么是POSIX?POSIX(Portable Operating System Interface,可移植操作系统接口)是由IEEE(Institute of Electrical and Electronics Engineers,电气和电子工程师学会)开发的一系列接口标准。
1990年,IEEE 正式颁发了POSIX标准的第一个版本,这个标准定义了许多操作系统接口的规范性要求,使得Unix的不同版本之间的软件可以轻松地进行移植。
POSIX标准的主旨是定义一组标准操作系统接口,使得应用程序的可移植性更好。
POSIX标准定义了一组与操作系统交互的API,包括文件操作、进程管理、信号传递等等。
POSIX标准在Linux中的应用在Linux系统中,POSIX标准已经得到了广泛的应用。
许多Linux上的C库都以POSIX标准为基础进行开发,例如glibc、bionic等等。
这些C库包含了POSIX标准中定义的一系列API,并提供了一些额外的功能。
开发者在编写应用程序时,可以使用这些API,从而使得应用程序更加规范,并且在其他系统上的移植性更好。
POSIX标准还提供了一个实现的方式,也就是POSIX兼容层。
POSIX兼容层是一个在Linux内核中的模块,它提供了一些非标准的API,这些API能够向后兼容POSIX标准。
这个兼容层让系统具有了更好的兼容性,比如在系统上运行一些遵循POSIX标准的旧应用程序时,可以使用这些非标准API进行支持。
在Linux系统中,可以使用一些工具来检查应用程序是否严格遵循了POSIX标准。
例如lint和splint工具,它们可以自动分析源代码,并提供一些有用的警告信息和其他功能。
POSIX 线程详解 1POSIX(可移植操作系统接口)线程是提高代码响应和性能的有力手段。
在本系列中,Daniel Robbins 向您精确地展示在编程中如何使用线程。
其中还涉及大量幕后细节,读完本系列文章,您完全可以运用POSIX 线程创建多线程程序。
线程是有趣的了解如何正确运用线程是每一个优秀程序员必备的素质。
线程类似于进程。
如同进程,线程由内核按时间分片进行管理。
在单处理器系统中,内核使用时间分片来模拟线程的并发执行,这种方式和进程的相同。
而在多处理器系统中,如同多个进程,线程实际上一样可以并发执行。
那么为什么对于大多数合作性任务,多线程比多个独立的进程更优越呢?这是因为,线程共享相同的内存空间。
不同的线程可以存取内存中的同一个变量。
所以,程序中的所有线程都可以读或写声明过的全局变量。
如果曾用 fork() 编写过重要代码,就会认识到这个工具的重要性。
为什么呢?虽然 fork() 允许创建多个进程,但它还会带来以下通信问题: 如何让多个进程相互通信,这里每个进程都有各自独立的内存空间。
对这个问题没有一个简单的答案。
虽然有许多不同种类的本地 IPC (进程间通信),但它们都遇到两个重要障碍:强加了某种形式的额外内核开销,从而降低性能。
对于大多数情形,IPC 不是对于代码的“自然”扩展。
通常极大地增加了程序的复杂性。
双重坏事: 开销和复杂性都非好事。
如果曾经为了支持 IPC 而对程序大动干戈过,那么您就会真正欣赏线程提供的简单共享内存机制。
由于所有的线程都驻留在同一内存空间,POSIX 线程无需进行开销大而复杂的长距离调用。
只要利用简单的同步机制,程序中所有的线程都可以读取和修改已有的数据结构。
而无需将数据经由文件描述符转储或挤入紧窄的共享内存空间。
仅此一个原因,就足以让您考虑应该采用单进程/多线程模式而非多进程/单线程模式。
线程是快捷的不仅如此。
线程同样还是非常快捷的。
与标准 fork() 相比,线程带来的开销很小。
linux pthread 用法Linux pthread(POSIX线程)是一种多线程库,它提供了在Linux系统上创建和管理线程的API。
使用pthread库,可以编写多线程程序,实现并发执行和资源共享。
下面是一些常用的pthread函数和用法:1.pthread_create():用于创建一个新的线程。
它接受一个指向线程属性的指针,一个指向线程函数的指针,以及传递给线程函数的参数。
函数原型为:intpthread_create(pthread_t *thread, const pthread_attr_t *attr, void*(*start_routine) (void *), void *arg);2.pthread_join():用于等待一个线程的结束。
它接受一个指向线程标识符的指针,以及一个指向用于存储线程返回值的指针的指针。
函数原型为:intpthread_join(pthread_t thread, void **retval);3.pthread_self():用于获取当前线程的标识符。
函数原型为:pthread_tpthread_self(void);4.pthread_detach():用于将一个线程从系统中分离出去。
这通常用于在后台运行的任务,不需要手动等待它们完成。
函数原型为:int pthread_detach(pthread_t thread);5.pthread_equal():用于比较两个线程标识符是否相等。
函数原型为:intpthread_equal(pthread_t thread1, pthread_t thread2);6.pthread_mutex_init():用于初始化一个互斥锁。
函数原型为:intpthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); 7.pthread_mutex_lock():用于获取一个互斥锁。
posix接口标准POSIX接口标准简介POSIX(可移植操作系统接口)是一组定义了操作系统接口的标准。
它的目的是为了实现操作系统的可移植性,使得开发者可以在不同的操作系统上编写可移植的软件。
POSIX定义了许多功能、命令以及系统调用,以便于开发者编写可移植的软件,并且保持对不同系统的一致性。
POSIX标准的内容POSIX接口标准包含了许多不同的组件,以下是其中几个重要的组件:1. 文件和目录操作:POSIX定义了一系列函数,使得开发者可以对文件和目录进行读、写、创建等操作。
例如,开发者可以使用open()函数打开一个文件,并使用read()和write()函数进行数据的读取和写入。
此外,POSIX还定义了一些标准的文件和目录路径,以及文件权限的控制。
2. 进程控制:POSIX提供了一套用于进程控制的函数。
开发者可以使用fork()函数创建一个新的进程,使用exec()函数来加载新程序并替换当前进程的地址空间,以及使用wait()函数等待一个子进程的结束。
这些函数使得进程的创建、管理和通信变得更加容易。
3. 线程控制:POSIX对线程控制也进行了定义。
通过使用线程,可以实现并发执行的能力,从而提高程序的性能。
POSIX定义了线程的创建、同步和销毁等操作。
开发者可以使用pthread_create()函数创建线程,使用mutex和condition variable等机制进行线程同步。
4. 信号处理:POSIX提供了一套用于处理信号的函数,使得开发者可以对软件中出现的不同事件做出响应。
开发者可以使用signal()函数来定义信号的处理程序,以及使用sigprocmask()来管理进程的信号屏蔽集。
5. 文件I/O:POSIX定义了标准的文件I/O接口和相关函数。
开发者可以使用fopen()和fclose()函数打开和关闭文件,使用fgets()和fputs()函数进行文件的读写操作,以及使用fseek()和ftell()函数进行文件指针的定位和查询。
简述Linux下的多线程编程互斥锁和条件变量应用Linux下的多线程遵循POSIX线程接口,称为pthread。
编写Linux下的多线程程序,需要使用头文件pthread.h,链接时需要使用库libpthread.a。
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。
线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器、一组寄存器和栈),但是它可与同属一个进程的其它的线程共享进程所拥有的全部资源。
当多个任务可以并行执行时,可以为每个任务启动一个线程。
线程是并发运行的。
在串行程序基础上引入线程和进程是为了提供程序的并发度,从而提高程序运行效率和响应时间。
与进程相比,线程的优势:(1)、线程共享相同的内存空间,不同的线程可以存取内存中的同一个变量;(2)、与标准fork()相比,线程带来的开销很小,节省了CPU时间,使得线程创建比新进程创建快上十到一百倍。
适应多线程的理由:(1)、和进程相比,它是一种非常“节俭”的多任务操作方式,在linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种“昂贵”的多任务工作方式。
而运行一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间;(2)、线程间方便的通信机制。
对不同的进程来说,它们具有独立的数据空间,要进行数据的传输只能通过通信的方式进行,这种方式不仅费时,而且很不方便。
线程则不然,同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。
多线程程序作为一种多任务、并发的工作方式,其优点包括:(1)、提供应用程序响应;(2)、使多CPU系统更加有效:操作系统会保证当线程数不大于CPU数目时,不同的线程运行在不同的CPU上;(3)、改善程序结构:一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序利于理解和修改。
Linux内核多线程(转)Linux内核可以看作一个服务进程(管理软硬件资源,响应用户进程的种种合理以及不合理的请求)。
内核需要多个执行流并行,为了防止可能的阻塞,支持多线程是必要的。
内核线程就是内核的分身,一个分身可以处理一件特定事情。
内核线程的调度由内核负责,一个内核线程处于阻塞状态时不影响其他的内核线程,因为其是调度的基本单位。
这与用户线程是不一样的。
因为内核线程只运行在内核态,因此,它只能使用大于PAGE_OFFSET(3G)的地址空间。
内核线程和普通的进程间的区别在于内核线程没有独立的地址空间,mm指针被设置为NULL;它只在内核空间运行,从来不切换到用户空间去;并且和普通进程一样,可以被调度,也可以被抢占。
内核线程(thread)或叫守护进程(daemon),在操作系统中占据相当大的比例,当Linux操作系统启动以后,你可以用”ps -ef”命令查看系统中的进程,这时会发现很多以”d”结尾的进程名,确切说名称显示里面加"[]"的,这些进程就是内核线程。
创建内核线程最基本的两个接口函数是:kthread_run(threadfn, data, namefmt, ...)和kernel_thread(int(* fn)(void *),void * arg,unsigned long flags)这里我们主要介绍kthread_run,后面会专门分析这两个函数的异同。
kthread_run 事实上是一个宏定义:/** * kthread_run - create and wake a thread. * @threadfn: the function to run until signal_pending(current). * @data: data ptr for @threadfn. * @namefmt: printf-style name for the thread. * * Description: Convenient wrapper for kthread_create() followed by * wake_up_process(). Returns the kthread or ERR_PTR(-ENOMEM). */#define kthread_run(threadfn, data, namefmt, ...)\({struct task_struct *__k= kthread_create(threadfn, data, namefmt, ## __VA_ARGS__);if (!IS_ERR(__k))wake_up_process(__k);__k;})kthread_run()负责内核线程的创建,它由kthread_create()和wake_up_process()两部分组成,这样的好处是用kthread_run()创建的线程可以直接运行。
Linux系统下的多线程遵循POSIX线程接口,称为pthread。
编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a。
顺便说一下,Linux 下pthread的实现是通过系统调用clone()来实现的。
clone()是Linux所特有的系统调用,它的使用方式类似fork,关于clone()的详细情况,有兴趣的读者可以去查看有关文档说明。
下面我们展示一个最简单的多线程程序example1.c。
/* example.c*/#include <stdio.h>#include <pthread.h>void thread(void){int i;for(i=0;i<3;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;i<3;i++)printf("This is the main process.\n");pthread_join(id,NULL);return (0);}我们编译此程序:gcc example1.c -lpthread -o example1运行example1,我们得到如下结果:This 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 process.前后两次结果不一样,这是两个线程争夺CPU资源的结果。
上面的示例中,我们使用到了两个函数,pthread_create和pthread_join,并声明了一个pthread_t型的变量。
pthread_t在头文件/usr/include/bits/pthreadtypes.h中定义:typedef unsigned long int pthread_t;它是一个线程的标识符。
函数pthread_create用来创建一个线程,它的原型为:extern int pthread_create __P ((pthread_t *__thread, __const pthread_attr_t *__attr,void *(*__start_routine) (void *), void *__arg));第一个参数为指向线程标识符的指针,第二个参数用来设置线程属性,第三个参数是线程运行函数的起始地址,最后一个参数是运行函数的参数。
这里,我们的函数thread不需要参数,所以最后一个参数设为空指针。
第二个参数我们也设为空指针,这样将生成默认属性的线程。
对线程属性的设定和修改我们将在下一节阐述。
当创建线程成功时,函数返回0,若不为0则说明创建线程失败,常见的错误返回代码为EAGAIN和EINV AL。
前者表示系统限制创建新的线程,例如线程数目过多了;后者表示第二个参数代表的线程属性值非法。
创建线程成功后,新创建的线程则运行参数三和参数四确定的函数,原来的线程则继续运行下一行代码。
函数pthread_join用来等待一个线程的结束。
函数原型为:extern int pthread_join __P ((pthread_t __th, void **__thread_return)); 第一个参数为被等待的线程标识符,第二个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值。
这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。
一个线程的结束有两种途径,一种是象我们上面的例子一样,函数结束了,调用它的线程也就结束了;另一种方式是通过函数pthread_exit来实现。
它的函数原型为:extern void pthread_exit __P ((void *__retval)) __attribute__ ((__noreturn__)); 唯一的参数是函数的返回代码,只要pthread_join中的第二个参数thread_return不是NULL,这个值将被传递给thread_return。
最后要说明的是,一个线程不能被多个线程等待,否则第一个接收到信号的线程成功返回,其余调用pthread_join的线程则返回错误代码ESRCH。
在这一节里,我们编写了一个最简单的线程,并掌握了最常用的三个函数pthread_create,pthread_join和pthread_exit。
下面,我们来了解线程的一些常用属性以及如何设置这些属性。
修改线程的属性在上一节的例子里,我们用pthread_create函数创建了一个线程,在这个线程中,我们使用了默认参数,即将该函数的第二个参数设为NULL。
的确,对大多数程序来说,使用默认属性就够了,但我们还是有必要来了解一下线程的有关属性。
属性结构为pthread_attr_t,它同样在头文件/usr/include/pthread.h中定义,喜欢追根问底的人可以自己去查看。
属性值不能直接设置,须使用相关函数进行操作,初始化的函数为pthread_attr_init,这个函数必须在pthread_create函数之前调用。
属性对象主要包括是否绑定、是否分离、堆栈地址、堆栈大小、优先级。
默认的属性为非绑定、非分离、缺省1M的堆栈、与父进程同样级别的优先级。
关于线程的绑定,牵涉到另外一个概念:轻进程(LWP:Light Weight Process)。
轻进程可以理解为内核线程,它位于用户层和系统层之间。
系统对线程资源的分配、对线程的控制是通过轻进程来实现的,一个轻进程可以控制一个或多个线程。
默认状况下,启动多少轻进程、哪些轻进程来控制哪些线程是由系统来控制的,这种状况即称为非绑定的。
绑定状况下,则顾名思义,即某个线程固定的"绑"在一个轻进程之上。
被绑定的线程具有较高的响应速度,这是因为CPU时间片的调度是面向轻进程的,绑定的线程可以保证在需要的时候它总有一个轻进程可用。
通过设置被绑定的轻进程的优先级和调度级可以使得绑定的线程满足诸如实时反应之类的要求。
设置线程绑定状态的函数为pthread_attr_setscope,它有两个参数,第一个是指向属性结构的指针,第二个是绑定类型,它有两个取值:PTHREAD_SCOPE_SYSTEM (绑定的)和PTHREAD_SCOPE_PROCESS(非绑定的)。
下面的代码即创建了一个绑定的线程。
#include <pthread.h>pthread_attr_t attr;pthread_t tid;/*初始化属性值,均设为默认值*/pthread_attr_init(&attr);pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);pthread_create(&tid, &attr, (void *) my_function, NULL);线程的分离状态决定一个线程以什么样的方式来终止自己。
在上面的例子中,我们采用了线程的默认属性,即为非分离状态,这种情况下,原有的线程等待创建的线程结束。
只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。
而分离线程不是这样子的,它没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。
程序员应该根据自己的需要,选择适当的分离状态。
设置线程分离状态的函数为pthread_attr_setdetachstate (pthread_attr_t *attr, int detachstate)。
第二个参数可选为PTHREAD_CREATE_DETACHED(分离线程)和PTHREAD _CREATE_JOINABLE (非分离线程)。
这里要注意的一点是,如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在pthread_create函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create的线程就得到了错误的线程号。
要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用pthread_cond_timewait函数,让这个线程等待一会儿,留出足够的时间让函数pthread_create返回。
设置一段等待时间,是在多线程编程里常用的方法。
但是注意不要使用诸如wait()之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。
另外一个可能常用的属性是线程的优先级,它存放在结构sched_param中。
用函数pthread_attr_getschedparam和函数pthread_attr_setschedparam进行存放,一般说来,我们总是先取优先级,对取得的值修改后再存放回去。
下面即是一段简单的例子。
#include <pthread.h>#include <sched.h>pthread_attr_t attr;pthread_t tid;sched_param param;int newprio=20;pthread_attr_init(&attr);pthread_attr_getschedparam(&attr, ¶m);param.sched_priority=newprio;pthread_attr_setschedparam(&attr, ¶m);pthread_create(&tid, &attr, (void *)myfunction, myarg);线程的数据处理和进程相比,线程的最大优点之一是数据的共享性,各个进程共享父进程处沿袭的数据段,可以方便的获得、修改数据。