Linux中的线程机制十分特殊,从内核的角度来说并没有线程的概念,故没有为线程单独定义数据结构,通过clone()创建的Linux线程,仅被看做一个与其他进程共享某些资源的特殊进程而已;
Linxu中把线程分为内核线程,内核支持的用户线程和线程库支持的用户线程等3种类型;
1、内核线程:内核线程的创建于撤销有内核内部的需求决定,内核线程没有用户地址空间,它共享内核的正文段和内核全局数据,具有自己的内核栈,内核线程的调度由于不需要经过CPU状态的转换,所以内核线程间的上下文切换比在用户线程间快得多;
2、内核支持的用户线程:有clone()函数创建的线程;
3、线程库支持的用户线程:用户线程是通过线程库实现的,内核不参与调度,它可以在没有内核参与下创建,释放和管理,线程库提供同步和调度的方法。内核通过调度进程,进程通过线程库调度线程;
一、内核支持的用户线程
通过clone()函数进行创建;
/*int clone(int (*fn)(void *arg),void *stack,int flags,void *arg)
fn:函数指针,线程标识符
flags:
CLONE_VM:父进程,子进程共享进程空间;
CLONE_FS:父进程,子进程共享文件系统信息;
CLONE_FILES:父进程,子进程共享打开的文件;
CLONE_SIGGHLD:子进程终结或者暂停时给父进程发信号;
CLONE_SIGHAND:父子进程共享信号处理函数;
CLONE_PID:父子进程共享进程标识符;
CLONE_VFORK:父进程在子进程释放空间时被唤醒;
stack:线程所使用的内核栈;
arg:传给fn的void*类型的参数
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <pthread.h>
#define STACK_SIZE 1024*1024*8
void *func(void * arg)
{
printf("arg = %d\n",*(int *)arg);
printf("PID = %d\n",getpid());
sleep(50);
return (void *)0;
}
int main()
{
int clone_flag,arg;
char *stack;
int retval;
arg = 1;
clone_flag = CLONE_VM|CLONE_SIGHAND|CLONE_FS|CLONE_FILES;
printf("pid = %d\n",getpid());
stack = (char *)malloc(STACK_SIZE);
//because the increase of stack address is from highaddress to low address
stack += STACK_SIZE;
retval = clone((void*)func,stack,clone_flag,(void *)&arg);
printf("retval = %d\n",retval);
return 1;
}
~
二、线程库支持的用户线程
1、POSIX线程
POSIX线程包提供一组函数,用来创建,删除和同步同一进程内的线程,使用时需要加入头文件pthread.h, 编译时需要使用-pthread来链接线程库
2、线程的创建
int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict attr,void *(*start_rtn)(void),void *restrict arg);
//tidp:要创建的线程的线程id
//attr:为新线程定义不同的属性(如栈的大小)
//start_rtn:指定执行的函数
//arg:传递给函数的参数
pthread_create与clone的区别在于:clone()创建的是一个内核支持的用户线程,对内核是可见的且由内核调度,而pthread_create创建一个线程库支持的用户线程,对内核不可见,由线程库调度;
线程的标识符:与进程类似,线程也有线程标识符,可通过pthread_self()获取;
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *create(void *arg)
{
printf("new thread...\n");
printf("thread id = %u\n",(unsigned int)pthread_self());
printf("thread pid = %d\n",getpid());
return (void*)0;
}
int main()
{
pthread_t tid;
int error;
printf("Main thread is starting...\n");
error = pthread_create(&tid,NULL,create,NULL);
if(error != 0)
{
printf("thread is not created...\n");
return -1;
}
printf("main pid = %d\n",getpid());
sleep(1);
return 0;
}
3、线程等待
int pthread_join(pthread_t thread,void **status);
//pthread:指定等待的线程
//status:指向指针的指针,用于指定返回值
//函数返回值:0表示成功,非0表示失败
4、线程同步
(1)信号量
#inclued <semaphore.h>
int sem_init(sem_t *sem,int pthread,unsigned int value);
//初始化信号量
//sem:为指向信号量结构的一个指针
//pshared:不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享
//value:给出了信号量的初始值
int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);
int sem_destroy(sem_t *sem);
可以参考 信号量-百度百科
(2)互斥量
#include<pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *mutexattr);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
通过使用互斥量,使用两个线程同时读取两个文件并统计单词个数:
#include <stdio.h>
#include <pthread.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
pthread_mutex_t counter_clock;
int total_words = 0;
void *count_words(void *f)
{
char *filename = (char *)f;
FILE *fp;
int c,prevc = '\0';
printf("file:%s\n",filename);
if((fp = fopen(filename,"r")) != NULL)
{
while((c = getc(fp)) != EOF)
{
if(!isalnum(c) && isalnum(prevc))
{
pthread_mutex_lock(&counter_clock);
total_words++;
pthread_mutex_unlock(&counter_clock);
}
prevc = c;
}
fclose(fp);
}
else
{
perror(filename);
}
return NULL;
}
int main(int argc,char *argv[])
{
pthread_t thread1,thread2;
void *thread_result1,*thread_result2;
int error;
if(argc != 3)
{
printf("Usage:%s file1 file2\n",argv[0]);
exit(1);
}
printf("reading...\n");
error = pthread_create(&thread1,NULL,count_words,(void *)argv[1]);
if(error != 0)
{
printf("create thread failed...\n");
return -1;
}
error = pthread_create(&thread2,NULL,count_words,(void *)argv[2]);
if(error != 0)
{
printf("create thread failed...\n");
return -1;
}
pthread_join(thread1,&thread_result1);
pthread_join(thread2,&thread_result2);
printf("total words:%d\n",total_words);
pthread_mutex_destroy(&counter_clock);
return 0;
}
5、线程终止
线程的正常退出包括以下几种
- 线程只是从启动例程返回,返回值是线程中的退出码;
- 线程被另一个线程终止;
- 线程自己调用pthread_exit()终止;
void pthread_exit(void *rval_prt);
//rval_ptr:线程退出返回的指针;