进程与线程(二)

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);
     returnvoid *)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:线程退出返回的指针;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值