[学习笔记]达内纪录片day24 文件夹操作和进程基础

    选择打赏方式

一、文件夹操作相关库函数

文件夹的内容就算文件夹里的文件或文件夹

文件夹没有可执行权限,x代表可通过


目录x权限对文件操作的影响


  cd进入该目录 cd进入该目录内目录 ls列出该目录内文件 操作该目录内文件(读/写已存在文件) 操作该目录内文件(新建)
r-- No No No No No
-w- No No No No No
--x Yes Yes No Yes No
r-x Yes Yes Yes Yes No

打开一个文件夹

#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
参数:
name  指定了要打开的文件夹的名字
返回值:
错误  NULL  errno被设置
成功   返回指向文件夹流的指针

关闭一个文件夹流
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);
参数:
dirp  指定了要关闭的文件夹流
返回值:
成功   0
错误  -1   errno被设置

从文件夹流中读取一个条目
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
参数:
dirp 指定了具体的文件夹流  从这个文件夹流中读取一个条目
返回值:
NULL   到达了文件夹流的末尾  errno不改变
错误   设置errno
成功 返回一个地址(文件夹流类型)


struct dirent 结构

struct dirent

{

   long d_ino; /* inode number 索引节点号 */

   off_t d_off; /* offset to this dirent 在目录文件中的偏移 */

   unsigned short d_reclen; /* length of this d_name 文件名长 */

   unsigned char d_type; /* the type of d_name 文件类型 */

   char d_name [NAME_MAX+1]; /* file name (null-terminated) 文件名,最长255字符 */

}


DIR 结构

struct __dirstream

{

void *__fd; /* `struct hurd_fd' pointer for descriptor.   */

char *__data; /* Directory block.   */

int __entry_data; /* Entry number `__data' corresponds to.   */

char *__ptr; /* Current pointer into the block.   */

int __entry_ptr; /* Entry number `__ptr' corresponds to.   */

size_t __allocation; /* Space allocated for the block.   */

size_t __size; /* Total valid data in the block.   */

__libc_lock_define (, __lock) /* Mutex lock for this structure.   */

};

typedef struct __dirstream DIR;



读取文件夹内的文件和文件夹演示:


#include <t_stdio.h>
#include <sys/types.h>
#include <dirent.h>
int main(int argc,char *argv[]){
    struct dirent *dirp;
    DIR *dir=opendir(argv[1]);
    if(!dir)E_MSG("opendir",-1);
    printf("opendir success...\n");
    //从文件夹流中读取一个条目
    while(dirp=readdir(dir))
        printf("file:%s\tinode:%lu\n",dirp->d_name,dirp->d_ino);

    //关闭文件夹流
    closedir(dir);
    return 0;
}


运行结果:

TIM截图20190803163157.png


二、文件操作的杂项

access(2)

chdir(2)
getcwd(3)
mkdir(2)
chmod(2)
rmdir(2)
umask(2)
unlink(2)
link(2)
rename(2)
remove(3)
symlink(3)
basename(3)
dirname(3)


三、进程基础

静态的文件是程序,根据ELF信息被加载到内存是进程,进程是程序的实例

一个程序可以被同时运行为多个进程

每个进程都有自己的pid  pcb (image fd 环境变量)


top(1) pstree(1)ps(1) ps-aux




查看进程信息


进程间的关系   父子关系  兄弟关系
PCB中记录了程序运行使用到的计算机资源的情况。
用户级进程的树根是init进程  1号进程


四、进程的创建

bash下执行了a.out  a.out是bash的子进程
a.out进程继承了bash进程的文件描述符。
0 1 2


创建一个子进程。当前进程是父进程  新建的进程是子进程通过复制父进程创建子进程
#include <unistd.h>
pid_t fork(void);
返回值:
成功   父进程中返回子进程的pid 子进程中返回0
失败   父进程中返回-1 errno被设置

子进程是父进程的不完全副本,子进程的数据区、BSS区、堆栈区(包括I/O流缓冲区),甚至参数和环境区都从父进程拷贝,唯有代码区与父进程共享

fork函数成功返回以后,父子进程各自独立运行,其被调度的先后顺序并不确定,某些实现可以保证子进程先被调度
fork函数成功返回以后,系统内核为父进程维护的文件描述符表也被复制到子进程的进程表项中,文件表项并不复制

