UNIX环境高级编程读书笔记___线程创建函数(1)

本文由线程的创建和退出Linux中man操作手册用法线程的堆和栈加之自己的理解整合而来。

一、创建线程

1.1 调用接口

自问:1为什么不直接使用整型变量记录线程id呢,就和pid一样不是更好吗?2restrict关键字是什么意思? 

先回答1,linux的发展历史UNIX操作系统有很多版本,Linux 2.4.22使用无符号长整型数表示pthread_t类型。Solaris把pthread_t数据类型表示为无符号整型数。FreeBSD和Mac OS X 10.3用一个指向pthread结构的指针来表示pthread_t数据类型种类繁多,没有统一的规范,这就给代码的可移植性带来了挑战。用结构pthread_t数据类型的后果是不能用一种可移植的方式打印该数据类型的值。在程序调试过程中打印线程ID有时候是非常有用的。我觉得,这就和大小端模式一样无聊,虽然今天的局面是历史原因,但为什么不在当时开个会讨论一套标准出来不是更好吗?也许这就是计算机世界的魅力吧!

再回答2,关键字restrict是什么意思?

通过翻译,我们看到了,它的意思是限定,这是英语告诉我们的;但是来到计算机世界,这个关键字到底限定什么呢? restrict关键字,表明指针所指向的内容,不能通过除此指针之外的其他直接或间接的方式修改。这就表明内容只唯一允许此指针修改,本要求是很强的了。

还有一点特别坑的地方就是——在创建函数返回之前新线程可能已经开始运行了。创建未完成,新线程已开始运行,这也是没谁了。

总结一下就是:

  • 线程标识符是pthread_t类型的
  • 关键字restrict在pthread_create中被使用
  • 在创建函数返回之前新的线程可能已经开始运行了

1.2 打印输出线程tid

想打印输出线程tid会比进程pid麻烦的多。如果进程创建函数的特性是返回之后,函数线程才开始执行,那么我们就可以从形参tidp中把线程的信息取出来,经过分析得到tid。事实却不是这样,如果我们非要这么做的话,新线程看到的可能是初始世化的形参,这个内容是不正确的线程tid。

介绍一个新的API,pthread_self(),功能是返回调用该函数的线程的id。在此不过多赘述实现思路,直接看实现代码吧。

1.3 代码实现

头文件的引入以及自主实现函数的声明:

#include<stdio.h> //printf()
#include<unistd.h> //getpid() 
#include<pthread.h> //pthread_create() pthread_self()
#include<stdlib.h>  //exit()
#include<assert.h>  //assert()

void print_ids(const char *buff);   //打印输出线程id
void* thr_fn(void*arg); //线程

main主线程 

int main()
{
    pthread_t tid;
    int err = pthread_create(&tid, NULL, thr_fn, NULL);
    assert(0 == err);
    print_ids("main");
    sleep(2);    //确保主线程在函数线程结束后结束
    exit(0);
}

其他  

void print_ids(const char *buff)    //打印输出线程信息
{
    pid_t pid = getpid();   //获得进程编号
    pthread_t tid = pthread_self(); //获得线程编号
    printf("%6s : pid = %u, tid = %u (0x%x)\n", buff, pid, tid, tid);
}

void* thr_fn(void*arg)  //函数线程
{
    print_ids("thr_fn");
    return NULL;
}

1.4 运行结果

小实验是完成了,但是还是有问题问自己, 从实验结果中观察到了什么?看图说话:①main主线程和thr_fn函数线程对应的pid相同——这是很自然的,因为它们是同一进程的不同线程,进程id肯定相同;②main主线程和thr_fn函数线程对应的tid不同,反复强调一点:tid的类型是pthread_t,到底是否是整型是未知的

二、man操作手册

2.1 括号内数字的含义

我的建议是遇见了不熟悉的Linux API,不要百度或谷歌,直接查看Linux操作手册就可以了,最权威,最全面,能在最短的时间提高自己。 

2.2 API-pthread_seft

可移植

上图是我从release 3.22 of the Linux 截取出来的,一共分四段话。第一段大意是说表述描述一个线程标识符的数据类型是自由的,这就导致使用C语言中的==判断是否相等变得不可移植;第二段大意是说线程标识符是不透明的,离开进程的大环境,一个线程并不能被识别;第三段大意是说线程标识符仅仅保证在进程内是独一无二的,而且可以被重复利用;第四段大意是说pthread_self返回的thread ID和pthread_self返回的kernel thread ID不是用一个事物。我们还是要进一步了解一下gettid()是什么。

2.3 API-gettid()

不可移植 Linux特有

上图是我从release 3.22 of the Linux 截取出来的,主要告诉读者:gettid() 是Linux特有的,不应该将其用在可移植的程序上,直接返回调用者线程id,这次真的是一个整型了。在描述中,又出现黑体字强调的clone() 函数,有必要去刨根问底了解一下。

2.4 API-clone()

Linux实现创建线程最终转调的函数,类似于fork(),注意比较两者的差别。

上图是我从release 3.22 of the Linux 截取出来的,文字很长,但是表达的意思很清楚。首先,clone函数是一个库函数,用于创建一个子进程,但是创建出来的子进程会和父进程共享很多资源,例如内存、文件描述符表、信号处理器表。因为内存是共享的,所以子进程必须要有自己独立的栈,child_stack就是做这个事情的。还需要有cb,函数指针+参数列表:fn和arg做这个事情的。flag主要是通过位运算控制共享资源的内容的。此处所指代的子进程就是我们理解的线程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值