产生进程或线程的3个函数

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

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值