注意:fork以后父子进程同时返回,所以子进程fork之前代码不会运行,fork被返回,不会陷入无限迭代。

演示源码:

#include <t_stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(void){
    printf("fork...\n");
    //创建子进程
    pid_t pid=fork();
    if(pid==-1)E_MSG("fork",-1);
    if(pid==0){//子进程执行的代码
        printf("child proccess...%d\n",getpid());

    }else{//父进程执行的代码
        printf("parent proccess...%d\n",getpid());
    }
    sleep(5);
    printf("both...\n");
    return 0;
}


运行结果
TIM截图20190803182914.png


创建轻量级子进程
#include <unistd.h>
pid_t vfork(void)

成功分别在父子进程中返回子进程的PID和0,失败返回-1vfork与fork函数的功能基本相同,只有以下两点区别
vfork函数创建的子进程不复制父进程的物理内存,也不拥有自己独立的内存映射,而是与父进程共享全部地址空间
vfork函数会在创建子进程的同时挂起其父进程,直到子进程终止,或通过exec函数启动了另一个可执行程序
终止vfork函数创建的子进程,不要使用return语句,世不要调用exit函数,而要调用_exit函数,以避免对其父进程造成不利影响
vfork函数的典型用法就是在所创建的子进程里直接调用exec函数启动另外一个进程取代其自身,这比调用fork函数完成同样的工作要快得多


五、进程的终止
return和exit(3)的区别
return如果用在main函数中,代表进程的结束(实质为返回上一层call,main是被start调用执行的)。
如果不是main函数,只是代表函数的调用完毕。并不代表进程的结束。
exit(3)  进程就结束了


让进程正常的终止
#include <stdlib.h>
void exit(int status);
参数:
status:status & 0377(1111 1111)的值返回给父进程。父进程调用wait(2)来回收
返回值:无法返回,程序已被结束


遗言函数
进程终止的时候,才调用的函数。称作遗言函数
进程存在的时候,给进程注册遗言函数,在进程终止的时候再调用。



向进程注册遗言函数

include <stdlib.h>
int atexit(void (*function)(void));
参数:
function   要注册的遗言函数的名字
返回值:
成功   0
错误  非0


#include <stdlib.h>
int on_exit(void (*function)(int , void *), void *arg);
功能:向进程注册遗言函数
参数:
function  指定了遗言函数的名字  这个函数的第一个参数来自于exit(3)的status.
arg 传递给遗言函数的第二个参数
返回值:
成功  0
错误  非0

两个函数的区别,一个无参,一个有参。


注意

1 遗言函数的注册顺序和调用顺序相反
2 同一个遗言函数可以被注册多次,每注册一次都会被调用一次
3 子进程继承父进程的遗言函数


演示源码

atexit:

#include <stdlib.h>
#include <t_stdio.h>
#include <unistd.h>
//遗言函数
void byebye(void){
    printf("bye...bye...\n");
    return;
}

void goodbye(void){
    printf("good...bye...\n");
    return;
}

int main(void){
    //向进程注册遗言函数
    atexit(byebye);
    atexit(goodbye);
    pid_t pid=fork();
    if(pid==-1)E_MSG("FORK",-1);
    sleep(3);
    return 0;
}


运行结果:

TIM截图20190803183838.png

on_exit:

#include <stdio.h>
#include <stdlib.h>

void bye(int n,void *arg){
    printf("n=%d\targ=%s\n",n,(char *)arg);
    return;
}
int main(void){
    //向进程注册遗言函数
    on_exit(bye,"boom!");
    getchar();
    exit(-1);
}


运行结果:

TIM截图20190803184156.png

遗言函数on_exit参数1的值是exit的参数1的值,并没有和0377做&运算。


六、进程资源的回收
进程执行结束的时候,系统需要回收进程的资源。

僵尸进程   如果子进程已经终止了,但是父进程还没有回收子进程的资源。这时候,子进程处于僵尸状态,这种进程成为僵尸进程。
孤儿进程  父进程已
经终止了,子进程过继给init进程。这个子进程就是孤儿进程。

计算机延时有两种情况  忙等   挂起等待


