apue.3e UNIX高级环境编程读书笔记1

第十二章 线程控制

关于习题12.1的思考

</pre><pre name="code" class="cpp">#include "apue.h"
#include <pthread.h>

pthread_mutex_t lock1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t lock2 = PTHREAD_MUTEX_INITIALIZER;

void prepare() {
    printf("preparing locks....%d \n", getpid());
    pthread_mutex_lock(&lock1);
    pthread_mutex_lock(&lock2);
}

void parent() {
    printf("parent unlocking locks.%d \n", getpid());
    pthread_mutex_unlock(&lock1);
    pthread_mutex_unlock(&lock2);
}

void child() {
    printf("child unlcoking locks. %d\n", getpid());
    pthread_mutex_unlock(&lock1);
    pthread_mutex_unlock(&lock2);
}

void * thr_fn(void *arg) {
    printf("this is a new thread %d \n", getpid());
    pause();
    return (0);
}

int main() {
    pid_t pid;
    pthread_t tid;

    pthread_atfork(prepare, parent, child);
    pthread_create(&tid, NULL, thr_fn, NULL);
    sleep(2);
    printf("parent fork ..%d\n", getpid());

    if ((pid = fork()) < 0) {
        return -1;
    } else if (pid == 0) {
        printf("child return from fork %d\n", getpid());
    } else {
        printf("parent return from fork %d\n", getpid());
    }

    return 0;
}

编译后执行两次,一次直接数据,一次通过管道重定向到文件

[AdvancedProgramingInUnixEnviroment]$ ./1217
this is a new thread 31855
parent fork ..31855
preparing locks....31855
parent unlocking locks.31855
parent return from fork 31855
child unlcoking locks. 31868
child return from fork 31868
[AdvancedProgramingInUnixEnviroment]$ rm file
[AdvancedProgramingInUnixEnviroment]$ ./1217 > file
[AdvancedProgramingInUnixEnviroment]$ cat file
this is a new thread 32051
parent fork ..32051
preparing locks....32051
parent unlocking locks.32051
parent return from fork 32051
this is a new thread 32051
parent fork ..32051
preparing locks....32051
child unlcoking locks. 32068
child return from fork 32068


可以看出,经过文件重定向后输出结果不同

猜测,在重定向文件输出时,fork后,相当于父进程和子进程两个进程,都会执行输出一次,并且先后重定向到文件中。

而普通情况情况下,都在一个流中直接输出。


为了简便分析,用一个简单的小程序代替上述程序

#include "apue.h"
#include <pthread.h>

int main()
{
    pid_t pid;
    printf("prepare fork %d\n", getpid());

    if ((pid = fork()) < 0) {
        return -1;
    } else if (pid == 0) {
        printf("this is child %d\n", getpid());
        sleep(100);
    } else {
        printf ("this is parent %d\n", getpid());
        sleep(100);
        /*pid = fork();
        if (pid == 0) {
            printf ("this is another child %d\n", getpid());
        } else {
            printf ("this is always parent %d\n", getpid());
        }*/
    }

    return 0;
}


利用linux 的strace命令,可以跟踪一个命令的执行,了解其细节,通过该命令分析。可以发现wirte命令的确不一样,前者是每次write一次到fd 1也就是标准输出上,而后者只write一次。

第一次

fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 6), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f09d29cc000
write(1, "prepare fork 61180\n", 19prepare fork 61180
)    = 19
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f09d29b3770) = 61181
write(1, "this is parent 61180\n", 21this is parent 61180
)  = 21
this is child 61181
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f09d29b3770) = 61182
write(1, "this is always parent 61180\n", 28this is another child 61182
this is always parent 61180
) = 28
munmap(0x7f09d29cc000, 4096)            = 0

第二次

fstat(1, {st_mode=S_IFREG|0664, st_size=0, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3dfd604000
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f3dfd5eb770) = 1525
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f3dfd5eb770) = 1526
write(1, "prepare fork 1524\nthis is parent"..., 65) = 65
munmap(0x7f3dfd604000, 4096) 




