linux下的简单进程创建

1. 进程是资源分配的最小单位,而线程是调度的最小单位。
2. 进程有独立的地址空间,拥有自己的代码段数据段堆栈段,而线程只有独立的堆栈段;
3. 进程拥有多种通信方式,而线程之间通信只有通过全局变量或者创建时传值。

之所以要使用多线程
1.和进程相比,它是一种非常节约的多任务操作方式。启动一个新进程,必须分配给它独立的地址空间
建立众多的数据表来维护它的代码段数据段和堆栈段,而创建一个进程中的线程,他们公用相同的地址
空间,共享大部分数据,启动一个线程所话费的时间远远小于一个进程。
2.线程间通信非常方便。不同的进程中通信,不仅费时,而且很不方便,线程则不然,由于同一个进程下的
线程共享数据空间,所以一个线程的数据可以直接为其他线程使用。
线程的创建方法,使用pthread_crpthread_t在头文件/usr/include/bits/pthreadtypes.h中定义:
  typedef unsigned long int pthread_t;
  它是一个线程的标识符。函数pthread_create用来创建一个线程,它的原型为:
  extern int pthread_create __P ((pthread_t *__thread, __const pthread_attr_t *__attr,
  void *(*__start_routine) (void *), void *__arg));
  第一个参数为指向线程标识符的指针,第二个参数用来设置线程属性,第三个参数是线程运行函数的起始地址,最后一个参数是运行函数的参数。这里,我们的函数thread不需要参数,所以最后一个参数设为空指针。第二个参数我们也设为空指针,这样将生成默认属性的线程。对线程属性的设定和修改我们将在下一节阐述。当创建线程成功时,函数返回0,若不为0则说明创建线程失败,常见的错误返回代码为EAGAIN和EINVAL。前者表示系统限制创建新的线程,例如线程数目过多了;后者表示第二个参数代表的线程属性值非法。创建线程成功后,新创建的线程则运行参数三和参数四确定的函数,原来的线
程则继续运行下一行代码。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>

struct menber
{
    int a;
    char *s;
};

/*
 * 线程执行函数
 * */
void *create(void *arg)
{
    struct menber *temp;
    temp=(struct menber *)arg;
    printf("menber->a = %d  \n",temp->a);
    printf("menber->s = %s  \n",temp->s);
    return (void *)0;
}

/*
 * 程序入口
 * */
int main(int argc,char *argv[])
{
    pthread_t tidp;
    int error;
    struct menber *b;

	/*为结构体指针b分配内存并赋值*/
    b=(struct menber *)malloc( sizeof(struct menber) );
    b->a = 4;
    b->s = "zieckey";
    
	/*创建线程并运行线程执行函数*/
    error = pthread_create(&tidp, NULL, create, (void *)b);
    if( error )
    {
        printf("phread is not created...\n");
        return -1;
    }

    sleep(1); //进程睡眠一秒使线程执行完后进程才会结束

    printf("pthread is created...\n");
    return 0;
}


由运行结果可以看到,线程打印出了在主函数中赋值的结构体,将这个结构体的值传入了线程中使用。

Linux中,默认情况下是在一个 线程被创建后,必须使用此函数对创建的线程进行资源回收,但是可以设置Threads attributes来设置当一个线程结束时,直接回收此线程所占用的 系统资源,详细资料查看Threads attributes。
其实在Linux中,新建的线程并不是在原先的进程中,而是系统通过一个 系统调用clone()。该系统copy了一个和原先进程完全一样的进程,并在这个进程中执行线程函数。不过这个copy过程和fork不一样。 copy后的进程和原先的进程共享了所有的 变量,运行环境。这样,原先进程中的 变量变动在copy后的进程中便能体现出来。

pthread_join的应用

pthread_join使一个线程等待另一个线程结束。
代码中如果没有 pthread_join 主线程会很快结束从而使整个进程结束,从而使创建的线程没有机会开始执行就结束了。加入 pthread_join后, 主线程会一直等待直到等待的线程结束自己才结束,使创建的线程有机会执行。
所有线程都有一个线程号,也就是 Thread ID。其类型为 pthread_t。通过调用 pthread_self()函数可以获得自身的线程号。
测试代码:
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>

/*
* 线程的执行函数
* */
void *thread(void *str)
{
    int i;
    for (i = 0; i < 3; ++i)
    {
        sleep(2);
        printf( "This in the thread : %d\n" , i );
    }
    return NULL;
}


/*
* 程序入口
* */
int main()
{
    pthread_t pth;
    int i;

	/*创建线程并执行线程执行函数*/
    int ret = pthread_create(&pth, NULL, thread, NULL);  
	printf("The main process will be to run,but will be blocked soon\n");	
	/*阻塞等待线程退出*/
    pthread_join(pth, NULL);

    printf("thread was exit\n");
    for (i = 0; i < 3; ++i)
    {
        sleep(1);
        printf( "This in the main : %d\n" , i );
    }
    return 0;
}