孤儿进程是没有父进程的进程,孤儿进程这个重任就落到了init进程身上,init进程就好像是一个民政局,专门负责处理孤儿进程的善后工作。每当出现一个孤儿进程的时候,内核就把孤 儿进程的父进程设置为init,而init进程会循环地wait()它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候,init进程就会代表党和政府出面处理它的一切善后工作。因此孤儿进程并不会有什么危害。


僵尸进程会造成系统资源的浪费,但是僵尸进程并不是根源,根源在创建它们的父进程,干掉它们的父进程,让他们成为孤儿进程,然后由init处理即可。


阻塞等待子进程的终止。如果子进程已经终止,立即返回。


#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);

参数:
status   存储子进程的终止状态码的。

这个值可以使用以下宏进行检测WIFEXITED(status) 如果子进程正常终止,返回真
WEXITSTATUS(status) 只有在上个宏返回真的时候,才能使用。返回子进程的退出状态码 status&0377的值
WIFSIGNALED(status) 如果子进程被信号终止了,返回真
WTERMSIG(status) 只有在WIFSIGNALED(status)返回真的时候,才能使用这个宏。返回终止子进程的信号编号
返回值: 
成功 返回子进程的pid(终止了的子进程的pid)
失败  -1   errno被设置

演示源码:

#include <t_stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void){
    int s;
    //创建子进程
    pid_t pid=fork();
    if(pid==-1)E_MSG("fork",-1);
    if(pid==0){//子进程执行的代码
        printf("child process...%d\n",getpid());
        getchar();
        exit(-1);
    }else{//父进程执行的代码
        wait(&s);//阻塞等待子进程的终止
        printf("parent process...\n");
        if(WIFEXITED(s))//子进程正常终止
            printf("exit code...%d\n",WEXITSTATUS(s));
        if(WIFSIGNALED(s))//子进程被信号打断
            printf("signum:%d\n",WTERMSIG(s));
    }
    return 0;
}


运行结果:
TIM截图20190803191819.png


阻塞等待任意进程

默认等待子进程的终止,等待的行为可以使用options修改。


pid_t waitpid(pid_t pid, int *status, int options);

参数:
pid:  指定了要回收的子进程的pid
<-1  |pid|  指定了一个组id。等待这个进程组中的子进程的终止
-1  等待任意子进程
0  等待和父进程同组的子进程
>0  指定了要等待的子进程的pid

status:  同wait(2)中的参数
options:
WNOHANG   如果没有子进程终止,立即返回。 非阻塞
0   默认动作  阻塞
返回值:
成功   返回终止了的子进程的pid。
         如果指定为非阻塞,要等待的子进程也存在,但是没有子进程终止,返回0
错误  -1   errno被设置

演示源码

#include <t_stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void){
    int s;
    //创建子进程
    pid_t pid=fork();
    if(pid==-1)E_MSG("fork",-1);
    if(pid==0){//子进程执行的代码
        printf("child process...%d\n",getpid());
        //sleep(20);
        exit(-1);
    }else{//父进程执行的代码
        sleep(1);
        int w=waitpid(-1,&s,WNOHANG);//阻塞等待子进程的终止
        printf("parent process...\n");
        if(w){
            if(WIFEXITED(s))//子进程正常终止
                printf("exit code...%d\n",WEXITSTATUS(s));
            if(WIFSIGNALED(s))//子进程被信号打断
                printf("signum:%d\n",WTERMSIG(s));
        }
    }
    return 0;
}



进程组
进程组中肯定有多个进程。每个进程组都有自己的leader.
leader是进程组中的一个进程。leader的pid就是进程组的id.

THE END!

版权声明:若无特殊注明,本文皆为《 8964CN 》原创,转载请保留文章出处。
本文链接:[学习笔记]达内纪录片day24 文件夹操作和进程基础 http://www.8964cn.net/?post=86
正文到此结束

热门推荐

发表吐槽

你肿么看?

你还可以输入 250 / 250 个字

嘻嘻 大笑 可怜 吃惊 害羞 调皮 鄙视 示爱 大哭 开心 偷笑 嘘 奸笑 委屈 抱抱 愤怒 思考 日了狗 胜利 不高兴 阴险 乖 酷 滑稽

评论信息框

吃奶的力气提交吐槽中...


既然没有吐槽,那就赶紧抢沙发吧!