接下来看两次执行时,每个线程的输出流的情况,第一次如下

[ AdvancedProgramingInUnixEnviroment]$ ./forktest &
[1] 22334
[AdvancedProgramingInUnixEnviroment]$ prepare fork 22334
this is parent 22334
this is child 22335

[AdvancedProgramingInUnixEnviroment]$
[lijingyi01@cp01-rdqa-dev401.cp01.baidu.com AdvancedProgramingInUnixEnviroment]$ ps -ef | grep fork
1026     22334 54923  0 17:44 pts/6    00:00:00 ./forktest
1026     22335 22334  0 17:44 pts/6    00:00:00 ./forktest
1026     22521 54923  0 17:44 pts/6    00:00:00 grep fork
[AdvancedProgramingInUnixEnviroment]$ ll /proc/22334/fd
total 0
lrwx------  1 lijingyi01 lijingyi01 64 Oct 21 17:44 0 -> /dev/pts/6
lrwx------  1 lijingyi01 lijingyi01 64 Oct 21 17:44 1 -> /dev/pts/6
lrwx------  1 lijingyi01 lijingyi01 64 Oct 21 17:44 2 -> /dev/pts/6
[AdvancedProgramingInUnixEnviroment]$ ll /proc/22335/fd
total 0
lrwx------  1 lijingyi01 lijingyi01 64 Oct 21 17:45 0 -> /dev/pts/6
lrwx------  1 lijingyi01 lijingyi01 64 Oct 21 17:45 1 -> /dev/pts/6
lrwx------  1 lijingyi01 lijingyi01 64 Oct 21 17:44 2 -> /dev/pts/6

第二次

[ AdvancedProgramingInUnixEnviroment]$ ./forktest > file4 &
[1] 36359
[AdvancedProgramingInUnixEnviroment]$ ps -ef  | grep fork
1026     36359 54923  0 17:48 pts/6    00:00:00 ./forktest
1026     36360 36359  0 17:48 pts/6    00:00:00 ./forktest
1026     36385 54923  0 17:48 pts/6    00:00:00 grep fork
[AdvancedProgramingInUnixEnviroment]$ ll /proc/36360/fd
total 0
lrwx------  1 lijingyi01 lijingyi01 64 Oct 21 17:48 0 -> /dev/pts/6
l-wx------  1 lijingyi01 lijingyi01 64 Oct 21 17:48 1 -> /home/users/lijingyi01/work/AdvancedProgramingInUnixEnviroment/file4
lrwx------  1 lijingyi01 lijingyi01 64 Oct 21 17:48 2 -> /dev/pts/6
[AdvancedProgramingInUnixEnviroment]$ ll /proc/36359/fd
total 0
lrwx------  1 lijingyi01 lijingyi01 64 Oct 21 17:48 0 -> /dev/pts/6
l-wx------  1 lijingyi01 lijingyi01 64 Oct 21 17:48 1 -> /home/users/lijingyi01/work/AdvancedProgramingInUnixEnviroment/file4
lrwx------  1 lijingyi01 lijingyi01 64 Oct 21 17:48 2 -> /dev/pts/6


可以看出,在重定向到文件时,主进程和子进程都会指定到对应的文件上,那么单个进程的所有输出已开始会缓存到一个buff中,然后都会打印到文件中一次。

但是奇怪的是 

write(1, "prepare fork 1524\nthis is parent"..., 65) = 65

表示写往文件的是字符是长度是65,正好是打印三行的长度,而不是四行。


把程序中printf改为c++中的cout 之后endl清空流后,经过文件重定向后数据的结果就和原来一样了,所以现在清晰的结论就是,输出到shell中时是一个buffer,两个进程共用,输出后就刷新。而重定向到文件时,主进程已开始把第一句话放到输出buffer中,在fork时,这个buffer也被子进程复制,于是子进程中也有了第一句话,然后主进程和子进程所有的数据都到各自的buffer后,然后分别输出到文件中。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值