linux停的简单进程创建
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。前者表示系统限制创建新的线程,例如线程数目过多了;后者表示第二个参数代表的线程属性值非法。创建线程成功后,新创建的线程则运行参数三和参数四确定的函数,原来的线程则继续运行下一行代码。




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涵盖的代码内。
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; }