-
操作系统 进程创建实验报告
2011-06-11 17:32:46它由三部分组成: 1、用户级上、下文。主要成分是用户程序; 2、寄存器上、下文。由CPU中的一些寄存器的内容组成,如PC,PSW,SP及通用寄存器等; 3、系统级上、下文。包括OS为管理进程所用的信息,有静态和动态之分... -
17 一个展示gcc编译优化选项-O效果的实例
2014-04-01 13:53:16gcc在编译的时候可以使用-O、-O2和-O3选项(字母O要大写)来对代码进行优化,以下是一个故意创建的代码欧用于配合time命令测试该优化选项的效果,time命令的输出为real、user和sys三项,各个部分说明如下: ...gcc在编译的时候可以使用-O、-O2和-O3选项(字母O要大写)来对代码进行优化,以下是一个故意创建的代码欧用于配合time命令测试该优化选项的效果,time命令的输出为real、user和sys三项,各个部分说明如下:real:进程总的执行时间, 它和系统负载有关(包括了进程调度,切换的时间),
user:被测量的进程中用户指令的执行时间;
sys:被测量进程中内核代用户指令执行的时间,user和sys的和被称为CPU时间。#include <stdio.h>
int main(void)
{
unsigned long int counter; /*定义相关的变量*/
unsigned long int result;
int i;unsigned long int temp;
unsigned int five;
/*判断条件在每一次for循环时都会进行一次计算*/
for (counter=0; counter < 2009 * 2009 * 100 / 4 + 2010; counter += (10 - 6) / 4)
{
temp=counter / 1979;
for(i=0; i < 20; i++)
{
five=200*200/8000; /*每一次for循环都会进行复杂的计算*/
}
result=counter;
}
printf("Result is %ld\n", result);
return 0;
}以下是三种不同的编译选项对应的执行时间差异(不同的机器有不同的差别,相同的机器在同时运行不同的其他程序的时候也有差异)
alloy@raspberrypi:~/linuxcupdate/chapter4$ gcc inefficient.c -o inefficient
alloy@raspberrypi:~/linuxcupdate/chapter4$ time ./inefficient
Result is 100904034
real 0m30.136s
user 0m29.920s
sys 0m0.000s
alloy@raspberrypi:~/linuxcupdate/chapter4$ gcc -O2 inefficient.c -o inefficient
alloy@raspberrypi:~/linuxcupdate/chapter4$ time ./inefficient
Result is 100904034
real 0m0.010s
user 0m0.000s
sys 0m0.000s
alloy@raspberrypi:~/linuxcupdate/chapter4$ gcc -O3 inefficient.c -o inefficient
alloy@raspberrypi:~/linuxcupdate/chapter4$ time ./inefficient
Result is 100904034
real 0m0.009s
user 0m0.000s
sys 0m0.010s以上运行环境是安装自带系统Raspbian的树莓派2代(Raspberry Pi) -
PHP基础教程 是一个比较有价值的PHP新手教程!
2010-04-24 18:52:44CGI程序的伸缩性不很理想,因为它为每一个正在运行的CGI程序开一个独立进程。解决方法就是将经常用来编写CGI程序的语言的解释器编译进你的web服务器(比如mod_perl,JSP)。PHP就可以以这种方式安装,虽然很少有人愿意... -
OS 操作系统 进程 线程 文件 设备 C# 多用户 登陆 课程设计 报告 算法 FCFS
2010-05-17 23:13:15实际设备,所以无法知道设备何时工作完成,所以假定一个数,这个数随着系统时间 增加而递减,减到0时,认为是设备工作完成); end. 表示文件结束,同时将结果写入文件out,其中包括文件路径名和i的值。 用户... -
总结: linux下的多线程API (POSIX线程)(转)
2013-03-06 17:23:02一个完成的线程/进程包括三部分,代码+数据+内存栈;子线程和子进程在被创建的时候, 对于fork()创建子进程,三部分都要复制一份,数据包括比如文件描述符,虚拟内存,子进程关闭文件描述符不会影响父进程中的描述符; ...POSIX线程:
一个完成的线程/进程包括三部分,代码+数据+内存栈;子线程和子进程在被创建的时候,
对于fork()创建子进程,三部分都要复制一份,数据包括比如文件描述符,虚拟内存,子进程关闭文件描述符不会影响父进程中的描述符;
对于pthread_create()创建子线程的时候,只有内存栈被复制,其他的部分(代码,数据都是共享的),如果一个线程改变了某变量的值,其他所有的线程都调用的是改变之后的值;
头文件#include <pthread.h>
编译参数: -lpthread
(一)涉及到的类型:
pthread_t, pthread_attr_t, pthread_cond_t, pthread_mutexattr_t, void* (*)(void*),
(二)涉及到的函数:
pthread_cancel,pthread_wait,
pthread_create, pthread_self, pthread_detach, pthread_join, pthread_exit,
pthread_mutex_lock, pthread_mutex_trylock, pthread_mutex_unlock, pthread_mutex_destory; //参数都是pthread_mutex_t*
int pthread_attr_init(pthread_attr_t*), int pthread_attr_destory(pthread_attr_t*),
int pthread_attr_setdetachstatus(pthread_attr_t*,int); //设置属性
int pthread_attr_getdetacstatus(const pthread_attr_t*, int*); //获取属性
pthread_cond_wait,pthread_cond_timewait,pthread_cond_signal,pthread_cond_broadcast,pthread_cond_destory;
1.设置线程属性:
一个线程属性对象,(例如pthread_attr_t attr_test),可以创建很多线程,创建线程之后没有必要保持此对象;
在线程的所有属性中,最重要的是分离属性(detach status),一个线程可以是等待线程(joinable thread)or分离线程(detach thread),默认是joinable thread;
对于一个非分离(joinable)线程, 类似于进程中的Zombies进程,joinbale thread在退出后,资源不会被立刻释放,直到被thread_join获取它的返回值;
对于一个分离(detach)线程在退出之后, 资源会被立刻释放, 其他线程无法获悉其返回值;
代码例子,main线程中:
pthread_attr_t attr; //(1)创建变量:
phtread_attr_init(&attr); //(2)初始化此属性变量;
pthread_setdetachstatus(&attr,PTHREAD_CREATED_DETACHED);//(3)
pthread_create(&t,&attr,&function,NULL);//(4)创建线程;
pthread_destory(&attr);//(5)创建完后,即可销毁此属性变量;
pthread_join(t,NULL); //错误!分离的线程无法被join
return; //main线程结束
2.创建线程:
int pthread_create(pthread *thread, pthread_attr_t *attr, void* (*start_routine)(*void), void* arg);
其中pthread_attr_t*和void* arg可设置为NULL;
3.获取线程的pid:
pthread_t pthread_self(void);
4.线程分离:
int pthread_detach(pthread_t thread);
线程设置为分离, 当此线程退出时, 不会向任何其他线程传递返回值, 分离的线程无法被pthread_join();
例子,把线程分离:
void* myThread(void* param)
{
pthread_detach(pthread_self());
pthread_exit(param);
}
5.线程等待(加入):
pthread_join(pthread thread, void **status);
main创建n个线程,无法确定main一定在所有线程都退出后再退出,所以要让main线程等待所有子线程退出后,再退出;
调用者将等待第一参数标识的线程退出后,才能退出; 第二参数保存函数的返回值;
6.线程退出和取消:
int pthread_exit(void* retval);
线程内调用,调用线程退出,参数是线程函数的返回值, 或者可以把参数设为NULL;
线程退出有三种方式,1线程函数返回,2调用pthread_exit,3调用pthread_cancel
一个线程可以请求中止另一个线程,只需调用pthread_cancel(pid)即可;被cancel的线程,可以被pthread_wait(pid),此wait函数会释放线程占用的资源,除非是脱离(detach)线程;
线程是否可以被取消(cancel)是线程的一个属性,类似"分离"属性.线程调用pthread_setcancelstatus()可以实现自身是否可被cancel; 注意区别pthread_attr_setXXX函数组;
pthread_setcancelstatus(PTHREAD_CALCEL_DISABLE,NULL);//调用线程不可被取消!
pthread_setcancelstatus(PTHREAD_CALCEL_ENABLE,NULL);//若第二参数不为NULL,则会存储线程前一个状态的可否取消状态;
在一般应用中,pthread_setcancelstatus可以设置一个代码范围,在此范围内线程不可被取消,比如银行转帐,需要两步操作,A账户扣除,B账户增加,在这过程中不希望线程被取消的;
7.互斥体 mutex:
//初始化
int pthread_mutex_init(pthread_mutex_t* , pthread_mutexattr_t*);//第二个参数NULL是指向互斥锁类型的结构,代表默认属性的互斥锁;
初始化互斥锁属性的代码如下:
pthread_mutex_t mutex;
pthread_mutexattr_t attr;
phtread_mutexattr_init(&attr);
pthread_mutexattr_setkind_np(&attr,PTHREAD_MUTEX_ERRORCHECK_NP);//设置互斥体类型
pthread_mutex_init(&mutex, &attr); //初始化互斥体
pthread_mutex_destory(&attr); //变量pthread_mutexattr_t使用完成后,销毁
//注:带_np结尾的是指"non-portable",不可移植
或者更简单的初始化mutex方法:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //快速互斥
PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP: 递归互斥,lock一个已锁互斥体时,不会阻塞,而是记录次数,unlock必须有相同的次数;
PTHREAD_CHECKERROR_MUTEX_INITIALIZER_NP:纠错互斥,
//互斥体锁定
int pthread_mutex_lock(pthread_mutex_t* mutex); //成功返0
如果mutex已被线程A锁定后, 线程B尝试锁定mutex, 此时B线程会被阻塞,直到线程A解除了mutex的锁定, 同一时刻可能会有多个线程阻塞在一个互斥体上;
//互斥体死锁
互斥体的类型分为三种, fast mutex(默认类型), recursive mutex(递归互斥体), error-checking mutex(纠错互斥体),
产生死锁的可能情况: 两个线程,两个互斥体交叉锁定,会出现互相等待的情况,即死锁, 代码例子:
- //线程A
- While(1){
- mutex_lock(L1);
- mutex_lock(L2);
- do_jobA();
- mutex_unlock(L2);
- mutex_unlock(L1);
- }
- //线程B
- While(1){
- mutex_lock(L2);
- mutex_lock(L1);
- do_jobB();
- mutex_unlock(L1);
- mutex_unlock(L2);
- }
- /*
- 进程A: mutex_lock(L1);
- 同时进程B:mutex_lock(L2); 将死锁 */
//非阻塞锁定互斥体:
int pthread_mutex_trylock(pthread_mutex_t* mutex);
/*成功锁定互斥体,返回0; 如果互斥体已被其他的线程锁定,返回EBUSY,不会阻塞*/
代码示例:
int ret = pthread_mutex_trylock(&mutex);
if(ret == EBUSY){
/*互斥体已被锁定,不阻塞*/
}
else if(ret == EINVAL){
assert(0); /*无效属性*/
}
else{
/*解锁成功, 进行关键代码操作*/
ret = pthread_mutex_unlock(&mutex);
}
//互斥体解锁
int pthread_mutex_unlock(pthread_mutex_t* mutex); //成功返0
//互斥体销毁
int pthread_mutex_destory(pthread_mutex_t*); //在程序末尾或其他位置,清理互斥锁,此时互斥体不能是locked状态;
8.线程条件变量
在a情况下让线程继续执行,在b情况下让线程阻塞,当阻塞条件改变时,所有被这个条件变量阻塞的线程都能被激活;
适用于的最简单情况,例如:线程thread1要循环执行从队列queue取出最后一个节点,线程thread2向queue增加节点;或者有更多的读写thread共同操作一个queue;
一般的做法是,所有线程while(1)循环,pthread_mutex_lock和_unlock保护一个代码区域,在此检查queue是否为空,,,为什么这种的效率不高?
(1)
int pthread_cond_init(pthread_cond_t*, NULL);//第二参数为NULL,则创建默认属性
(2)条件变量等待:
int pthread_cond_wait(pthread_cond_t*, pthread_mutex_t*);
设ThreadA,执行pthread_cond_wait函数之前,必须锁定互斥体, pthread_cond_wait以一个原子操作[解锁互斥体]并[开始等待信号], 然后线程相当于wait状态或sleep状态, 处理器会在此时处理其他的线程;
当某线程向ThreadA发送信号时, 线程ThreadA从pthread_cond_wait函数中返回并锁定互斥体;
// cond代码示例(a)
// work_load表示当前负载,当超过MAX_WORK_LOAD时,consumer线程调用process()函数进行work_load减; producer线程每次work_load++后发送一次信号;
pthread_mutex_lock(&mutex);
while(work_load < MAX_WORK_LOAD)
pthread_cond_wait(&cond,&mutex);
process(); //保护部分处理
pthread_mutex_unlock(&mutex);
// cond代码示例(b)
// queue表示一个任务队列构成的链表,consumer线程从链表中取出节点并处理,producer向链表增加节点
/*线程consumer*/
void* consumer(void*){
while(!exit){
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond); //互斥设为非锁定,等待条件信号
/*收到信号,从wait函数返回时,互斥设为锁定,queue--操作 */
pthread_mutex_unlock();
}
printf("recv exit single/n");
return;
}
/*线程producer*/
void* producer(void*){
pthread_mutex_lock(&mutex);
/*queue++*/
pthread_cond_signal(&cond);//或broadcast
pthread_mutex_unlock(&mutex);
return;
}
//注:为了代码健壮性,上面的wait函数应该assert检测返回值
(3)条件变量等待函数的另外一种,指定最长等待时间,超过这个时间,即使没有条件信号到达,也从函数_wait中返回ETIMEOUT:
原型: int pthread_cond_timewait(pthread_cond_t*, pthread_mutex_t*, const struct timespec*);
pthread_mutex_lock(&mutex);
int ret = pthread_cond_timewait(&cond,&mutex,&time);
if(ret == ETIMEOUT) { /*未收到信号,超时*/}
else { /*收到条件信号*/}
(4)条件信号发送函数
int pthread_cond_signal(pthread_cond_t*);
int pthread_cond_broadcast(pthread_cond_t*);
(5)销毁条件变量,在GUN/Linux中,并没有资源真正的被分配给条件变量,destroy只是简单的检查是否还有线程在等待这个条件变量;
int pthread_cond_destory(pthread_cond_t*);
9.线程信号量
涉及类型: sem_t
涉及函数:sem_init, sem_destory, sem_getvalue;
信号量也就是操作系统所应用到的PV原子操作,广泛应用于线程/进程的同步和互斥,信号量本质是一个非负的整数计数器;
/*--PV原子操作的原理: 整数计数器sem, 一次p操作使sem减1,一次v操作使sem加1; 当sem大于等于0时, 线程拥有公共资源访问权,sem小于0时,该线程将阻塞直至sem>=0 */
PV原子操作用于"互斥" or "同步"操作;
(三)阻塞与非阻塞函数,调用函数后,代码是否阻塞在此等待函数返回?
单线程+阻塞函数, 多线程+阻塞函数 ?
pthread_join / pthread_mutex_lock / pthread_mutex_trylock / pthread_cancel / pthread_wait
_mutex_trylock: 如果互斥已经锁定,不会阻断;
_mutex_lock : 如果互斥没有锁定,不阻断;如果互斥已经锁定,则阻塞在此;
-
总结: linux下的多线程API (POSIX线程)
2011-02-09 18:36:00POSIX线程:一个完成的线程/进程包括三部分,代码+数据+内存栈;子线程和子进程在被创建的时候,对于fork()创建子进程,三部分都要复制一份,数据包括比如文件描述符,虚拟内存,子进程关闭文件描述符不会影响父进程中的描述...POSIX线程:
一个完成的线程/进程包括三部分,代码+数据+内存栈;子线程和子进程在被创建的时候,
对于fork()创建子进程,三部分都要复制一份,数据包括比如文件描述符,虚拟内存,子进程关闭文件描述符不会影响父进程中的描述符;
对于pthread_create()创建子线程的时候,只有内存栈被复制,其他的部分(代码,数据都是共享的),如果一个线程改变了某变量的值,其他所有的线程都调用的是改变之后的值;
头文件#include <pthread.h>
编译参数: -lpthread
(一)涉及到的类型:
pthread_t, pthread_attr_t, pthread_cond_t, pthread_mutexattr_t, void* (*)(void*),
(二)涉及到的函数:
pthread_cancel,pthread_wait,
pthread_create, pthread_self, pthread_detach, pthread_join, pthread_exit,
pthread_mutex_lock, pthread_mutex_trylock, pthread_mutex_unlock, pthread_mutex_destory; //参数都是pthread_mutex_t*
int pthread_attr_init(pthread_attr_t*), int pthread_attr_destory(pthread_attr_t*),
int pthread_attr_setdetachstatus(pthread_attr_t*,int); //设置属性
int pthread_attr_getdetacstatus(const pthread_attr_t*, int*); //获取属性
pthread_cond_wait,pthread_cond_timewait,pthread_cond_signal,pthread_cond_broadcast,pthread_cond_destory;
1.设置线程属性:
一个线程属性对象,(例如pthread_attr_t attr_test),可以创建很多线程,创建线程之后没有必要保持此对象;
在线程的所有属性中,最重要的是分离属性(detach status),一个线程可以是等待线程(joinable thread)or分离线程(detach thread),默认是joinable thread;
对于一个非分离(joinable)线程, 类似于进程中的Zombies进程,joinbale thread在退出后,资源不会被立刻释放,直到被thread_join获取它的返回值;
对于一个分离(detach)线程在退出之后, 资源会被立刻释放, 其他线程无法获悉其返回值;
代码例子,main线程中:
pthread_attr_t attr; //(1)创建变量:
phtread_attr_init(&attr); //(2)初始化此属性变量;
pthread_setdetachstatus(&attr,PTHREAD_CREATED_DETACHED);//(3)
pthread_create(&t,&attr,&function,NULL);//(4)创建线程;
pthread_destory(&attr);//(5)创建完后,即可销毁此属性变量;
pthread_join(t,NULL); //错误!分离的线程无法被join
return; //main线程结束
2.创建线程:
int pthread_create(pthread *thread, pthread_attr_t *attr, void* (*start_routine)(*void), void* arg);
其中pthread_attr_t*和void* arg可设置为NULL;
3.获取线程的pid:
pthread_t pthread_self(void);
4.线程分离:
int pthread_detach(pthread_t thread);
线程设置为分离, 当此线程退出时, 不会向任何其他线程传递返回值, 分离的线程无法被pthread_join();
例子,把线程分离:
void* myThread(void* param)
{
pthread_detach(pthread_self());
pthread_exit(param);
}
5.线程等待(加入):
pthread_join(pthread thread, void **status);
main创建n个线程,无法确定main一定在所有线程都退出后再退出,所以要让main线程等待所有子线程退出后,再退出;
调用者将等待第一参数标识的线程退出后,才能退出; 第二参数保存函数的返回值;
6.线程退出和取消:
int pthread_exit(void* retval);
线程内调用,调用线程退出,参数是线程函数的返回值, 或者可以把参数设为NULL;
线程退出有三种方式,1线程函数返回,2调用pthread_exit,3调用pthread_cancel
一个线程可以请求中止另一个线程,只需调用pthread_cancel(pid)即可;被cancel的线程,可以被pthread_wait(pid),此wait函数会释放线程占用的资源,除非是脱离(detach)线程;
线程是否可以被取消(cancel)是线程的一个属性,类似"分离"属性.线程调用pthread_setcancelstatus()可以实现自身是否可被cancel; 注意区别pthread_attr_setXXX函数组;
pthread_setcancelstatus(PTHREAD_CALCEL_DISABLE,NULL);//调用线程不可被取消!
pthread_setcancelstatus(PTHREAD_CALCEL_ENABLE,NULL);//若第二参数不为NULL,则会存储线程前一个状态的可否取消状态;
在一般应用中,pthread_setcancelstatus可以设置一个代码范围,在此范围内线程不可被取消,比如银行转帐,需要两步操作,A账户扣除,B账户增加,在这过程中不希望线程被取消的;
7.互斥体 mutex:
//初始化
int pthread_mutex_init(pthread_mutex_t* , pthread_mutexattr_t*);//第二个参数NULL是指向互斥锁类型的结构,代表默认属性的互斥锁;
初始化互斥锁属性的代码如下:
pthread_mutex_t mutex;
pthread_mutexattr_t attr;
phtread_mutexattr_init(&attr);
pthread_mutexattr_setkind_np(&attr,PTHREAD_MUTEX_ERRORCHECK_NP);//设置互斥体类型
pthread_mutex_init(&mutex, &attr); //初始化互斥体
pthread_mutex_destory(&attr); //变量pthread_mutexattr_t使用完成后,销毁
//注:带_np结尾的是指"non-portable",不可移植
或者更简单的初始化mutex方法:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //快速互斥
PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP: 递归互斥,lock一个已锁互斥体时,不会阻塞,而是记录次数,unlock必须有相同的次数;
PTHREAD_CHECKERROR_MUTEX_INITIALIZER_NP:纠错互斥,
//互斥体锁定
int pthread_mutex_lock(pthread_mutex_t* mutex); //成功返0
如果mutex已被线程A锁定后, 线程B尝试锁定mutex, 此时B线程会被阻塞,直到线程A解除了mutex的锁定, 同一时刻可能会有多个线程阻塞在一个互斥体上;
//互斥体死锁
互斥体的类型分为三种, fast mutex(默认类型), recursive mutex(递归互斥体), error-checking mutex(纠错互斥体),
产生死锁的可能情况: 两个线程,两个互斥体交叉锁定,会出现互相等待的情况,即死锁, 代码例子:
//非阻塞锁定互斥体:
int pthread_mutex_trylock(pthread_mutex_t* mutex);
/*成功锁定互斥体,返回0; 如果互斥体已被其他的线程锁定,返回EBUSY,不会阻塞*/
代码示例:
int ret = pthread_mutex_trylock(&mutex);
if(ret == EBUSY){
/*互斥体已被锁定,不阻塞*/
}
else if(ret == EINVAL){
assert(0); /*无效属性*/
}
else{
/*解锁成功, 进行关键代码操作*/
ret = pthread_mutex_unlock(&mutex);
}
//互斥体解锁
int pthread_mutex_unlock(pthread_mutex_t* mutex); //成功返0
//互斥体销毁
int pthread_mutex_destory(pthread_mutex_t*); //在程序末尾或其他位置,清理互斥锁,此时互斥体不能是locked状态;
8.线程条件变量
在a情况下让线程继续执行,在b情况下让线程阻塞,当阻塞条件改变时,所有被这个条件变量阻塞的线程都能被激活;
适用于的最简单情况,例如:线程thread1要循环执行从队列queue取出最后一个节点,线程thread2向queue增加节点;或者有更多的读写thread共同操作一个queue;
一般的做法是,所有线程while(1)循环,pthread_mutex_lock和_unlock保护一个代码区域,在此检查queue是否为空,,,为什么这种的效率不高?
(1)
int pthread_cond_init(pthread_cond_t*, NULL);//第二参数为NULL,则创建默认属性
(2)条件变量等待:
int pthread_cond_wait(pthread_cond_t*, pthread_mutex_t*);
设ThreadA,执行pthread_cond_wait函数之前,必须锁定互斥体, pthread_cond_wait以一个原子操作[解锁互斥体]并[开始等待信号], 然后线程相当于wait状态或sleep状态, 处理器会在此时处理其他的线程;
当某线程向ThreadA发送信号时, 线程ThreadA从pthread_cond_wait函数中返回并锁定互斥体;
// cond代码示例(a)
// work_load表示当前负载,当超过MAX_WORK_LOAD时,consumer线程调用process()函数进行work_load减; producer线程每次work_load++后发送一次信号;
pthread_mutex_lock(&mutex);
while(work_load < MAX_WORK_LOAD)
pthread_cond_wait(&cond,&mutex);
process(); //保护部分处理
pthread_mutex_unlock(&mutex);
// cond代码示例(b)
// queue表示一个任务队列构成的链表,consumer线程从链表中取出节点并处理,producer向链表增加节点
/*线程consumer*/
void* consumer(void*){
while(!exit){
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond); //互斥设为非锁定,等待条件信号
/*收到信号,从wait函数返回时,互斥设为锁定,queue--操作 */
pthread_mutex_unlock();
}
printf("recv exit single/n");
return;
}
/*线程producer*/
void* producer(void*){
pthread_mutex_lock(&mutex);
/*queue++*/
pthread_cond_signal(&cond);//或broadcast
pthread_mutex_unlock(&mutex);
return;
}
//注:为了代码健壮性,上面的wait函数应该assert检测返回值
(3)条件变量等待函数的另外一种,指定最长等待时间,超过这个时间,即使没有条件信号到达,也从函数_wait中返回ETIMEOUT:
原型: int pthread_cond_timewait(pthread_cond_t*, pthread_mutex_t*, const struct timespec*);
pthread_mutex_lock(&mutex);
int ret = pthread_cond_timewait(&cond,&mutex,&time);
if(ret == ETIMEOUT) { /*未收到信号,超时*/}
else { /*收到条件信号*/}
(4)条件信号发送函数
int pthread_cond_signal(pthread_cond_t*);
int pthread_cond_broadcast(pthread_cond_t*);
(5)销毁条件变量,在GUN/Linux中,并没有资源真正的被分配给条件变量,destroy只是简单的检查是否还有线程在等待这个条件变量;
int pthread_cond_destory(pthread_cond_t*);
9.线程信号量
涉及类型: sem_t
涉及函数:sem_init, sem_destory, sem_getvalue;
信号量也就是操作系统所应用到的PV原子操作,广泛应用于线程/进程的同步和互斥,信号量本质是一个非负的整数计数器;
/*--PV原子操作的原理: 整数计数器sem, 一次p操作使sem减1,一次v操作使sem加1; 当sem大于等于0时, 线程拥有公共资源访问权,sem小于0时,该线程将阻塞直至sem>=0 */
PV原子操作用于"互斥" or "同步"操作;
(三)阻塞与非阻塞函数,调用函数后,代码是否阻塞在此等待函数返回?
单线程+阻塞函数, 多线程+阻塞函数 ?
pthread_join / pthread_mutex_lock / pthread_mutex_trylock / pthread_cancel / pthread_wait
_mutex_trylock: 如果互斥已经锁定,不会阻断;
_mutex_lock : 如果互斥没有锁定,不阻断;如果互斥已经锁定,则阻塞在此;
-
Url重写篇视频------本讲将通过实例比较ASP.NET下的三种典型URL重写方案
2009-04-22 08:15:51创建数据驱动的 ASP.NET 网站时,通常会产生一个单个的网页,该网页基于查询字符串参数显示数据库数据的子集。例如,在设计电子商务站点时,您的任务之一便是允许用户浏览待售产品。为此,您可以创建一个名为 ... -
UNIX环境高级编程(中文第三版)(已经加书签)
2017-11-19 17:05:35书中除了介绍UNIX文件和目录、标准I/O库、系统数据文件和信息、进程环境、进程控制、进程关系、信号、线程、线程控制、守护进程、各种I/O、进程间通信、网络IPC、伪终端等方面的内容,还在此基础上介绍了众多应用... -
APUE第二版中文版和第三版英文版.zip
2017-10-09 17:39:56书中除了介绍UNIX文件和目录、标准I/O库、系统数据文件和信息、进程环境、进程控制、进程关系、信号、线程、线程控制、守护进程、各种I/O、进程间通信、网络IPC、伪终端等方面的内容,还在此基础上介绍了多个应用... -
入门学习Linux常用必会60个命令实例详解doc/txt
2011-06-09 00:08:45在Linux 和Unix系统上,所有文件都是作为一个大型树(以/为根)的一部分访问的。要访问CD-ROM上的文件,需要将CD-ROM设备挂装在文件树中的某个挂装点。如果发行版安装了自动挂装包,那么这个步骤可自动进行。在Linux... -
【Linux】fork()、vfork()、clone()的区别
2019-08-10 11:05:03Linux的用户进程不能直接被创建出来,因为不存在这样的API。它只能从某个进程中复制出来,再通过exec这样的API来切换到实际想要运行的程序文件。 复制的API包括三种:fork、clone、vfork。这三个API的内部实际都是...Linux的用户进程不能直接被创建出来,因为不存在这样的API。它只能从某个进程中复制出来,再通过exec这样的API来切换到实际想要运行的程序文件。
复制的API包括三种:fork、clone、vfork。这三个API的内部实际都是调用一个内核内部函数do_fork,只是填写的参数不同而已。
vfork,其实就是fork的部分过程,用以简化并提高效率。而fork与clone是区别的。fork是进程资源的完全复制,包括进程的PCB、线程的系统堆栈、进程的用户空间、进程打开的设备等。而在clone中其实只有前两项是被复制了的,后两项都与父进程共享。
在四项资源的复制中,用户空间是相对庞大的,如果完全复制则效率会很低。在Linux中采用的“写时复制”技术,也就是说,fork执行时并不真正复制用户空间的所有页面,而只是复制页面表。这样,无论父进程还是子进程,当发生用户空间的写操作时,都会引发“写复制”操作,而另行分配一块可用的用户空间,使其完全独立。这是一种提高效率的非常有效的方法。
而对于clone来说,它们连这些页面表都是与父进程共享,故而是真正意义上的共享,因此对共享数据的保护必须有上层应用来保证。
在linux源码中这三个调用的执行过程是执行fork(),vfork(),clone()时,通过一个系统调用表映射到sys_fork(),sys_vfork(),sys_clone(),再在这三个函数中去调用do_fork()去做具体的创建进程工作。
fork:
fork创建一个进程时,子进程只是完全复制父进程的资源,复制出来的子进程有自己的task_struct结构和pid,但却复制父进程其它所有的资源。例如,要是父进程打开了五个文件,那么子进程也有五个打开的文件,而且这些文件的当前读写指针也停在相同的地方。所以,这一步所做的是复制。这样得到的子进程独立于父进程, 具有良好的并发性,但是二者之间的通讯需要通过专门的通讯机制,如:pipe,共享内存等机制, 另外通过fork创建子进程,需要将上面描述的每种资源都复制一个副本。
这样看来,fork是一个开销十分大的系统调用,这些开销并不是所有的情况下都是必须的,比如某进程fork出一个子进程后,其子进程仅仅是为了调用exec执行另一个可执行文件,那么在fork过程中对于虚存空间的复制将是一个多余的过程。但由于现在Linux中是采取了copy-on-write(COW写时复制)技术,为了降低开销,fork最初并不会真的产生两个不同的拷贝,因为在那个时候,大量的数据其实完全是一样的。写时复制是在推迟真正的数据拷贝。若后来确实发生了写入,那意味着parent和child的数据不一致了,于是产生复制动作,每个进程拿到属于自己的那一份,这样就可以降低系统调用的开销。所以有了写时复制后呢,vfork其实现意义就不大了。
fork()调用执行一次返回两个值,对于父进程,fork函数返回子程序的进程号,而对于子程序,fork函数则返回零,这就是一个函数返回两次的本质。
在fork之后,子进程和父进程都会继续执行fork调用之后的指令。子进程是父进程的副本。它将获得父进程的数据空间,堆和栈的副本,这些都是副本,父子进程并不共享这部分的内存。也就是说,子进程对父进程中的同名变量进行修改并不会影响其在父进程中的值。但是父子进程又共享一些东西,简单说来就是程序的正文段。正文段存放着由cpu执行的机器指令,通常是read-only的。vfork:
vfork系统调用不同于fork,用vfork创建的子进程与父进程共享地址空间,也就是说子进程完全运行在父进程的地址空间上,如果这时子进程修改了某个变量,这将影响到父进程。
其次,子进程在vfork()返回后直接运行在父进程的栈空间,并使用父进程的内存和数据。这意味着子进程可能破坏父进程的数据结构或栈,造成失败。为了避免这些问题,需要确保一旦调用vfork(),子进程就不从当前的栈框架中返回,并且如果子进程改变了父进程的数据结构就不能调用exit函数。子进程还必须避免改变全局数据结构或全局变量中的任何信息,因为这些改变都有可能使父进程不能继续。
但此处有一点要注意的是用vfork()创建的子进程必须显示调用exit()来结束,否则子进程将不能结束,而fork()则不存在这个情况。
vfork也是在父进程中返回子进程的进程号,在子进程中返回0。
用vfork创建子进程后,父进程会被阻塞直到子进程调用exec,将一个新的可执行文件载入到地址空间并执行之或exit。vfork的好处是在子进程被创建后往往仅仅是为了调用exec执行另一个程序,因为它就不会对父进程的地址空间有任何引用,所以对地址空间的复制是多余的 ,因此通过vfork共享内存可以减少不必要的开销
再次强调:在使用vfork()时,必须在子进程中调用exit()函数调用,否则会出现:__new_exitfn: Assertion `l != ((void *)0)' failed 错误!而且,现在这个函数已经很少使用了!clone:
系统调用fork()和vfork()是无参数的,而clone()则带有参数。fork()是全部复制,vfork()是共享内存,而clone()是则可以将父进程资源有选择地复制给子进程,而没有复制的数据结构则通过指针的复制让子进程共享,具体要复制哪些资源给子进程,由参数列表中的clone_flags决决定。
fork不对父子进程的执行次序进行任何限制,fork返回后,子进程和父进程都从调用fork函数的下一条语句开始行,但父子进程运行顺序是不定的,它取决于内核的调度算法;而在vfork调用中,子进程先运行,父进程挂起,直到子进程调用了exec或exit之后,父子进程的执行次序才不再有限制;clone中由标志CLONE_VFORK来决定子进程在执行时父进程是阻塞还是运行,若没有设置该标志,则父子进程同时运行,设置了该标志,则父进程挂起,直到子进程结束为止。 -
linux下的 fork vfork和clone函数
2016-03-28 11:18:44Linux的用户进程不能直接被创建出来,因为不存在这样的API。它只能从某个进程中复制出来,再通过exec这样的API来切换到实际想要运行的程序文件。 复制的API包括三种:fork、clone、vfork。 这三个API的内部... -
用TCP/IP进行网际互联 第三卷:客户-服务器编程与应用(Linux/POSIX套接字版)--详细书签版
2012-10-12 14:44:573.5 一个创建并发进程的例子 20 3.5.1 一个顺序执行的C实例 20 3.5.2 程序的开发版本 21 3.5.3 时间分片 22 3.5.4 单线程的进程 23 3.5.5 使各进程分离 23 3.6 执行新的代码 24 3.7 上下文切换和协议软件... -
软考之软件设计师——操作系统知识
2020-10-27 17:32:56进程是程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的独立单位,由程序块(是程序执行时不可修改的部分)、进程控制块PCB(进程存在的唯一标识)和数据块(包括程序执行的数据,工作区,是进程可... -
学习笔记——Linux fork vfork exec clone的关系
2013-10-30 09:14:10Linux的用户进程不能直接被创建出来,因为不存在这样的API。它只能从某个进程中复制出来,再通过exec这样的API来切换到实际想要运行的程序文件。 复制的API包括三种:fork、clone、vfork。 这三个API的... -
Oracle DBA突击:帮你赢得一份DBA职位--详细书签版
2013-02-06 15:56:40狗会因为人随手丢出的一个东西而追逐,可能是一个骨头、一块肉、一个眼神,甚至是一个石头,警示一定要看清自己在追逐的东西。 目录 封面 -17 扉页 -16 版权 -15 序 -14 前言 -12 自我测试 -7 目录 -4 第一部分 ... -
超级有影响力霸气的Java面试题大全文档
2012-07-18 09:47:04与cgi的区别在于servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于servlet。... -
Oracle9i的init.ora参数中文说明
2008-11-07 20:14:11小数分隔符用来将一个数字的整数部分与小数部分分隔开。其格式是 <decimal_character><group_separator>。 值范围: 任何单字节字符, '+', '-', ', '>' 除外。 默认值: 从 NLS_TERRITORY 中获得 nls_sort: 说明: ... -
windows 内部原理(一)
2009-12-22 16:49:49Windows的启动是一个复杂的过程,从加载器(NTLDR或WinLoad)开始工作到Windows子系统准备就绪,中间经历了若干个复杂的步骤,包括内核和执行体的初始化,创建系统进程和线程,对象管理器初始化基本对象,I/O管理器... -
Linux环境变量与文件查找
2019-05-01 17:23:12默认情况下,当一个进程被创建时,除了创建过程中明确指定的话,它将继承其父进程的绝大部分环境设置。 打印环境变量信息的三个命令: set:显示当前Shell所有变量**,包括其内建环境变量、用户自定义变量及导... -
python线程类实例参数传递_Python | 多线程
2021-02-04 09:12:22threading模块线程简述线程(轻量级进程)与进程类似,不过它们是在同一个进程下执行的,并共享相同的上下文。可以将它们认为是在一个主进程或"主线程"中并行运行的一些"迷你进程"。线程包括开始、执行顺序和结束三... -
LINGO软件的学习
2009-08-08 22:36:50一个集部分可以放置于模型的任何地方,但是一个集及其属性在模型约束中被引用之前必须定义了它们。 2.3.1 定义原始集 为了定义一个原始集,必须详细声明: •集的名字 •可选,集的成员 •可选,集成员的属性 定义一... -
Windows服务实现Tcp服务器
2019-08-16 20:26:42前言 没有前言。 Windows服务介绍 Windows服务可以在系统启动时自动打开,如果需要在没有用户操作的情况下运行的程序,就可以创建Windows服务。...一个Windows服务包括三个部分: ... -
Linux namespace
2014-03-06 16:03:30Linux的用户进程不能直接被创建出来,因为不存在这样的API。它只能从某个进程中复制出来,再通过exec这样的API来切换到实际想要运行的程序文件。 复制的API包括三种:fork、clone、vfork。 这三个API的... -
NET高级调试(奋斗的小鸟)
2014-10-21 08:00:26除了系统应用程序域和共享应用程序域之外,程序集要么是被加载到默认应用程序域中,要么是被加载到显式创建的应用程序域中。当程序集被加载到某个应用程序域时,它将停留在这个应用程序域中,直到这个应用程序域被... -
如何查杀运行状态下的EXE、DLL病毒
2008-11-15 00:13:262、在进程中可以发现的双进程EXE病毒或木马程序,由于手工方式不能同时停掉两个进程,当我们手工掉其中一个进程后,另一个进程会将该进程重新启动。针对这种情况杀毒软件也无能为力,若两个都是非系统进程,我们... -
新版Android开发教程.rar
2010-12-14 15:49:11Android 是一个专门针对移动设备的软件集,它包括一个操作系统,中间件和一些重要的应用程序。 Beta 版 的 Android SDK 提供了在 Android 平台上使用 JaVa 语言进行 Android 应用开发必须的工具和 API 接口。 特性 ...