关于写时复制:因为一般 fork后面都接着exec,所以,如今的 fork都在用写时复制的技术,顾名思意,就是,数据段,堆,栈,一開始并不复制,由父,子进程共享,并将这些内存设置为仅仅读。直到父,子进程一方尝试写这些区域,则内核才为须要改动的那片内存拷贝副本。这样做能够提高 fork的效率。
三.多线程
线程是可运行代码的可分派单元。这个名称来源于“运行的线索”的概念。在基于线程的多任务的环境中,全部进程有至少一个线程,可是它们能够具有多个任务。这意味着单个程序能够并发运行两个或者多个任务。
简而言之,线程就是把一个进程分为非常多片,每一片都能够是一个独立的流程。这已经明显不同于多进程了,进程是一个拷贝的流程,而线程不过把一条河流截成非常多条小溪。它没有拷贝这些额外的开销,可是不过现存的一条河流,就被多线程技术差点儿无开销地转成非常多条小流程,它的伟大就在于它少之又少的系统开销。(当然伟大的后面又引发了重入性等种种问题,这个后面慢慢比較)。
还是先看linux提供的多线程的系统调用:
int pthread_create(pthread_t *restrict tidp,
const pthread_attr_t *restrict attr,
void *(*start_rtn)(void),
void *restrict arg);
Returns: 0 if OK, error number on failure
第一个參数为指向线程标识符的指针。
第二个參数用来设置线程属性。
第三个參数是线程执行函数的起始地址。
最后一个參数是执行函数的參数。
#include<stdio.h> #include<string.h> #include<stdlib.h> #include<unistd.h> #include<pthread.h> void* task1(void*); void* task2(void*); void usr(); int p1,p2; int main() { usr(); getchar(); return 1; } void usr() { pthread_t pid1, pid2; pthread_attr_t attr; void *p; int ret=0; pthread_attr_init(&attr); //初始化线程属性结构 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); //设置attr结构为分离 pthread_create(&pid1, &attr, task1, NULL); //创建线程,返回线程号给pid1,线程属性设置为attr的属性,线程函数入口为task1,參数为NULL pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); pthread_create(&pid2, &attr, task2, NULL); //前台工作 ret=pthread_join(pid2, &p); //等待pid2返回,返回值赋给p printf("after pthread2:ret=%d,p=%d/n", ret,(int)p); } void* task1(void *arg1) { printf("task1/n"); //艰苦而无法预料的工作,设置为分离线程,任其自生自灭 pthread_exit( (void *)1); } void* task2(void *arg2) { int i=0; printf("thread2 begin./n"); //继续送外卖的工作 pthread_exit((void *)2); }
这个多线程的样例应该非常明了了,主线程做自己的事情,生成2个子线程,task1为分离,任其自生自灭,而task2还是继续送外卖,须要等待返回。(因该还记得前面说过僵尸进程吧,线程也是须要等待的。假设不想等待,就设置线程为分离线程)
额外的说下,linux下要编译使用线程的代码,一定要记得调用pthread库。例如以下编译:
gcc -o pthrea -pthread pthrea.c
四.比較以及注意事项
1.看完前面,应该对多进程和多线程有个直观的认识。假设总结多进程和多线程的差别,你肯定能说,前者开销大,后者开销较小。确实,这就是最主要的差别。
2.线程函数的可重入性:
说到函数的可重入,和线程安全,我偷懒了,引用网上的一些总结。
线程安全:概念比較直观。一般说来,一个函数被称为线程安全的,当且仅当被多个并发线程重复调用时,它会一直产生正确的结果。