线程是一种允许程序一次执行多个操作的机制。 与进程一样,线程似乎同时运行。
Unix(POSIX)的便携式操作系统接口是IEEE计算机协会规定的一系列标准,用于维护操作系统之间的兼容性。
POSIX定义了应用程序编程接口(API),以及命令行shell和实用程序接口,以便与Unix和其他操作系统(Windows)的变体兼容。
API的实现可以在许多类似于Unix的POSIX一致的操作系统上使用,例如freeBSD,NetBSD,OpenBSD,Linux,MAX OS X,Android和Solaris,通常捆绑为库libpthread。
GNU / Linux实现了POSIX标准线程API(称为pthreads)。 所有线程函数和数据类型都在头文件
//pthread_create - 用于创造一个新的线程
#include
int pthread_create(pthread_ *thread, pthread_attr_t *attr, void*(*start_rountine)(void *arg), void *arg);
///threadcreate.c first part
#include
#include
#include
#include
#include
#define gettidv1() syscall(__NR_gettid)
#define gettidv2() syscall(SYS_gettid)
#define getpid() syscall(SYS_getpid)
/*系统调用 - 间接系统调用
syscall()是一个小型库函数,它调用系统调用,其汇编语言接口具有指定参数的指定编号。*/
void *thread_function(void *ptr)
{
char *message;
message = (char *)ptr;
printf("in thread tid=%ld,pid=%ld\n",gettidv2(),getpid(),message);
sprintf(ptr,"hello from child tid=%ld pid=%ld\n",gettidv1(),getpid());
}
int main(void)
{
pthread_t thread1,thread2;
char *message1 = "Thread 1";
char *message2 = "Thread 2";
int iret1, iret2;
char buf[100];
sprintf(buf,"hello from parent pid=%ld\n",getpid());
printf("\nin main process pid=%ld\n original buf=%s\n",getpid(),buf);
//创建独立的线程,每个线程都将执行函数
iret1 = pthread_create(&thread1,NULL,thread_function,(void*)buf);
iret2 = pthread_create(&thread2,NULL,thread_function,(void*)buf);
/*在主函数继续之前等待线程完成。除非我们等待,
否则我们会冒执行退出的风险,这将终止进程以及线程完成之前的所有线程。*/
pthread_join(thread1,NULL);
pthread_join(thread2,NULL);
printf("in parent process %ld\n inputs buf=%s\n", getpid(),buf);
exit(0);
}
一种解决方案是强制主函数等待其他两个线程完成。 我们需要的是一个类似于wait的函数,用于线程完成一个进程的内部。
该函数是pthread_join,它接受两个参数:*要等待的线程的线程ID,以及指向将接收完成线程的返回值的void 变量的指针。 如果您不关心线程返回值,则将NULL作为第二个参数传递。
pthread_join - 使用已终止的线程加入
#include
int pthread_join(pthread_t thread, void ** retval);
pthread_join()函数等待线程指定的线程终止。当函数返回时,被等待线程的资源被收回。如果该线程已经终止,则pthread_join()立即返回。 线程指定的线程必须是可连接的。
在成功时,pthread_join()返回0; 出错时,它会返回错误编号。
CLAGS = -g
tc:threadcreate.o
gcc $(CLAGS) -o tc threadcreate.o -lpthread
threadcreate.o: threadcreate.c
gcc $(CFLAGS) -c threadcreate.c
GNU / Linux上POSIX线程的实现与许多其他类UNIX系统上的线程实现不同:
虽然在同一程序中创建的GNU / Linux线程是作为单独的进程实现的,但它们共享其虚拟内存空间和其他资源
#include
int clone(int (*fn)(void *), void *child_stack, int flags, void *arg,...);
///clone_vm.c part 1
#define _GNU_SOURCE
#include
#include
#include
#include
#include
#include
#include
#include
#define gettidv1() syscall(__NR_gettid)//syscall(__NR_gettid)函数的意思就是获取线程ID,这句话的意思是宏定义gettidv1()函数为获取线程id的函数
#define gettidv2() syscall(SYS_gettid)//这两个syscall没有任何区别,所以函数两个gettidv也没有任何的区别
#define getpid() syscall(SYS_getpid)//通过宏定义的方式获取pid
#define STACK_SIZE 1024*1024
static int child_func(void *arg)
{
char* buf = (char*)arg;
printf("in child sees buf = %s \n",buf);
sprintf(buf,"hello from child tid=%ld,pid=%ld\n",gettidv1(),getpid());
return 0;
}
有一种方法可以在程序开始执行时将命令行参数或参数传递给它。 调用main函数时,会使用两个参数调用它。
///clone_vm.c part 2
int main(int argc,char **argv)
{
//给子任务分配参数
char *stack = malloc(STACK_SIZE);
if(!stack){
perror("malloc");
exit(1);
}
//看看第一个命令行参数
printf("\nthe programme name is %s\n",argv[0]);
//唤起命令行参数vm,设置CLONE_VM标记
unsigned long flags = 0;
if((argc>1)&&(!strcmp(argv[1],"vm")))
flag |= CLONE_VM;
char buf[100];
sprintf(buf,"hello from parent pid=%ld\n",getpid());
printf("in parent process before clone\n buf=%s",buf);
//根据不同的标记创建子进程
if(clone(child_func,stack+STACK_SIZE,flags|SIGCHLD,buf)==-1)
{
perror("clone");
exit(1);
}
int status;
if(waitpid(-1,&status,0)==-1)
{
perror("wait");
exit(1);
}
printf("in parent process: child exited with status %d.\n buf = %s \n",status,buf);
return 0;
}
clone_vm.o:clone_vm.c
gcc $(CFLAGS) -c clone_vm.c
clone_vm:clone_vm.o
gcc $(CFLAGS) -o clone_vm clone_vm.o
clean:
rm -f *.o tc clone_vm