如果创建“进程”,不独立创建地址空间,用户及页表,甚至不进行I/O将程序的数据和代码加载到内存;只创建 task_struct;然后让新的 PCB,指向和老的PCB指向同样的进程地址空间 mm_struct,当前进程通过合理的资源分配,让每个 task_struct 都能使用进程的一部分资源,如代码也拆成多份,让每个线程去执行不同的代码;此时我们的每个PCB被CPU调度的时候,执行的 “ 粒度 ”比原始进程执行的 “ 粒度 ”要小一些。
进程的地址空间:站在资源的较低,其实式进程的资源窗口。进程,站在OS角度看:承担分配系统资源的基本单位。一个进程被创建好以后,后续可能内部存在多个执行流(线程)。如何看待曾经学的、用的进程呢?本质是:承担系统资源的基本实体,不过内部只有一个执行流。
windows具有真正线程的概念。系统内可能存在大量的进程、线程,进程线程比:1:n,操作系统需要管理线程;支持真线程的系统一定要做到描述线程:TCB,TCB又有一堆属性,其中可能包括又隶属于哪一个进程的信息等;操作系统既要进行进程管理,又要进行线程管理,设计层面是比较复杂的。windows上一定会有相关线程操作的系统调用接口。
Linux下其实没有真正意义上的线程的概念的。由于TCB和PCB有很强的相似性,进程和线程都是执行流;因此把概念同意,用进程模拟实现线程,只需要一套机制进行管理。站在CPU的角度,原本切换进程需要大量的准备工作,而线程还是在刚才进程的内部,就省去了一堆麻烦事儿。现在可能执行的“进程流”,只是更加轻量化的进程。所以在Linux上不可能直接在OS层面提供线程的系统调用接口,最多是轻量级进程的调度接口。线程原生库提供的线程操作,其实是对轻量级进程接口的封装,再配上一些缓冲区保存用户的临时数据等别的功能。另外语言上的接口,用的也都是系统提供的线程接口。
-lpthread
”选项int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);
thread
:返回线程IDattr
:设置线程的属性,attr为NULL表示使用默认属性。start_routine
:是个函数地址,线程启动后要执行的函数。arg
:传给线程启动函数的参数。#include
#include
#include
#include
#include
void *rout(void *arg) {
int i;
for( ; ; )
{
printf("I'am thread 1\n");
sleep(1);
}
}
int main( void )
{
pthread_t tid;
int ret;
if ( (ret=pthread_create(&tid, NULL, rout, NULL)) != 0 )
{
fprintf(stderr, "pthread_create : %s\n", strerror(ret));
exit(EXIT_FAILURE);
}
int i;
for(; ; )
{
printf("I'am main thread\n");
sleep(1);
}
}
----//void* 是系统层面设计的一个通用接口。
t1:t1.cc
g++ -o $@ $^ -std=c++11 -lphtread
$ ps -aL | head -1 && ps -aL | grep t1
pthread_ create
函数,会产生一个线程ID,库层面管理线程的标识符,存放在第一个参数指向的地址中。第一个参数指向一个虚拟内存单元,该内存单元的地址即为新创建线程的线程ID,属于NPTL线程库的范畴。线程库的后续操作,就是根据该线程ID来操作线程的。pthread_ self
函数,可以获得线程自身的ID:pthread_t pthread_self(void);
mmap区域
mmap是一种内存映射的方法,这一功能可以用在文件的处理上,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。在编程时可以使某个磁盘文件的内容看起来像是内存中的一个数组。如果文件由记录组成,而这些记录又能够用结构体来描述的话,可以通过访问结构数组来更新文件的内容。
实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,即完成了对文件的操作而不必再调用read,write等系统调用函数。内核空间对这段区域的修改也直接反映用户空间,从而可以实现不同进程间的文件共享。mmap 内存映射详解
return
。这种方法对主线程不适用,从main函数return相当于调用exit。pthread_ exit
终止自己。pthread_ cancel
终止同一进程中的另一个线程。pthread_exit
函数:void pthread_exit(void *value_ptr);
value_ptr:value_ptr
不要指向一个局部变量。pthread_exit
或者return
返回的指针所指向的内存单元必须是全局的或者是用malloc
分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。---//return
void *thread1( void *arg )
{
printf("thread 1 returning ... \n");
int *p = (int*)malloc(sizeof(int));
*p = 1;
return (void*)p;
}
void *thread3( void *arg )
{
while ( 1 )
{
printf("thread 3 is running ...\n");
sleep(1);
}
return NULL;
}
----//`pthread_exit`
void *thread2( void *arg )
{
printf("thread 2 exiting ...\n");
int *p = (int*)malloc(sizeof(int));
*p = 2;
pthread_exit((void*)p);
}
pthread_cancel
函数:int pthread_cancel(pthread_t thread);
::取消一个执行中的线程
thread:
:线程ID....
pthread_t tid;
pthread_create(&tid, NULL, thread3, NULL);
sleep(3);
pthread_cancel(tid);
.....
int pthread_join(pthread_t thread, void **value_ptr);
:主线程阻式等待线程结束
thread
:线程ID,value_ptr
:它指向一个指针,后者指向线程的返回值,可以返回任意结构类型的线程退出信息。....
void *ret;
pthread_t tid;
pthread_create(&tid, NULL, thread3, NULL);
sleep(3);
pthread_cancel(tid);
pthread_join(tid, &ret);
....
int pthread_detach(pthread_t thread);
pthread_detach(pthread_self());
#include
#include
#include
#include
#include
void *thread_run( void * arg )
{
pthread_detach(pthread_self());
printf("%s\n", (char*)arg);
return NULL;
}
int main( void )
{
pthread_t tid;
if ( pthread_create(&tid, NULL, thread_run, "thread1 run...") != 0 )
{
printf("create thread error\n");
return 1;
}
int ret = 0;
sleep(1);//很重要,要让线程先分离,再等待
if ( pthread_join(tid, NULL ) == 0 )
{
printf("pthread wait success\n");
ret = 0;
} else
{
printf("pthread wait failed\n");
ret = 1;
}
return ret;
}
Linux没有真正意义上的线程,但是会提供轻量级进程的接口vfrok;为了更好的适配,封装了一个用户层的原生线程库,线程相关的属性数据在用户空间的共享区的线程库内,由库保存维护。
线程库NPTL(现代Linux上模式运行的线程库是NPTL(Native POSIX Thread Library),用户层线程ID,其实是共享区里线程库中的某一个起始位置。主线程,不使用库中的栈结构,直接使用地址空间中的栈。