Linux下3种产生进程或线程的函数对比
动机
为了探究fork()、vfork()、pthread()的区别,以及每个函数创建子进程后与父进程的联系。
进程具有的特征:
并发性,动态性,独立性,结构性。
动态性:进程是程序的一次执行过程,是有生命周期的,是动态产生和消亡的
独立性:进程是操作系统进行资源分配和调度的一个独立单位
结构性:进程由程序,数据和进程控制块三部分组成
fork
使用fork函数创建进程,
fork_test.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(void) {
int count = 1;
int child;
child = fork( );
if(child < 0) {
perror("fork error : ");
}
else if(child == 0) {
// fork return 0 in the child process because child can get hid PID by getpid( )
printf("This is son, his count is: %d (%p). and his pid is: %d\n", ++count, &count, getpid());
}
else {
// the PID of the child process is returned in the parent thread of execution
wait(); // 保证父进程在后面执行 printf();
printf("This is father, his count is: %d (%p), his pid is: %d\n", count, &count, getpid());
}
return EXIT_SUCCESS;
}
输出
This is son,his count is:2 (0x7ffcac4a29b8).and his pid is :16565
This is father,his count is:1 (0x7ffcac4a29b8),his pid is:16564
结果分析
从输出的结果中可以得出父进程和子进程的Pid是不同的,内存资源count复制l了。子进程在没有执行count++前,其实子进程和父进程指向同一块内存区域。当子进程改变了父进程的变量的时候,会通过copy_on_write建立一个新的副本,将父进程的内容复制一份给子进程。
所以其实父子进程的资源都独立了。
vfork
vfork1_test.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(void) {
int count = 1;
int child;
printf("Before create son, the father's count is:%d\n", count);
if((child = vfork())< 0) {
perror("vfork error : ");
}
else if(child == 0) {
// vfork return 0 in the child process because child can get hid PID by getpid( )
printf("This is son, his count is: %d (%p). and his pid is: %d\n", ++count, &count, getpid());
//sleep(2);
exit(0);
}
else {
// the PID of the child process is returned in the parent鈥檚 thread of execution
printf("After son, This is father, his count is: %d (%p), his pid is: %d\n", count, &count, getpid());
exit(0);
}
return EXIT_SUCCESS;
}
输出
Before create son, the father's count is : 1
This is son, his count is: 2 (0x7fffla7c9408). and his pid is: 16804
After son, This is father, his count is: 2122152597 (0x7fffla7c9408), his pid is: 16804
vfork2_test.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
int count = 1;
int child;
printf("Before create son, the father's count is : %d\n", count);
if((child = vfork())< 0) {
perror("fork error : ");
}
else if(child == 0) {
// vfork return 0 in the child process because child can get hid PID by getpid( )
printf("This is son, his count is: %d (%p). and his pid is: %d\n", ++count, &count, getpid());
sleep(2);
}
else {
// the PID of the child process is returned in the parent鈥檚 thread of execution
printf("After son, This is father, his count is: %d (%p), his pid is: %d\n", count, &count, getpid());
}
return EXIT_SUCCESS;
}
输出
Before create son, the father's count is : 1
This is son, his count is: 2 (0x7ffc2885b038). and his pid is: 10354
//间隔两秒
After son, This is father, his count is: 2 (0x7ffc2885b038), his pid is: 10353
结果分析
vfork也是创建一个子进程,但是子进程共享父进程的空间。创建子进程后,父进程将会阻塞,直到子进程执行了exec()或者exit()。
pthread
pthread1Test.c
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *tfn(void *arg)
{
printf("我是线程,我的进程ID = %lu,我的ID = %d\n", getpid(), pthread_self());
return NULL;
}
int main(void)
{
pthread_t tid;
pthread_create(&tid, NULL, tfn, NULL);
sleep(1);
printf("我是进程,我的进程ID = %d\n", getpid());
return 0;
}
输出
我是**线程**,我的进程ID = 25201,我的ID = -1707608320
我是进程,我的进程ID = 25201
结果分析
pthread_create(pthread_t * thread, const pthread_arrt_t* attr,void*(*start_routine)(void *), void* arg);
函数分析:
1.thread参数是新线程的标识符,为一个整型。
2.attr参数用于设置新线程的属性。给传递NULL表示设置为默认线程属性。
3.start_routine和arg参数分别指定新线程将运行的函数和参数。start_routine返回时,这个线程就退出了
4.返回值:成功返回0,失败返回错误号。
从结果中可以看出create函数创建了一个线程,并且指定新线程运行的函数tfn成功了。
pthread2Test.c
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
void *tfn(void *arg)
{
int i;
i = (int)arg;
sleep(i); //通过i来区别每个线程
printf("我是第%d个线程,我的进程ID = %d, 我的线程ID = %lu\n", i ,getpid(), pthread_self());
return NULL;
}
int main(int argc, char *argv[])
{
int n = 5, i;
pthread_t tid;
for (i = 0; i < n; i++) {
pthread_create(&tid, NULL, tfn, (void *)i);
//将i转换为指针,在tfn中再强转回整形。
}
sleep(n);
printf("我是main函数,我的进程ID = %d, 我的线程ID = %lu\n", getpid(), pthread_self());
return 0;
}
输出
我是第0个线程,我的进程ID = 25394, 我的线程ID = 140662188218112
我是第1个线程,我的进程ID = 25394, 我的线程ID = 140662154659584
我是第2个线程,我的进程ID = 25394, 我的线程ID = 140662121101056
我是第3个线程,我的进程ID = 25394, 我的线程ID = 140662087542528
我是第4个线程,我的进程ID = 25394, 我的线程ID = 140662053984000
我是main函数,我的进程ID = 25394, 我的线程ID = 140662196483904
结果分析
create函数中arg是线程运行函数的入参,for循环中i入参不断增加,所以tfn函数中收到的i不同,睡眠时间也不同。
pthread3Test.c
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
void *tfn(void *arg)
{
int i;
i = (int)arg;
int j;
for (j=0;j<100;j++)
printf("我是第%d个线程,我的j = %d\n", i ,j);
printf("我是第%d个线程,我的进程ID = %d, 我的线程ID = %lu\n", i ,getpid(), pthread_self());
return NULL;
}
int main(int argc, char *argv[])
{
int n = 5, i;
pthread_t tid;
for (i = 0; i < n; i++) {
pthread_create(&tid, NULL, tfn, (void *)i);
//将i转换为指针,在tfn中再强转回整形。
}
sleep(n);
printf("我是main函数,我的进程ID = %d, 我的线程ID = %lu\n", getpid(), pthread_self());
return 0;
}
输出
...
...
我是第4个线程,我的j = 60
我是第4个线程,我的j = 61
我是第4个线程,我的j = 62
我是第4个线程,我的j = 63
我是第3个线程,我的j = 11
我是第4个线程,我的j = 64
我是第2个线程,我的j = 19
我是第2个线程,我的j = 20
我是第2个线程,我的j = 21
我是第2个线程,我的j = 22
我是第2个线程,我的j = 23
我是第1个线程,我的j = 5
我是第1个线程,我的j = 6
我是第1个线程,我的j = 7
我是第1个线程,我的j = 8
我是第1个线程,我的j = 9
我是第1个线程,我的j = 10
我是第2个线程,我的j = 24
我是第2个线程,我的j = 25
...
...
结果分析
可以从线程运行的tfn函数中for循环中打印情况看出,并不是第0个线程全部执行完才会执行下一个线程。所以线程并不是串行,而是并行。
pthread4Test.c
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
void *tfn(void *arg)
{
int i;
i = (int)arg;
int j;
for (j=0;j<100;j++)
printf("我是第%d个线程,我的j = %d\n", i ,j);
printf("我是第%d个线程,我的进程ID = %d, 我的线程ID = %lu\n", i ,getpid(), pthread_self());
return NULL;
}
int main(int argc, char *argv[])
{
int n = 5, i;
pthread_t tid;
for (i = 0; i < n; i++) {
pthread_create(&tid, NULL, tfn, (void *)i);
//将i转换为指针,在tfn中再强转回整形。
}
// sleep(n);
printf("我是main函数,我的进程ID = %d, 我的线程ID = %lu\n", getpid(), pthread_self());
return 0;
}
输出
我是第0个线程,我的j = 0
我是第0个线程,我的j = 1
我是第3个线程,我的j = 0
我是第3个线程,我的j = 1
我是第4个线程,我的j = 0
我是第4个线程,我的j = 1
我是第4个线程,我的j = 2
我是第4个线程,我的j = 3
我是第4个线程,我的j = 4
我是第4个线程,我的j = 5
我是第0个线程,我的j = 2
我是第2个线程,我的j = 0
我是第2个线程,我的j = 1
我是第2个线程,我的j = 2
我是main函数,我的进程ID = 25555, 我的线程ID = 139957369931584
结果分析
Test4和Test3相比较,Test4在main函数中将sleep注释,把等待线程运行这个过程取消了。从结果上看线程还未执行完就结束。
本篇的linux下创建子进程或线程的函数对比结束,下一篇将介绍clone