可以看到,调用pthread_join后进程阻塞直到线程退出。
pthread_join注释掉后可以看到结果如下:
此时进程和线程同时运行。

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <time.h>
pthread_t tid; sigset_t set;
void myfunc()
{
 printf("hello\n");
}
static void* mythread(void *p)
{
 int signum;
 while(1) {
	 
    sigwait(&set,&signum);
   if(SIGUSR1 == signum)
   myfunc();
   if(SIGUSR2 == signum)
     {
      printf("I will sleep 2 second and exit\n");
      sleep(2);
      break;
     }
    }
}
int main()
{
  char tmp;
  void* status;
  sigemptyset(&set);
  sigaddset(&set,SIGUSR1);
  sigaddset(&set,SIGUSR2);
  sigprocmask(SIG_SETMASK,&set,NULL);
  pthread_create(&tid,NULL,mythread,NULL);
  while(1)
  {
    printf(":");
    scanf("%c",&tmp);
    if('a' == tmp)
    {
       pthread_kill(tid,SIGUSR1);
    }
    else if('q'==tmp)
    {
     
      pthread_kill(tid,SIGUSR2);
   
      pthread_join(tid,&status); 
      printf("finish\n");
      break;
    }
    else
      continue;
  }
  return 0;
}

运行结果:
// 如果输入a,子线程打印"hello",主程序继续等待输入;
// 如果输入q,主程序等待子程序结束。子线程打印"I will sleep 2 second and exit",并延时两秒后结束。主线程随之打印"finish",程序结束。

不论是可预见的线程终止还是异常终止,都会存在资源释放的问题,在不考虑因运行出错而退出的前提下,如何保证线程终止时能顺利的释放掉自己所占用的资源,特别是锁资源,就是一个必须考虑解决的问题。

最经常出现的情形是资源独占锁的使用:线程为了访问临界资源而为其加上锁,但在访问过程中被外界取消,如果线程处于响应取消状态,且采用异步方式响应,或者在打开独占锁以前的运行路径上存在取消点,则该临界资源将永远处于锁定状态得不到释放。外界取消操作是不可预见的,因此的确需要一个机制来简化用于资源释放的编程。

在POSIX线程API中提供了一个pthread_cleanup_push()/pthread_cleanup_pop()函数对用于自动释放资源--从pthread_cleanup_push()的调用点到pthread_cleanup_pop()之间的程序段中的终止动作(包括调用pthread_exit()和取消点终止)都将执行pthread_cleanup_push()所指定的清理函数。API定义如下:

void pthread_cleanup_push(void (*routine)(void*), void *arg);

void pthread_cleanup_pop(int execute);//这里的int参数,0是不执行push的内容,非0是执行。
需要注意的问题有几点:
1,push与pop一定是成对出现的,其实push中包含"{"而pop中包含"}",少一个不行。
2,push可以有多个,同样的pop也要对应的数量,遵循"先进后出原则"。

push进去的函数可能在以下三个时机执行:
1,显示的调用pthread_exit();

2,在cancel点线程被cancel。

3,pthread_cleanup_pop()的参数不为0时。

以上动作都限定在push/pop涵盖的代码内。
测试代码:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

/*
 * 线程清理函数
 * */
void *clean(void *arg)
{
    printf("cleanup :%s\n",(char *)arg);
    return (void *)0;
}

/*
 * 线程1的执行函数
 * */
void *thr_fn1(void *arg)
{
    printf("thread 1 start  \n");
	/*将线程清理函数压入清除栈两次*/
    pthread_cleanup_push( (void*)clean,"thread 1 first handler");
    pthread_cleanup_push( (void*)clean,"thread 1 second hadler");
    printf("thread 1 push complete  \n");

    if(arg)
    {
        return((void *)1); //线程运行到这里会结束,后面的代码不会被运行。由于是用return退出,所以不会执行线程清理函数。
    }
    pthread_cleanup_pop(0);
    pthread_cleanup_pop(0);
    return (void *)1;
}

/*
 * 线程2的执行函数
 * */
void *thr_fn2(void *arg)
{
    printf("thread 2 start  \n");

	/*将线程清理函数压入清除栈两次*/
    pthread_cleanup_push( (void*)clean,"thread 2 first handler");
    pthread_cleanup_push( (void*)clean,"thread 2 second handler");
    printf("thread 2 push complete  \n");
    
	if(arg)
    {
        pthread_exit((void *)2);//线程运行到这里会结束,后面的代码不会被运行。由于是用pthread_exit退出,所以会执行线程清理函数。执行的顺序是先压进栈的后执行,即后进先出。
    }
    pthread_cleanup_pop(0);
    pthread_cleanup_pop(0);
    pthread_exit((void *)2);
}

/*
 * 程序入口
 * */
int main(void)
{
    int err;
    pthread_t tid1,tid2;
    void *tret;

	/*创建线程1并执行线程执行函数*/
    err=pthread_create(&tid1,NULL,thr_fn1,(void *)1);
    if(err!=0)
    {
        printf("error .... \n");
        return -1;
    }
	/*创建线程2并执行线程执行函数*/
    err=pthread_create(&tid2,NULL,thr_fn2,(void *)1);
	if(err!=0)
    {
        printf("error .... \n");
        return -1;
    }

	/*阻塞等待线程1退出,并获取线程1的返回值*/
    err=pthread_join(tid1,&tret);
    if(err!=0)
    {
        printf("error .... \n");
        return -1;
    }
    printf("thread 1 exit code %d  \n",(int)tret);
	/*阻塞等待线程2退出,并获取线程2的返回值*/
    err=pthread_join(tid2,&tret);
    if(err!=0)
    {
        printf("error .... ");
        return -1;
    }
	printf("thread 2 exit code %d  \n",(int)tret);  
    return 1;
}





评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值