新创建线程和主线程会共享地址空间,但在实际执行中各自都有独立的task_struct。那么在pthread_create一个新线程之后,如果主线程再打开一个文件,原有线程是否可以用主线程的句柄读写文件呢。一般来说通过文件句柄来读写文件遵循下面的流程通过对sys_clone的分析应该是可以的,下面验证一下:
1.源码
#include<stdlib.h>
#include<stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int iOpenFd = -1;
char *str = "Hello World";
void *func(void *arg)
{
while(1)
{
sleep(2);
if(iOpenFd < 0)
{
printf("thread says:openfd < 0\n");
continue;
}
write(iOpenFd , str , strlen(str));
printf("done\n");
}
return 0;
}
int main(int argc , char **argv)
{
int ifd = -1;
pthread_t tid;
if(pthread_create(&tid , NULL , func , NULL) < 0)
{
printf("create thread failed!\n");
return -1;
}
sleep(4);
printf("try to open thread.info\n");
ifd = open("./thread.info" , O_RDWR|O_APPEND);
if(ifd < 0)
{
printf("open failed!\n");
return -1;
}
printf("open success!\n");
iOpenFd = ifd;
pause();
return 0;
}
iOpenFd再创建一个新线程之后在主线程打开,如果线程1共享了对应的打开文件信息,则通过检验iOpenFd则可以拿到对应的文件描述符进行读写。因为一般打开文件基于以下流程
fd-->task_struct.files_struct->fd[fd]-->files。所以就算iOpenFd>0如果线程1的task_struct.file_struct指向新的一块地址,那么task_struct.files_struct->fd[iOpenFd]==NULL也无法读写,所以如果能够正常读写则说明线程1的task_struct1.files_struct与主线程的task_struct0.files_struct指向同一块内存地址
2.执行命令 主线程pid=5718
3.查看主线程打开的文件
lsof -p 5718
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
...
thread 5718 xx 0u CHR 136,1 0t0 4 /dev/pts/1
thread 5718 xx 1u CHR 136,1 0t0 4 /dev/pts/1
thread 5718 xx 2u CHR 136,1 0t0 4 /dev/pts/1
thread 5718 xx 3u REG 202,17 136 4784176 /data/home/xx/prog/env/env1/thread.info
4.查看线程1打开的文件,这个直接通过/proc/pid/fd来查看即可
ps -Lfp 5718
UID PID PPID LWP C NLWP STIME TTY TIME CMD
xx 5718 2324 5718 0 2 20:49 pts/1 00:00:00 ./thread
xx 5718 2324 5719 0 2 20:49 pts/1 00:00:00 ./thread
可以看到对应的pid是5719
我们查看/proc/5719/fd
[root@aa /proc/5719/fd]# ls
total 0
lrwx------ 1 xx xx 64 Jul 9 20:51 0 -> /dev/pts/1
lrwx------ 1 xx xx 64 Jul 9 20:51 1 -> /dev/pts/1
lrwx------ 1 xx xx 64 Jul 9 20:50 2 -> /dev/pts/1
lrwx------ 1 xx xx 64 Jul 9 20:51 3 -> /data/home/xx/prog/env/env1/thread.info
OK 符合预期
总结:
在生成线程和进程时,会有两种不同的效果
>生成线程时
main_thread.files_struct与child_thread.files_struct指向同一块地址
>生成进程时
main_process.files_struct与chile_process.files_struct会指向不同的地址,但是系统会让子进程的files_struct copy父进程的files_struct内容从而继承父进程开始打开的文件
通过阅读源码kernel2.6.x也可以知道 对应函数在copy_files里面
static int copy_files(unsigned long clone_flags, struct task_struct * tsk)
{
...
struct files_struct *oldf, *newf;
struct file **old_fds, **new_fds;
int open_files, size, i, error = 0, expand;
/*
* A background process may not have any files ...
*/
oldf = current->files;
if (!oldf)
goto out;
//这里是pthread_create调用生成线程传入标记 不会新生成files_struct 只是会对应的结构增加一个引用计数
//所以与主线程共享fiels_struct
if (clone_flags & CLONE_FILES) {
atomic_inc(&oldf->count);
goto out;
}
--生成子进程往下走
/*
* Note: we may be using current for both targets (See exec.c)
* This works because we cache current->files (old) as oldf. Don't
* break this.
*/
tsk->files = NULL;
error = -ENOMEM;
newf = kmem_cache_alloc(files_cachep, SLAB_KERNEL);
if (!newf)
goto out;
atomic_set(&newf->count, 1);
spin_lock_init(&newf->file_lock);
newf->next_fd = 0;
newf->max_fds = NR_OPEN_DEFAULT;
newf->max_fdset = __FD_SETSIZE;
newf->close_on_exec = &newf->close_on_exec_init;
newf->open_fds = &newf->open_fds_init;
newf->fd = &newf->fd_array[0];
spin_lock(&oldf->file_lock);
open_files = count_open_files(oldf, oldf->max_fdset);
expand = 0;
/*
* Check whether we need to allocate a larger fd array or fd set.
* Note: we're not a clone task, so the open count won't change.
*/
if (open_files > newf->max_fdset) {
newf->max_fdset = 0;
expand = 1;
}
if (open_files > newf->max_fds) {
newf->max_fds = 0;
expand = 1;
}
/* if the old fdset gets grown now, we'll only copy up to "size" fds */
if (expand) {
spin_unlock(&oldf->file_lock);
spin_lock(&newf->file_lock);
error = expand_files(newf, open_files-1);
spin_unlock(&newf->file_lock);
if (error < 0)
goto out_release;
spin_lock(&oldf->file_lock);
}
old_fds = oldf->fd;
new_fds = newf->fd;
memcpy(newf->open_fds->fds_bits, oldf->open_fds->fds_bits, open_files/8);
memcpy(newf->close_on_exec->fds_bits, oldf->close_on_exec->fds_bits, open_files/8);
//各种拷贝对应的结构数据
for (i = open_files; i != 0; i--) {
struct file *f = *old_fds++;
if (f) {
get_file(f);
} else {
/*
* The fd may be claimed in the fd bitmap but not yet
* instantiated in the files array if a sibling thread
* is partway through open(). So make sure that this
* fd is available to the new process.
*/
FD_CLR(open_files - i, newf->open_fds);
}
*new_fds++ = f;
}
spin_unlock(&oldf->file_lock);
....
tsk->files = newf; //新的结构赋值
...
}
通过strace 我们刚才的程序可以看到传入的标记
strace ./thread 2>&1 | grep CLONE_FILES
clone(child_stack=0x7fe620f1cff0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7fe620f1d9d0, tls=0x7fe620f1d700, child_tidptr=0x7fe620f1d9d0) = 6434
本文探讨了在创建新线程后,主线程如何共享文件描述符的问题。通过示例代码展示了即使新线程在主线程打开文件后,依然能通过共享的files_struct结构访问并读写文件。分析了在生成线程时,files_struct如何保持共享,而在生成进程时,files_struct会被复制。结论是pthread_create创建的线程与主线程共享files_struct,使得线程间能使用相同的文件句柄。
1101

被折叠的 条评论
为什么被折叠?



