Linux中的进程和线程
Linux进程的创建-fork
fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值: 1)在父进程中,fork返回新创建子进程的进程ID; 2)在子进程中,fork返回0; 3)如果出现错误,fork返回一个负值;
在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID。我们可以通过fork返回的值来判断当前进程是子进程还是父进程。
示例一,
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int glob_count = 6;
char buf[] = "a write to stdout\n";
int main(int argc, const char * argv[]) {
int var;
int pid;
var = 88;
if(write(STDOUT_FILENO,buf,sizeof(buf) -1) != sizeof(buf) -1)
{
perror("fail to write");
return -1;
}
printf("before fork\n");
if((pid = fork()) < 0)
{
perror("fail to fork");
return -1;
}else if(pid == 0){
glob_count ++;
var ++;
}else{
sleep(2);
}
printf("pid = %d,glob = %d,var = %d\n",getpid(),glob_count,var);
return 0;
}
运行结果(macOS平台运行),
a write to stdout
before fork
pid = 4172,glob = 7,var = 89
pid = 4169,glob = 6,var = 88
Program ended with exit code: 0
示例二
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int glob_count = 6;
char buf[] = "a write to stdout\n";
int main(int argc, const char * argv[]) {
int var;
int pid;
var = 88;
printf("father:\n");
printf("&glob = %p\n",&glob_count);
printf("&var = %p\n",&var);
printf("__________________________________\n");
if((pid = fork()) < 0)
{
perror("fail to fork");
return -1;
}else if(pid == 0){
printf("child var value not change:\n");
printf("&glob = %p\n",&glob_count);
printf("&var = %p\n",&var);
glob_count ++;
var ++;
printf("__________________________________\n");
printf("child var value change:\n");
printf("&glob = %p\n",&glob_count);
printf("&var = %p\n",&var);
}
return 0;
}
运行结果,
father:
&glob = 0x100001030
&var = 0x7fff5fbff79c
__________________________________
child var value not change:
&glob = 0x100001030
&var = 0x7fff5fbff79c
__________________________________
child var value change:
&glob = 0x100001030
&var = 0x7fff5fbff79c
Program ended with exit code: 0
Linux进程的创建-vfork
- fork要拷贝父进程的数据段;而vfork则不需要完全拷贝父进程的数据段,在子进程没有调用exec和exit之前,子进程与父进程共享数据段
- fork不对父子进程的执行次序进行任何限制;而在vfork调用中,子进程先运行,父进程挂起,直到子进程调用了exec或exit之后,父子进程的执行次序才不再有限制。
示例,
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, const char * argv[]) {
int count;
int pid;
count = 88;
if((pid=vfork()) < 0){
perror("fail to vfork");
exit(-1);
} else if (pid == 0){
count++;
} else {
//在父进程中,返回子进程的id,pid 为子进程的id
printf("child pid = %d \n",pid);
}
printf("pid = %d ,count = %d \n",getpid(),count);
exit(0);
}
运行结果,
pid = 4466 ,count = 89
child pid = 4466
pid = 4463 ,count = 89
Program ended with exit code: 0
Linux线程的实现
线程机制是现代编程技术中常用的一种抽象。该机制提供了在同一程序内共享内存地址空间运行的一组线程。这些线程还可以共享打开的文件和其他资源。
Linux实现线程的机制非常独特。内核并没有准备特别的调度算法或是定义特别的数据结构来表征线程。相反,线程仅仅被视为一个与其它进程共享某些资源的进程。每个线程都拥有唯一隶属于自己的task_struct,所以在内核中,他看起来就像一个普通的进程(只是该进程和其他一些进程共享某些资源,如地址空间)。
在其他的操作系统中,相较于重量级的进程,线程被抽象成一种耗费较少资源,运行迅速的执行单元。而对于Linux来说,它只是一种进程间共享资源的手段。举个例子来说,假如我们有一个包含四个线程的进程,在提供专门线程支持的系统中,通常会有一个包含指向四个不同线程的指针的进程描述符。该描述符负责描述像地址空间,打开的文件这样的共享资源。线程本身再去描述它独占的资源。相反,Linux仅仅创建四个进程并分配四个普通的task_struct结构。建立这四个进程时制定他们共享某些资源,这是相当高雅的做法。
线程的创建和普通进程的创建类似,只不过在调用clone()的时候需要传递一些参数标志来知名需要共享的资源。
clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0);
上面的代码产生的结果和调用fork差不多,只是父子俩共享地址空间、文件系统资源、文件描述符和信号处理程序。换个说法就是,新建的进程和他的父进程就是流行的所谓线程。 对比一下,一个普通的fork()实现是:
clone(SIGCHLD, 0);
而vfork()的实现是:
clone(CLONE_VFORK | CLONE_VM | SIGCHLD, 0);
传递给clone()的参数标志决定了新创建进程的行为方式和父子进程之间共享的资源种类。
Linux 线程模型的比较:LinuxThreads 和 NPTL
http://www.ibm.com/developerworks/cn/linux/l-threading.html
http://blog.csdn.net/adcxf/article/details/3940982
参考:
http://hustpawpaw.blog.163.com/blog/static/18422832420134694142375/
http://blog.chinaunix.net/uid-26833883-id-3222794.html
http://blog.csdn.net/feiyangyangfei/article/details/12577661
=======END=======