电脑电脑如何重装系统统后出现这个,求解

Linux下获得线程ID syscall(224)_Linux编程_Linux公社-Linux系统门户网站
你好,游客
Linux下获得线程ID syscall(224)
来源:Linux社区&
作者:Linux
对于Linux线程,也许大家并不陌生,而我们通常所说的线程是指基于POSIX标准的线程,而Linux下除了兼容POSIX标准,而且提供线程在内核级的支持。
早在Linux kernel2.4之前,Linux的线程(LinuxThread)属于用户级,内核根本不知道线程的存在,为了得到内核的支持,2003 年,公司(很了不起的一个公司)发布了NPTL(Native POSIX Thread Library),至今广泛使用。
NPTL很优秀,对信号的处理是按照每进程的原则进行的;getpid() 会为所有的线程返回相同的进程 ID。例如,如果发送了 SIGSTOP 信号,那么整个进程都会停止;使用 LinuxThreads,只有接收到这个信号的线程才会停止。这样可以在基于 NPTL 的应用程序上更好地利用调试器,例如 GDB。
额外话说多了,下面具体看看下面如何获得线程的ID吧!
经常我们需要在程序一个线程中获得这个线程的id,有人会说,可以通过pthread_create函数传递参数传入,等主线程创建子线程成功,那么这个pid会自动赋值
#define TNUM 2struct BN{& & pthread_& & int N;};
void *run(void *args){& & BN *recive = (BN *)& & cout && "pid: " && recive-&pid &&& & return (void *)(recive-&N);}
int main(int c, char *v[]){& && && & char *& & pthread_t pid[TNUM];& & BN bn[TNUM];
& & for (i = 0; i & TNUM; i ++)& &
{& & & & bn[i].pid = pid[i];& & & & bn[i].N = mN;& & & & ret = pthread_create(&pid[i], NULL, run, &bn[i]);& & //...出错处理& & }& & return 0;}
难题是主线程去分配子线程id的时刻,run函数并不能很确切的知道,所以当读取recive-&pid时,就会产生错误;& & 正确的方法应该使用pthread_self()函数来获得POSIX的线程id,此函数调用内部进制直到pid分配完成。& & 有人会问,获取进程id的函数不是getpid(),获取线程id的函数不是gettid()么?这里与刚才前面所说的有些关系,目前gettid获得的pid是需要系统调用(syscall)的,这个syscall返回的pid不是上面所说的POSIX线程id,这个是内核的线程id,具体调用的方式如下:
pid_t pid = syscall(SYS_gettid);
pid_t pid = syscall(__NR_gettid);
//仅限于i386,如果是x86_64,则调用186pid_t pid = syscall(224);
以往旧的方式syscallx的调用已经被废弃。& & 看到这里,也许有人会问,pid_t和pthread_t可以相互转换么?一个是POSIX的线程、一个是内核的线程?如果你感兴趣在这方面研究的话,可以不妨试试,答案肯定是可以的。
& & 至此,你看完这篇文章,明白了Linux线程id怎么获得了么?
本文永久更新链接地址:
相关资讯 & & &
& (01月23日)
& (06/15/:42)
& (01/08/:40)
& (01月18日)
& (04/12/:56)
& (01/04/:32)
   同意评论声明
   发表
尊重网上道德,遵守中华人民共和国的各项有关法律法规
承担一切因您的行为而直接或间接导致的民事或刑事法律责任
本站管理人员有权保留或删除其管辖留言中的任意内容
本站有权在网站内转载或引用您的评论
参与本评论即表明您已经阅读并接受上述条款用户名:华清远见
文章数:434
评论数:51
访问量:126373
注册日期:
阅读量:1297
阅读量:3317
阅读量:428175
阅读量:1116179
51CTO推荐博文
作者:刘洪涛,讲师。
本文主要介绍一个linux内核线程的实例,以及在QEMU平台上测试的过程。
一、内核线程的创建
编写一个字符设备驱动,在驱动注册时,开启一个内核线程。在用户向设备写入数据时,字符设备的wirte方法能够激活此内核线程,并在线程中实现打印用户输入的数据。
驱动代码如下(在2.6.22内核上测试通过),关键部分加上了注释:
#include &linux/module.h&
&&&&&&&&#include &linux/moduleparam.h&
&&&&&&&&#include &linux/kernel.h& /* printk(), min() */
&&&&&&&&#include &linux/slab.h& /* kmalloc() */
&&&&&&&&#include &linux/fs.h& /* everything... */
&&&&&&&&#include &linux/errno.h& /* error codes */
&&&&&&&&#include &linux/types.h& /* size_t */
&&&&&&&&#include &linux/fcntl.h&
&&&&&&&&#include &linux/cdev.h&
&&&&&&&&#include &asm/uaccess.h&
&&&&&&&&#include &linux/device.h&
&&&&&&&&#include &linux/kthread.h&
&&&&&&&&#include &linux/spinlock.h&
&&&&&&&&static int kthread_major = 0;
&&&&&&&&module_param(kthread_major, int, 0);
&&&&&&&&MODULE_AUTHOR(&farsight&);
&&&&&&&&MODULE_LICENSE(&Dual BSD/GPL&);
&&&&&&&&struct kthread_dev {
&&&&&&&&&&&&&&&&struct task_struct *
&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&char*
&&&&&&&&&&&&&&&&int data_
&&&&&&&&&&&&&&&&char data[100];
&&&&&&&&&&&&&&&&spinlock_t queue_
&&&&&&&&int kthread_open(struct inode *inode,struct file *filp)
&&&&&&&&&&&&&&&&return 0;
&&&&&&&&ssize_t kthread_read(struct file *file, char __user *buff, size_t count, loff_t *offp)
&&&&&&&&return 0;
&&&&&&&&ssize_t kthread_write(struct file *file, const char __user *buff, size_t count, loff_t *offp)
&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&ret=sizeof(kthread_dev_obj-&data);
&&&&&&&&&&&&&&&&if(count&(ret-1))
&&&&&&&&&&&&&&&&count=ret-1;
&&&&&&&&&&&&&&&&if(copy_from_user(kthread_dev_obj-&data,buff,count)&0)//获取用户数据
&&&&&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&&&&&&&&&goto out1;
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&spin_lock(&kthread_dev_obj-&queue_lock);
&&&&&&&&&&&&&&&&kthread_dev_obj-&data_size=
&&&&&&&&&&&&&&&&spin_unlock(&kthread_dev_obj-&queue_lock);
&&&&&&&&&&&&&&&&wake_up_process(kthread_dev_obj-&thread);//唤醒内核线程
&&&&&&&&&&&&&&&&
&&&&&&&&out1:
&&&&&&&&&&&&&&&&return 0;
static int kthread_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
&&&&&&&&&&&&&&&&return 0;
static int kthread_release(struct inode *node, struct file *file)
&&&&&&&&&&&&&&&&return 0;
&&&&&&&&* Set up the cdev structure for a device.
&&&&&&&&*/
&&&&&&&&static void kthread_setup_cdev(struct cdev *dev, int minor,struct file_operations *fops)
&&&&&&&&&&&&&&&&int err, devno = MKDEV(kthread_major, minor);
&&&&&&&&cdev_init(dev, fops);
&&&&&&&&&&&&&&&&dev-&owner = THIS_MODULE;
&&&&&&&&&&&&&&&&err = cdev_add (dev, devno, 1);
&&&&&&&&&&&&&&&&/* Fail gracefully if need be */
&&&&&&&&&&&&&&&&if (err)
&&&&&&&&&&&&&&&&&&&&&&&&printk (KERN_NOTICE &Error %d adding kthread%d&, err, minor);
static struct file_operations kthread_remap_ops = {
&&&&&&&&&&&&&&&&.owner = THIS_MODULE,
&&&&&&&&&&&&&&&&.open = kthread_open,
&&&&&&&&&&&&&&&&.release = kthread_release,
&&&&&&&&&&&&&&&&.read = kthread_read,
&&&&&&&&&&&&&&&&.write = kthread_write,
&&&&&&&&&&&&&&&&.ioctl = kthread_ioctl,
&&&&&&&&};
static int kthread_fun(void * arg) //内核线程运行函数
&&&&&&&&&&&&&&&&while (!kthread_should_stop()) {
&&&&&&&&&&&&&&&&&&&&&&&&spin_lock(&kthread_dev_obj-&queue_lock);
&&&&&&&&&&&&&&&&&&&&&&&&if(kthread_dev_obj-&data_size){
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&spin_unlock(&kthread_dev_obj-&queue_lock);
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&kthread_dev_obj-&data[kthread_dev_obj-&data_size]='\0';
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&printk(kthread_dev_obj-&data);//打印出用户空间数据
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&printk(&in kthread\n&);
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&kthread_dev_obj-&data_size=0;
&&&&&&&&&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&&&&&&&&&else{
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&set_current_state(TASK_INTERRUPTIBLE);
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&spin_unlock(&kthread_dev_obj-&queue_lock);
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&schedule();
&&&&&&&&&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&return 0;
static int kthread_init(void)
&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&dev_t dev = MKDEV(kthread_major, 0);
&&&&&&&&/* Figure out our device number. */
&&&&&&&&&&&&&&&&if (kthread_major)
&&&&&&&&&&&&&&&&&&&&&&&&result = register_chrdev_region(dev, 1, &kthread&);
&&&&&&&&&&&&&&&&else {
&&&&&&&&&&&&&&&&&&&&&&&&result = alloc_chrdev_region(&dev, 0, 1, &kthread&);
&&&&&&&&&&&&&&&&&&&&&&&&kthread_major = MAJOR(dev);
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&if (result & 0) {
&&&&&&&&&&&&&&&&&&&&&&&&printk(KERN_WARNING &kthread: unable to get major %d\n&, kthread_major);
&&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&if (kthread_major == 0)
&&&&&&&&&&&&&&&&&&&&&&&&kthread_major =
&&&&&&&&kthread_dev_obj= kmalloc(sizeof(struct kthread_dev), GFP_KERNEL);
&&&&&&&&&&&&&&&&kthread_setup_cdev(&kthread_dev_obj-&cdev, 0,&kthread_remap_ops);
&&&&&&&&&&&&&&&&printk(&kthread device installed, with major %d\n&, kthread_major);
&&&&&&&&&&&&&&&&my_class= class_create(THIS_MODULE, &kthread&);
&&&&&&&&&//&&&&&device_create(my_class, NULL, MKDEV(kthread_major, 0),NULL, &kthread&);
&&&&&&&&&&&&&&&&device_create(my_class, NULL, MKDEV(kthread_major, 0), &kthread&);//for
&&&&&&&&&//&&&&&2.6.22
&&&&&&&&&&&&&&&&kthread_dev_obj-&name=&kthreadtest&;//内核线程的名称
&&&&&&&&&&&&&&&&spin_lock_init(&kthread_dev_obj-&queue_lock);
&&&&&&&&&&&&&&&&kthread_dev_obj-&thread=kthread_run(kthread_fun,kthread_dev_obj,&%sd&,kthread_dev_obj-&name);//创建并运行内核线程
&&&&&&&&&&&&&&&&return 0;
&&&&&&&&static void kthread_cleanup(void)
&&&&&&&&&&&&&&&&kthread_stop(kthread_dev_obj-&thread);//停止内核线程
&&&&&&&&&&&&&&&&cdev_del(&kthread_dev_obj-&cdev);
&&&&&&&&&&&&&&&&unregister_chrdev_region(MKDEV(kthread_major, 0), 1);
&&&&&&&&&&&&&&&&device_destroy(my_class,MKDEV(kthread_major,0));
&&&&&&&&&&&&&&&&class_destroy(my_class);
&&&&&&&&&&&&&&&&kfree(kthread_dev_obj);
&&&&&&&&&&&&&&&&printk(&kthread device uninstalled\n&);
&&&&&&&&module_init(kthread_init);
&&&&&&&&module_exit(kthread_cleanup);
二、在QEMU平台上的测试方法
QEMU可以模拟很多硬件平台,使用QEMU适用于你手边没用硬件平台,或没用很好的内核调试工具的情况。
这里主要介绍使用QEMU模拟ARM开发环境,并运行linux系统的过程。
1、系统环境
操作系统平台:Ubuntu 10.10
&&&&&&&&交叉工具:arm-softfloat-linux-gnu
&&&&&&&&测试内核:Linux2.6.22
&&&&&&&&测试平台:RealView-EB (QEMU 模拟)
2、安装QEMU的方法
使用新立得获取安装包
650) this.width=650;" onclick='window.open("/viewpic.php?refimg=" + this.src)'
height="124" src="http://www.embedu.org/Column/images/Column334-1.JPG" width="600" alt="" />
安装完毕后,测试一下可以支持的ARM平台
$ qemu-system-arm -M ?
&&&&&&&&Supported machines are:
&&&&&&&&syborg&&&&&&&&&&&&&Syborg (Symbian Virtual Platform)
&&&&&&&&musicpal&&&&&&&&Marvell 88w8618 / MusicPal (ARM926EJ-S)
&&&&&&&&mainstone&&&&&&Mainstone II (PXA27x)
&&&&&&&&n800&&&&&&&&&&&&&&&&Nokia N800 tablet aka. RX-34 (OMAP2420)
&&&&&&&&n810&&&&&&&&&&&&&&&&Nokia N810 tablet aka. RX-44 (OMAP2420)
&&&&&&&&cheetah&&&&&&&&&&&Palm Tungsten|E aka. Cheetah PDA (OMAP310)
&&&&&&&&sx1&&&&&&&&&&&&&&&&&&&Siemens SX1 (OMAP310) V2
&&&&&&&&sx1-v1&&&&&&&&&&&&& Siemens SX1 (OMAP310) V1
&&&&&&&&tosa&&&&&&&&&&&&&&&& Tosa PDA (PXA255)
&&&&&&&&akita&&&&&&&&&&&&&&& Akita PDA (PXA270)
&&&&&&&&spitz&&&&&&&&&&&&&&&& Spitz PDA (PXA270)
&&&&&&&&borzoi&&&&&&&&&&&&&& Borzoi PDA (PXA270)
&&&&&&&&terrier&&&&&&&&&&&&&&&Terrier PDA (PXA270)
&&&&&&&&connex&&&&&&&&&&&&&Gumstix Connex (PXA255)
&&&&&&&&verdex&&&&&&&&&&&&&&Gumstix Verdex (PXA270)
&&&&&&&&lm3s811evb Stellaris LM3S811EVB
&&&&&&&&lm3s6965evb Stellaris LM3S6965EVB
&&&&&&&&realview-eb ARM RealView Emulation Baseboard (ARM926EJ-S)
&&&&&&&&realview-eb-mpcore ARM RealView Emulation Baseboard (ARM11MPCore)
&&&&&&&&realview-pb-a8 ARM RealView Platform Baseboard for Cortex-A8
&&&&&&&&realview-pbx-a9 ARM RealView Platform Baseboard Explore for Cortex-A9
&&&&&&&&versatilepb ARM Versatile/PB (ARM926EJ-S)
&&&&&&&&versatileab ARM Versatile/AB (ARM926EJ-S)
&&&&&&&&integratorcp ARM Integrator/CP (ARM926EJ-S) (default)
3、制作内核镜像
(1)配置好交叉开发工具
(2)配置内核
&&&&&&&&#cp arch/arm/configs/realview_defconfig .config
&&&&&&&&#make menuconfig
&&&&&&&&配置支持initial ramdisk
650) this.width=650;" onclick='window.open("/viewpic.php?refimg=" + this.src)'
height="375" src="http://www.embedu.org/Column/images/Column334-2.JPG" width="600" alt="" />
配置支持Ramdisk块设备:
&&&&&&&&Device Drivers -&Block devices-&RAM disk support
&&&&&&&&其中:Default RAM disk size (kbytes)必须要改成和你的RAMDISK镜像一样的大小。
650) this.width=650;" onclick='window.open("/viewpic.php?refimg=" + this.src)'
height="375" src="http://www.embedu.org/Column/images/Column334-3.JPG" width="600" alt="" />
配置内核添加调试选项:
&&&&&&&&Kernel hacking -&Compile the kernel with debug info
650) this.width=650;" onclick='window.open("/viewpic.php?refimg=" + this.src)'
height="375" src="http://www.embedu.org/Column/images/Column334-4.JPG" width="600" alt="" />
设置内核支持ext2文件系统
650) this.width=650;" onclick='window.open("/viewpic.php?refimg=" + this.src)'
height="126" src="http://www.embedu.org/Column/images/Column334-5.JPG" width="473" alt="" />
保存退出。
(3) 将上述的内核线程驱动加入到内核中,然后编译内核。
&&&&&&&&#make zImage
4、制作根文件系统
制作一个8M的ramdisk根文件系统。这个步骤没有什么特别的,可以参考其它资料。
5、安装gdb
(1)下载GDB源码:
(2)交叉编译GDB
#./configure --target=arm-softfloat-linux-gnu &prefix=/home/lht/QEMU/arm-gdb
&&&&&&&&#make && make install
6、调试内核
#qemu-system-arm -M realview-eb -kernel ./zImage -initrd ./initrd.img -nographic -append &console=ttyAMA0& -m 64 -s -S
系统会暂停,等待远端gdb连接调试。在另一终端下运行:
#ddd&&&&&&&&-debugger &&&&&&&&/home/lht/QEMU/arm-gdb/bin/arm-softfloat-linux-gnu-gdb ~/disk2/s3c2410/linux-2.6.22.6-qemu/vmlinux
此时会出现ddd运行界面,然后运行远程连接命令:
(gdb)target remote localhost:1234
此时就可以运行gdb命令调试内核了。
如:在kthread_fun函数中设置一个断点的方法
&&&&&&&&连接后,搜索栏中输入kthead_fun,出现如下图的显示:
650) this.width=650;" onclick='window.open("/viewpic.php?refimg=" + this.src)'
height="543" src="http://www.embedu.org/Column/images/Column334-6.JPG" width="600" alt="" />
在view菜单中打开汇编窗口,然后在汇编窗口中设置断点(比在c中准确)。
650) this.width=650;" onclick='window.open("/viewpic.php?refimg=" + this.src)'
height="367" src="http://www.embedu.org/Column/images/Column334-7.JPG" width="600" alt="" />
Gdb命令行输入c
&&&&&&&&(gdb) c
&&&&&&&&启动目标系统
&&&&&&&&系统会在断点处停止,接下就可以用ddd提供图形调试工具调试代码了。
&&&&&&&&系统正常启动后,测试结果如下:
&&&&&&&&[root@farsight /]# echo 123456 & /dev/kthread
&&&&&&&&123456
&&&&&&&&in kthread
&&&&&&&&[root@farsight /]# ps w
&&&&&&&&PID USER&&&&&&&&VSZ STAT COMMAND
&&&&&&&&&&& 1 0 &&&&&&&&&&&&&&&&0 SW [swapper]
&&&&&&&& &&&2 0&&&&&&&&&&&&&&&&0 SW& [kthreadd]
&&&&&&&& &&&3 0&&&&&&&&&&&&&&&&0 SWN [ksoftirqd/0]
&&&&&&&& &&&4 0&&&&&&&&&&&&&&&&0 SW& [watchdog/0]
&&&&&&&& &&&5 0&&&&&&&&&&&&&&&&0 SW& [events/0]
&&&&&&&& &&&6 0&&&&&&&&&&&&&&&&0 SW& [khelper]
&&&&&&&&&&40 0&&&&&&&&&&&&&&&&0 SW& [kblockd/0]
&&&&&&&&&&41 0&&&&&&&&&&&&&&&&0 SW& [kseriod]
&&&&&&&&&&53 0&&&&&&&&&&&&&&&&0 SW [pdflush]
&&&&&&&&&&54 0&&&&&&&&&&&&&&&&0 SW [pdflush]
&&&&&&&&&&55 0&&&&&&&&&&&&&&&&0 SW& [kswapd0]
&&&&&&&&&&56 0&&&&&&&&&&&&&&&&0 SW& [aio/0]
&&&&&&&&<font color="#ff&&&&&&&&&&&&&&&&0 SW& [kthreadtestd]
&&&&&&&&188 0&&&&&&&&&&&&&&&&0 SW& [mtdblockd]
&&&&&&&&196 0&&&&&&&&&&&&&&&&0 SW& [kpsmoused]
&&&&&&&&202 0&&&&&&&&1996 S init
&&&&&&&&206 0&&&&&&&&1492 S & /bin/udevd --daemon
&&&&&&&&208 0&&&&&&&&2000 S -/bin/sh
&&&&&&&&209 0&&&&&&&&2000 R ps w
了这篇文章
类别:未分类┆阅读(0)┆评论(0)Linux内核线程之深入浅出
我的图书馆
Linux内核线程之深入浅出
【摘要】本文首先介绍了进程和线程的区别,接着分析了内核线程、轻量级LWP线程以及常见的用户线程的特点,同时介绍了内核线程和进程的区别。分析了创建内核线程kernel_thread函数的实现过程,介绍了一个在驱动中使用内核线程的实例。最后针对内核线程创建销毁的特点,给出了通用的内核线程操作函数API,使用该API可在自己的驱动或内核代码中方便的使用内核线程。【关键词】线程,进程,内核线程,用户线程,LWP,kernel_thread,daemonize,kill_proc, allow_signal,SIGKILL1&&&&& 线程和进程的差别在现代操作系统中,进程支持多线程。进程是资源管理及分配的最小单元;而线程是程序执行的最小单元。一个进程的组成实体可以分为两大部分:线程集和资源集。进程中的线程是动态的对象,代表了进程指令的执行过程。资源,包括地址空间、打开的文件、用户信息等等,由进程内的线程共享。线程有自己的私有数据:程序计数器,栈空间以及寄存器。现实中有很多需要并发处理的任务,如数据库的服务器端、网络服务器、大容量计算等。传统的UNIX进程是单线程的,单线程意味着程序必须是顺序执行,不能并发,即在一个时刻只能运行在一个处理器上,因此不能充分利用多处理器框架的计算机。如果采用多进程的方法,则有如下问题:2&&&&& fork一个子进程的消耗是很大的,fork是一个昂贵的系统调用。2&&&&& 各个进程拥有自己独立的地址空间,进程间的协作需要复杂的IPC技术,如消息传递和共享内存等。线程推广了进程的概念,使一个进程可以包含多个活动(或者说执行序列等等)。多线程的优点和缺点实际上是对立统一的。使用线程的优点在于:2&&&&& 改进程序的实时响应能力;2&&&&& 更有效的使用多处理器,真正的并行(parallelism);2&&&&& 改进程序结构,具备多个控制流;2&&&&& 通讯方便,由于共享进程的代码和全局数据;2&&&&& 减少对系统资源的使用。对属于同一个进程的线程之间进行调度切换时不需要调用系统调用,因此将减少额外的消耗,往往一个进程可以启动上千个线程也没有什么问题。缺点在于:由于各线程共享进程的地址空间,因此可能会导致竞争,因此对某一块有多个线程要访问的数据需要一些同步技术。2&&&&& 线程的分类2.1&&&& 内核线程Linux内核可以看作一个服务进程(管理软硬件资源,响应用户进程的种种合理以及不合理的请求)。内核需要多个执行流并行,为了防止可能的阻塞,多线程化是必要的。内核线程就是内核的分身,一个分身可以处理一件特定事情。Linux内核使用内核线程来将内核分成几个功能模块,像kswapd、kflushd等,这在处理异步事件如异步IO时特别有用。内核线程的使用是廉价的,唯一使用的资源就是内核栈和上下文切换时保存寄存器的空间。支持多线程的内核叫做多线程内核(Multi-Threads kernel )。内核线程的调度由内核负责,一个内核线程处于阻塞状态时不影响其他的内核线程,因为其是调度的基本单位。这与用户线程是不一样的。2.2&&&& 轻量级进程轻量级进程(LWP)是一种由内核支持的用户线程。它是基于内核线程的高级抽象,因此只有先支持内核线程,才能有LWP。每一个进程有一个或多个LWPs,每个LWP由一个内核线程支持。这种模型实际上就是恐龙书上所提到的一对一线程模型。在这种实现的操作系统中,LWP就是用户线程。由于每个LWP都与一个特定的内核线程关联,因此每个LWP都是一个独立的线程调度单元。即使有一个LWP在系统调用中阻塞,也不会影响整个进程的执行。轻量级进程具有局限性。首先,大多数LWP的操作,如建立、析构以及同步,都需要进行系统调用。系统调用的代价相对较高:需要在user mode和kernel mode中切换。其次,每个LWP都需要有一个内核线程支持,因此LWP要消耗内核资源(内核线程的栈空间)。因此一个系统不能支持大量的LWP。注:LWP的术语是借自于SVR4/MP和Solaris 2.x。将之称为轻量级进程的原因可能是:在内核线程的支持下,LWP是独立的调度单元,就像普通的进程一样。所以LWP的最大特点还是每个LWP都有一个内核线程支持。2.3&&&& 用户线程LWP虽然本质上属于用户线程,但LWP线程库是建立在内核之上的,LWP的许多操作都要进行系统调用,因此效率不高。而这里的用户线程指的是指不需要内核支持而完全建立在用户空间的线程库,用户线程的建立、同步、销毁及调度完全在用户空间完成,不需要内核的参与帮助。因此这种线程的操作是极其快速的且低消耗的。上图是最初的一个多对一用户线程模型,从中可以看出,进程中包含线程,用户线程在用户空间中实现,内核并没有直接对用户线程进程调度,内核的调度对象和传统进程一样,还是进程本身,内核并不知道用户线程的存在。由于Linux内核没有轻量级进程(线程)的概念,因此不能独立的对用户线程进行调度,而是由一个线程运行库来组织线程的调度,其主要工作在于在各个线程的栈之间调度。如果一个进程中的某一个线程调用了一个阻塞的系统调用,整个进程就会被调度程序切换为等待状态,其他线程得不到运行的机会。因此UNIX使用了异步I/O机制。这种机制主要的缺点在于在一个进程中的多个线程的调度中无法发挥多处理器的优势(如上述的阻塞情况)。3&&&&& 初识内核线程内核线程(thread)或叫守护进程(daemon),在操作系统中占据相当大的比例,当Linux操作系统启动以后,尤其是X window也启动以后,你可以用”ps -ef”命令查看系统中的进程,这时会发现很多以”d”结尾的进程名,确切说名称显示里面加 "[]"的,这些进程就是内核线程。系统的启动是从硬件-&内核-&用户态进程的,pid的分配是一个往前的循环的过程,所以随系统启动的内核线程的pid往往很小。2&&&&& PID TTY STAT TIME COMMAND2&&&&& 1 ? S 0:01 init [3]2&&&&& 3 ? SN 0:00 [ksoftirqd/0]2&&&&& 5 ? SN 0:00 [ksoftirqd/1]2&&&&& 6 ? S& 0:00 [events/0]2&&&&& 7 ? S& 0:00 [events/1]2&&&&& 8 ? S& 0:00 [khelper]2&&&&& 9 ? S& 0:00 [kblockd/0]2&&&&& 10 ? S& 0:00 [kblockd/1]2&&&&& 11 ? S 0:00 [khubd]2&&&&& 35 ? S 0:42 [pdflush]2&&&&& 36 ? S 0:02 [pdflush]2&&&&& 38 ? S& 0:00 [aio/0]2&&&&& 39 ? S& 0:00 [aio/1]2&&&&& 37 ? S 0:19 [kswapd0]2&&&&& 112 ? S 0:00 [kseriod]2&&&&& 177 ? S 0:00 [scsi_eh_0]2&&&&& 178 ? S 0:00 [ahc_dv_0]2&&&&& 188 ? S 0:00 [scsi_eh_1]2&&&&& 189 ? S 0:00 [ahc_dv_1]2&&&&& 196 ? S 2:31 [kjournald]2&&&&& 1277 ? S 0:00 [kjournald]2&&&&& 1745 ? Ss 0:02 syslogd -m 02&&&&& 1749 ? Ss 0:00 klogd -x2&&&&& 1958 ? Ss 0:13 /usr/sbin/sshd2&&&&& 2060 ? Ss 0:00 crond2&&&&& 2135 tty2 Ss+ 0:00 /sbin/mingetty tty22&&&&& 2136 tty3 Ss+ 0:00 /sbin/mingetty tty32&&&&& 2137 tty4 Ss+ 0:00 /sbin/mingetty tty42&&&&& 2138 tty5 Ss+ 0:00 /sbin/mingetty tty52&&&&& 2139 tty6 Ss+ 0:00 /sbin/mingetty tty62&&&&& 23564 ? S 0:00 bash2&&&&& 25605 ? Ss 0:00 sshd: peter [priv]2&&&&& 25607 ? S 0:00 sshd: peter@pts/23.1&&&& 内核线程2&&&&& events 处理内核事件 很多软硬件事件(比如断电,文件变更)被转换为events,并分发给对相应事件感兴趣的线程进行响应2&&&&& ksoftirqd 处理软中断 硬件中断处理往往需要关中断,而这个时间不能太长,否则会丢失新的中断。所以中断处理的很大一部分工作移出,转给任劳任怨的ksoftirqd在中断之外进行处理。比如一个网络包,从网卡里面取出这个过程可能需要关中断,但是TCP/IP协议处理就不必关中断了2&&&&& kblockd 管理磁盘块读写2&&&&& kjournald Ext3文件系统的日志管理 通常每个 _已mount_ 的 Ext3分区会有一个 kjournald看管,各分区的日志是独立2&&&&& pdflush dirty内存页面的回写 太多dirty的页面意味着风险,比如故障时候的内容丢失,以及对突发的大量物理内存请求的响应(大量回写会导致糟糕的响应时间)2&&&&& kswapd 内存回收 确保系统空闲物理内存的数量在一个合适的范围2&&&&& aio 代替用户进程管理io 用以支持用户态的AIO3.2&&&& 用户进程2&&&&& crond 执行定时任务2&&&&& init 为内核创建的第一个线程。引导用户空间服务,管理孤儿线程,以及运行级别的转换2&&&&& mingetty 等待用户从tty登录2&&&&& bash shell进程,一个命令行形式的系统接口;接受用户的命令,并进行解释、执2&&&&& sshd ssh登录、文件传输、命令执行 等操作的服务进程2&&&&& klogd 从内核信息缓冲区获取打印信息。内核在发现异常的时候,往往会输出一些消息给用户,这个对于故障处理很有用2&&&&& syslogd 系统日志进程2&&&&& udevd 支持用户态设备操作 (userspace device)4&&&&& 内核线程与用户进程的关系内核线程也可以叫内核任务,例如,磁盘高速缓存的刷新,网络连接的维护,页面的换入换出等等。在Linux中,内核线程与普通进程有一些本质的区别,从以下几个方面可以看出二者之间的差异:2&&&&& 内核线程能够访问内核中数据,调用内核函数,而普通进程只有通过系统调用才能执行内核中的函数;2&&&&& 内核线程只运行在内核态,而普通进程既可以运行在用户态,也可以运行在内核态;2&&&&& 因为内核线程指只运行在内核态,因此,它只能使用大于PAGE_OFFSET(3G)的地址空间。另一方面,不管在用户态还是内核态,普通进程可以使用4GB的地址空间。5&&&&& 内核线程的创建内核线程是由kernel_thread(& )函数在内核态下创建的:linux+v2.6.19/arch/arm/kernel/process.c460 pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)461{462&&&&&&& struct pt_463464&&&&&&& memset(&regs, 0, sizeof(regs));465466&&&&&&& regs.ARM_r1 = (unsigned long)467&&&&&&& regs.ARM_r2 = (unsigned long)468&&&&&&& regs.ARM_r3 = (unsigned long)do_469&&&&&&& regs.ARM_pc = (unsigned long)kernel_thread_470&&&&&&& regs.ARM_cpsr = SVC_MODE;471472&&&&&&& return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);473}Regs:内核栈中的一个地址,通过这个地址,可以找到内核栈CPU寄存器的初始化值。线程的创建本质上就是建立了一个task_struct进程结构,这样内核调度时就会根据此结构的Regs信息运行。建立新的内核线程时默认采用了如下标志:CLONE_VM:置起此标志在进程间共享地址空间。因此内核线程和调用的进程(current)具备相同的进程空间,因为调用者运行在进程的内核态,所以进程在内核态时共享内核空间。CLONE_UNTRACED:保证即使父进程正在被调试,内核线程也不会被调试。其他调用时可用的标志如下: CLONE_FS置起此标志在进程间共享文件系统信息 CLONE_FILES置起此标志在进程间共享打开的文件 CLONE_SIGHAND置起此标志在进程间共享信号处理程序建立线程完毕后将其状态设置为TASK_RUNNING,然后放入运行队列,等待调度。kernel_thread的返回值同sys_fork,正数为新建线程的pid,否则为错误代码。kernel_thread.未返回时,新建线程已经退出,则返回值为0。新线程中执行fn函数,arg为传递给新建线程fn的参数,应确保fn未退出时arg可用。如果父进程未得到子进程退出信息时已经退出,则内核线程将变为孤儿进程。新建内核线程的父进程是模块的加载者,或者确切的说是调用kernel_thread的进程。kernel_thread_helper是一个汇编函数,代码如下:441/*442 * Shuffle the argument into the correct register before calling the443 * thread function.& r1 is the thread argument, r2 is the pointer to444 * the thread function, and r3 points to the exit function.445 */446extern void kernel_thread_helper(void);447asm(&&& ".section .text\n"448"&&&&&& .align\n"449"&&&&&& .type&& kernel_thread_helper, #function\n"450"kernel_thread_helper:\n"451"&&&&&& mov&&&& r0, r1\n"452"&&&&&& mov&&&& lr, r3\n"453"&&&&&& mov&&&& pc, r2\n"454"&&&&&& .size&& kernel_thread_helper, . - kernel_thread_helper\n"455"&&&&&& .previous");Pc执行r2即待创建的线程执行体,arg参数r1赋值给r0,传递给r2的执行线程,lr为ARM的链接寄存器,当从r2返回时将赋给pc,即执行r3指向的do_exit,进行线程的清除工作。所以内核线程是通过fn函数开始的。当结束时,执行系统调用do_exit。本质上相当于运行exit(fn(arg))。6&&&&& 如何在驱动中使用内核线程/* * file mythread.c */#include &linux/kernel.h&#include &linux/module.h&#include &linux/init.h&#include &linux/param.h&#include &linux/jiffies.h&#include &asm/system.h&#include &asm/processor.h&#include &asm/signal.h&static pid_t thread_static DECLARE_COMPLETION(exit_completion);static atomic_t time_to_quit = ATOMIC_INIT(0);int my_fuction(void *arg) {&&&&&daemonize("demo-thread");&&&&&&&&allow_signal(SIGKILL);complete (&exit_completion);while(!signal_pending(current))&&&&&&&&{&&&&&&&&&&&&&&&&printk("jiffies is %lu\n", jiffies);set_current_stat(TASK_INTERRUPTIBLE);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&schedule_timeout(10 * HZ);if (atomic_read(&kthread-&terminate)){ /* we received a request to terminate ourself */ }}/*for(;;){Ret = wait_event_interruptible(wq, condition); //可采用任何同步措施}*/complete_and_exit(&exit_completion, 1);&return 0;}static int __init init(void){&&&&&&&&thread_id = kernel_thread(my_fuction, NULL, CLONE_FS | CLONE_FILES);wait_for_completion(&exit_completion);return 0;}static void __exit finish(void){&&&&&atomic_inc(&time_to_quit);&kill_proc(thread_id, SIGKILL, 1);&wait_for_completion(&exit_completion);printk("Goodbye\n");}module_init(init);module_exit(finish);MODULE_LICENSE("GPL");7&&&&& 通用的内核线程模块7.1&&&& 内核线程相关API本驱动实例展示如何创建及停止一个内核线程。以模块形式加载,相关接口函数export到内核中,供其他模块使用。内核版本 2.6.19,包含kthread.h和kthread.c文件。2&&&&& start_kthread:建立一个内核线程。可在任何进程上下文中执行,down()阻塞在同步信号量semaphore上直至新建立线程启动后执行init_kthread,其调用up()通知start_kthread新线程建立成功并运行。2&&&&& stop_kthread:清除一个内核线程。可在除待清楚进程之外的任何进程上下文中执行。设置退出标志供所建线程检测并发送SIGKILL信号给该线程,阻塞,直至其调用exit_kthread通知killer其已经安全退出。2&&&&& init_kthread:初始化新建立的线程环境,在新建线程循环外部执行。其将调用daemonize更新父进程为init进程便于回收资源,设置待捕捉的信号以便killer可以清除该线程。同时设置新线程的名称,清除退出标志,up()通知建立者新线程成功建立。2&&&&& exit_kthread:收到中止信号后调用,退出。up()通知stop_kthread()其已经退出。7.2&&&& kthread.h#ifndef _KTHREAD_H#define _KTHREAD_H#include &linux/config.h&#include &linux/version.h&#include &linux/kernel.h&#include &linux/sched.h&#include &linux/tqueue.h&#include &linux/wait.h&#include &asm/unistd.h&#include &asm/semaphore.h&/* 新建线程的所以相关信息 */typedef struct kthread_struct {/* private data *//* New Linux task structure of thread ,新建线程的进程数据结构,由current获得*/struct task_struct */* function to be started as thread ,待执行的任务*/void (*function) (struct kthread_struct *kthread);/* semaphore needed on start and creation of thread. 新建或清除过程中用来同步建立者和清除者与所建线程 */struct semaphore startstop_/* public data *//* queue thread is waiting on. Gets initialized by init_kthread, can be used by thread itself. 新线程用来或者外部事件的等待队列 */wait_queue_head_/* flag to tell thread whether to die or not. When the thread receives a signal, it must check the value of terminate and call exit_kthread and terminate if set.。killer设置的退出标识 */atomic_t&/* additional data to pass to kernel thread 。传递给新建线程的附加信息,可选*/void *} kthread_t;/* 原型声明*//* start new kthread (called by creator) */void start_kthread(void (*func)(kthread_t *), kthread_t *kthread);/* stop a running thread (called by "killer") */void stop_kthread(kthread_t *kthread);/* setup thread environment (called by new thread) */void init_kthread(kthread_t *kthread, char *name);/* cleanup thread environment (called by thread upon receiving termination signal) */void exit_kthread(kthread_t *kthread);#endif7.3&&&& kthread.c#include &linux/config.h&#include &linux/version.h&#if defined(MODVERSIONS)#include &linux/modversions.h&#endif#include &linux/kernel.h&#include &linux/sched.h&#include &linux/tqueue.h&#include &linux/wait.h&#include &linux/signal.h&#include &asm/semaphore.h&#include &asm/smplock.h&#include "kthread.h"/* public functions *//* create a new kernel thread. Called by the creator. */void start_kthread(void (*func)(kthread_t *), kthread_t *kthread){/* initialize the semaphore: we start with the semaphore locked. The new kernel thread will setup its stuff and unlock it. This control flow (the one that creates the thread) blocks in the down operation below until the thread has reached the up() operation. */init_MUTEX_LOCKED(&kthread-&startstop_sem);/* store the function to be executed in the data passed to the launcher */kthread-&function=kernel_thread((int (*)(void *))kthread-&function, (void *)kthread, 0);/* wait till it has reached the setup_thread routine */down(&kthread-&startstop_sem);}/* stop a kernel thread. Called by the removing instance */void stop_kthread(kthread_t *kthread){if (kthread-&thread == NULL){ printk("stop_kthread: killing non existing thread!\n"); }/* initialize the semaphore. We lock it here, the leave_thread call of the thread to be terminated will unlock it. As soon as we see the semaphore unlocked, we know that the thread has exited. */init_MUTEX_LOCKED(&kthread-&startstop_sem);/* We need to do a memory barrier here to be sure that the flags are visible on all CPUs. */mb();/* set flag to request thread termination */atomic_inc(&kthread-&terminate);/* We need to do a memory barrier here to be sure that the flags are visible on all CPUs. */mb();kill_proc(kthread-&thread-&pid, SIGKILL, 1);/* block till thread terminated */down(&kthread-&startstop_sem);} /*initialize new created thread. Called by the new thread. */void init_kthread(kthread_t *kthread, char *name){/* fill in thread structure */kthread-&thread =#if LINUX_VERSION_CODE &= KERNEL_VERSION(2,6,0)/* reparent it to init and give name of “Kthread_example” to the new thread */daemonize("Kthread_example");#elsedaemonize();strcpy(current-&comm, " Kthread_example");#endif/* set signal mask to what we want to respond */allow_signal (SIGKILL| SIGINT| SIGTERM);/* initialise wait queue */init_waitqueue_head(&kthread-&queue);/* initialise termination flag */atomic_set( & kthread-&terminate, 0 );/* tell the creator that we are ready and let him continue */up(&kthread-&startstop_sem);}/* cleanup of thread. Called by the exiting thread. */void exit_kthread(kthread_t *kthread){/* we are terminating */kthread-&thread = NULL;/* notify the stop_kthread() routine that we are terminating. */up(&kthread-&startstop_sem);}static int __init init(void){&&&&printk("Kernel thread Lib start!\n");return 0;}static void __exit finish(void){&&&&&&&&&&&&&printk("Kernel thread Lib Goodbye!\n");}module_init(init); module_exit(finish);EXPORT_SYMBOL(start_kthread);EXPORT_SYMBOL(stop_kthread);EXPORT_SYMBOL(init_kthread);EXPORT_SYMBOL(exit_kthread);MODULE_LICENSE("GPL");7.4&&&& thread_drv.c新建线程函数体为 example_thread()执行loop (for(;;)),在内部等待监测事件或者信号,收到事件后执行正常任务,收到信号时检测退出标志。#include &linux/config.h& #include &linux/version.h& #include &linux/module.h& #if defined(MODVERSIONS) #include &linux/modversions.h& #endif #include &linux/kernel.h& #include &linux/string.h& #include &linux/errno.h& #include &linux/sched.h& #include "kthread.h" #define NTHREADS 5 /* the variable that contains the thread data */ kthread_t example[NTHREADS]; /* prototype for the example thread */ static void example_thread(kthread_t *kthread); /* load the module */ int init_module(void) { /* create new kernel threads */ for (i=0; i &NTHREADS; i++) start_kthread(example_thread, &example); return(0); } /* remove the module */ void cleanup_module(void) { /* terminate the kernel threads */ for (i=0; i&NTHREADS; i++) stop_kthread(&example); } /* this is the thread function that we are executing */ static void example_thread(kthread_t *kthread) { /* setup the thread environment */ init_kthread(kthread, "example thread"); printk("hi, here is the kernel thread\n"); /* an endless loop in which we are doing our work */ for(;;) { /* fall asleep for one second */ interruptible_sleep_on_timeout(&kthread-&queue, HZ); /* We need to do a memory barrier here to be sure that the flags are visible on all CPUs. */ mb(); /* here we are back from sleep, either due to the timeout (one second), or because we caught a signal. */ if (atomic_read(&kthread-&terminate)) { /* we received a request to terminate ourself */ } /* Insert your own code here */ printk("example thread: thread woke up\n"); }/* here we go only in case of termination of the thread */ /* cleanup the thread, leave */ exit_kthread(kthread); /* returning from the thread here calls the exit functions */ }7.5&&&& Makefile# set to your kernel tree KERNEL = XXXXX # get the Linux architecture. Needed to find proper include file for CFLAGS ARCH= XXXXX# set cross compile toolsCROSS_COMPILE := XXXXX## Include the make variables (CC, etc...)#LD&&&&&&&&&& = $(CROSS_COMPILE)ldCC&&&&&&&&&& = $(CROSS_COMPILE)gcc# set default flags to compile module CFLAGS = -D__KERNEL__ -DMODULE -I$(KERNEL)/include CFLAGS+= -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing # get configuration of kernel include $(KERNEL)/.config # modify CFLAGS with architecture specific flags include $(KERNEL)/arch/${ARCH}/Makefile # enable the module versions, if configured in kernel source tree ifdef CONFIG_MODVERSIONS CFLAGS+= -DMODVERSIONS -include $(KERNEL)/include/linux/modversions.h endif# enable SMP, if configured in kernel source tree ifdef CONFIG_SMP CFLAGS+= -D__SMP__ endifOBJS =XXXX.koall : $(OBJS)$(OBJS) : thread_drv.o XXXX.o YYYY.ko$(LD) -r $^ -o $@ clean: rm -f *.o8&&&&& 参数文献内核线程的使用tarius.wu,内核线程、轻量级进程、用户线程唐宇,关于内核线程(kernel_thread)Linux内核线程的解释,.cn/s/blog_03bb.htmlLinux Kernel Threads in Device Drivers―― http://www.scs.ch/~frey/linux/kernelthreads.htmlSreekrishnan Venkateswaran ,Kernel Threads,http://www./id/2195How to kill a kernel thread?,http://kerneltrap.org/node/6207Can I start kernel threads from a kernel module?,http://www.kasperd.net/%7Ekasperd/comp.os.linux.development.faqDinesh Ahuja,Linux Kernel Series: Linux Kernel Threads转自
TA的最新馆藏

我要回帖

更多关于 电脑怎么重装系统 的文章

 

随机推荐