day 4

这篇博客介绍了Linux系统中关于文件和目录的基本操作,如IO操作、目录流的打开、读取和关闭。同时,讲解了进程的相关知识,包括进程的创建、查看、结束以及进程收尸。还涉及了文件属性的获取,如文件类型判断。最后讨论了进程执行任务的exec族函数。内容涵盖了文件IO、目录IO、进程管理和文件属性检查等多个方面。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

复习:
IO操作:打开-读写-关闭
文件操作
文件IO:open -read- write-close-perror-lseek —fd
标准IO:fopen(r w a)-fclose-fgets-fread-fwrite-fputs-!feof(fp) —FILE *
fgetc-fseek-rewind-fputc-ftell

    目录操作

新知识:
一、目录IO
1.
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
功能:打开目录流
参数:要打开的目录 包括路径
返回值:成功返回目录流指针 失败返回NULL

  1. #include <dirent.h>
    struct dirent *readdir(DIR *dirp);
    

    功能:读取目录
    返回值:成功struct dirent类型指针 到达结尾或失败返回NULL

    struct dirent {
    ino_t d_ino; /* inode number /inode编号 文件编号
    off_t d_off; /
    offset to the next dirent /
    unsigned short d_reclen; /
    length of this record /
    unsigned char d_type; /
    type of file; not supported
    by all file system types /
    char d_name[256]; /
    filename */ 文件名
    };

    //dp = opendir(".")
    //
    while(1)
    {
    sdt = readdir(dp)
    if()
    {break;}
    sdt->d_name
    }

  2.  #include <sys/types.h>
    
    #include <dirent.h>
    
    int closedir(DIR *dirp);
     功能:关闭目录
     
     //////////////////////////////////////////
     #include"my.h"
     int main()
     {
         //打开当前目录
         DIR *pd = opendir(".");
         if(NULL==pd)
         {
             perror("opendir");
             return 0;
         }
    
         struct dirent *pread=NULL;
         while(1)
         {
             //读取目录pd
             pread = readdir(pd);
             if(NULL==pread)//说明读到目录结尾
             {
                 break;
             }
    
             //输出读取到的文件名
             printf("%s\n",pread->d_name);
    
         }
    
         //关闭目录
         closedir(pd);
         return 0;
    
     }
    

练习:打印当前目录下所有文件名,不包括隐藏文件
//////////////////////////////////////////////
#include"my.h"

int main()
{
struct dirent *pret = NULL;

//打开当前目录 
DIR *pdir = opendir(".");
if(NULL==pdir)
{
	perror("opendir");
	return 0;
}	

while(1)
{
    //读取目录 读到一个文件信息
	pret = readdir(pdir);
	if(pret==NULL)
	{
		break;
	}
	if(pret->d_name[0]=='.')//遇到.开头的文件 
	{
		continue;//进入下一次循环
	}
	printf("%s   ",pret->d_name);
}
closedir(pdir);
return 0;

}

4.文件属性
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

   int stat(const char *path, struct stat *buf);//stat("a.txt",&s);
	功能:获取path文件属性 将其保存到buf 不能获取链接文件属性
   返回值:成功0 失败-1
 struct stat {
           dev_t     st_dev;     /* ID of device containing file */
           ino_t     st_ino;     /* inode number */ inode 文件编号
           mode_t    st_mode;    /* protection */文件操作权限 文件类型
           nlink_t   st_nlink;   /* number of hard links */
           uid_t     st_uid;     /* user ID of owner */
           gid_t     st_gid;     /* group ID of owner */
           dev_t     st_rdev;    /* device ID (if special file) */
           off_t     st_size;    /* total size, in bytes */ 文件大小
           blksize_t st_blksize; /* blocksize for file system I/O */
           blkcnt_t  st_blocks;  /* number of 512B blocks allocated */
           time_t    st_atime;   /* time of last access */
           time_t    st_mtime;   /* time of last modification */
           time_t    st_ctime;   /* time of last status change */
       };

    //////////////////
    实例:读取一个文件的属性
    #include"my.h"
    int main()
    {
        struct stat s;

        //获取my.h文件属性
        int ret = stat("my.h",&s);
        if(ret<0)
        {
            perror("stat");
            return 0;
        }

        //输出my.h大小
        printf("%ld\n",s.st_size);
        return 0;

    }

5.获取文件类型 m代表st_mode
S_ISREG(m) is it a regular file? //如果该文件是常规文件 则宏返回真 否则返回假

       S_ISDIR(m)  directory?  //目录文件

       S_ISCHR(m)  character device? //字符设备文件

       S_ISBLK(m)  block device?//块设备文件

       S_ISFIFO(m) FIFO (named pipe)?//管道

       S_ISLNK(m)  symbolic link? (Not in POSIX.1-1996.)//软链接文件

       S_ISSOCK(m) socket? (Not in POSIX.1-1996.)//套接字文件

        代码: 
        #include"my.h"
        int main()
        {
            struct stat s;//
            
            //1.获取文件属性
            int ret = stat("/home",&s);
            if(ret<0)
            {
                perror("stat");
                return 0;
            }
            
            if(S_ISREG(s.st_mode))//说明当前文件是常规文件
            {
                puts("-");
            }
            else if(S_ISDIR(s.st_mode))//说明当前文件是目录文件
            {
                puts("d");
            }
            return 0;
        }

进程系统调用
一、进程查看及创建

  1. #include <sys/types.h>
    #include <unistd.h>

    pid_t getpid(void);//pid 是int别名 
    功能:获取当前调用进程PID
    
    pid_t getppid(void);
    功能:获取当前调用进程的父进程的PID
    
    //////////////////////////////////////
    #include<stdio.h>
    
     int main()
     {
         printf("pid:%d\n",getpid());//获取当前调用进程PID
         printf("ppid:%d\n",getppid());//获取父进程PID
         while(1);
        return 0;
     }
    

#include <unistd.h>

   pid_t fork(void);
功能:创建新进程
返回值:成功调用返回两次 在父进程中返回子进程PID 在子进程中返回0
出错返回-1

/////////////////////////////////////////
#include"my.h"
int main()
{
    //创建子进程:创建后 有两个进程 一个是main所在进程 另一个是新创建的子进程
    fork();
    printf("good good study!\n");//打印两次 说明有两个进程调用printf
    return 0;

}

3.如何区分两个进程 :返回值
#include"my.h"
int main()
{
printf(“main:%d\n”,getpid());
//创建子进程:创建后 有两个进程 一个是main所在进程 另一个是新创建的子进程
pid_t id = fork();//pid <0 >0 =0
if(id>0)//返回的是子进程PID 说明在父进程中
{
sleep(1);//本进程睡眠1s
printf(“father:%d\n”,getpid());
}
else if(0==id)//说明在子进程中
{
printf(“Baby:%d father:%d\n”,getpid(),getppid());
}
else //<0 出错
{
perror(“fork”);
return 0;
}

return 0;

}

4.控制父子进程执行顺序
练习:用fork创建子进程 并查看父子进程PID号分别是多少

总结:
1.子进程复制父进程的数据空间 堆栈 代码段(共享正文段)
2.复制后 两个进程空间完全独立 子进程变量改变 父进程不会影响

二、进程消亡
进程终止
有8种方式使进程终止,其中前5种为正常终止,它们是
1:从 main 返回
2:调用 exit
3:调用 _exit 或 _Exit
4:最后一个线程从其启动例程返回
5:最后一个线程调用 pthread_exit

		异常终止有3种,它们是
		6:调用 abort()
		7:接到一个信号并终止
		8:最后一个线程对取消请求做出响应
	函数名: abort
		功 能: 异常终止一个进程
		用 法: void abort(void);
		头文件:#include <stdlib.h>
		说明:abort函数是一个比较严重的函数,当调用它时,会导致程序异常终止,

#include <stdlib.h>

   void exit(int status);
功能:结束进程 

/////////////////////////////////
#include<stdio.h>
#include<stdlib.h>
int fun()
{
    printf("i am fun...");
    exit(0);//退出整个进程
    //return 0;//退出函数
}
int main()
{
    fun();
    puts("i am main...");
    return 0;
}

#include <unistd.h>

   void _exit(int status);

功能:立即结束进程
///////////////////////////////
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int fun()
{
    printf("i am fun...");
    _exit(0);//立即退出整个进程 没输出        ----不刷新缓存
    //exit(0);//退出整个进程  i am fun...      ---刷新缓存
}
int main()
{
    fun();
    puts("i am main...");
    return 0;

}

三、进程收尸
#include <sys/types.h>
#include <sys/wait.h>

1、   pid_t wait(int *status);
功能:等待一个子进程结束 取得子进程结束的状态 将其保存在status中
返回值:返回被收尸的子进程PID 如果没有子进程返回-1

注意: 
a、父进程调用wait时 父进程会阻塞等待子进程结束
b、如果父进程不关心子进程退出的状态  wait参数可以传NULL 就丢弃结束信息
c、wait后 子进程占用的资源会被释放


参数:status 如果为空 就丢弃结束信息
	WIFEXITED(status) 判断是否正常退出 真--正常退出 假 是异常退出
	WIFSIGNALED(status) 判断进程是否被信号终止 如果是 则返回真
	WTERMSIG(status)   判断是被哪个信号终止的
   
    WEXITSTATUS(status) 取得进程结束时的返回值
    
    ///////////////////////////////////////
    实例1:
    #include"my.h"
    int main()
    {

        //创建子进程:创建后 有两个进程 一个是main所在进程 另一个是新创建的子进程
        pid_t id = fork();//pid  <0   >0 =0
        if(id>0)//返回的是子进程PID 说明在父进程中
        {
            //为子进程收尸 阻塞等待子进程消亡
            pid_t waitpid = wait(NULL);
            printf("father:%d waitpid:%d\n",getpid(),waitpid);
        }
        else if(0==id)//说明在子进程中 
        {
            printf("Baby:%d \n",getpid());
        }
        else //<0 出错
        {
            perror("fork");
            return 0;
        }

        return 0;

    }

///////////////////////////////////////////////
实例2:退出状态
#include"my.h"
int main()
{

        //创建子进程:创建后 有两个进程 一个是main所在进程 另一个是新创建的子进程
        pid_t id = fork();//pid  <0   >0 =0
        if(id>0)//返回的是子进程PID 说明在父进程中
        {
            int sta;

            //为子进程收尸 阻塞等待子进程消亡
            wait(&sta);//sta 存储子进程退出的状态
            printf("father:%d sta:%d\n",getpid(),WIFEXITED(sta));//1 正常退出  
        }
        else if(0==id)//说明在子进程中 
        {
        
            printf("Baby:%d \n",getpid());
        }
        else //<0 出错
        {
            perror("fork");
            return 0;
        }

        return 0;

    }
    
////////////////////////////////////////////////////////////////////////////
实例3:
#include"my.h"
int main()
{

    //创建子进程:创建后 有两个进程 一个是main所在进程 另一个是新创建的子进程
    pid_t id = fork();//pid  <0   >0 =0
    if(id>0)//返回的是子进程PID 说明在父进程中
    {
        int sta;

        //为子进程收尸 阻塞等待子进程消亡
        wait(&sta);//sta 存储子进程退出的状态
        printf("father:%d sta:%d\n",getpid(),WIFEXITED(sta));//1 正常退出 0 异常退出 
        printf("杀手:%d\n",WIFSIGNALED(sta));//1 信号杀死子进程
        printf("凶手:%d\n",WTERMSIG(sta));//8号信号杀死子进程

    }
    else if(0==id)//说明在子进程中 
    {
    
        printf("Baby:%d \n",getpid());
        int x=10;
        int y=0;
        printf("%d\n",x/y);//产生异常
    }
    else //<0 出错
    {
        perror("fork");
        return 0;
    }

    return 0;

}

2、
pid_t waitpid(pid_t pid, int *status, int options);
功能:收尸
参数1:
pid<-1 等待其进程组组长ID(GID)等于pid的绝对值的任意子进程
pid==-1 该进程任意子进程
pid>0 进程ID为pid的进程

参数2 同wait
参数3:
0 等待子进程结束
WNOHANG  不等待

返回值:
>0 被收尸的子进程的PID 
0  参数3用WNOHANG 且没有子进程退出
-1 出错

waitpid(-1,&sta,0)等价于wait(&sta)

////////////////////////////////////////////////
实例:
#include"my.h"
int main()
{

//创建子进程:创建后 有两个进程 一个是main所在进程 另一个是新创建的子进程
pid_t id = fork();//pid  <0   >0 =0
if(id>0)//返回的是子进程PID 说明在父进程中
{
	waitpid(-1,NULL,0);//等价wait(NULL);
	printf("father:%d \n",getpid());

}
else if(0==id)//说明在子进程中 
{
	sleep(5);
	printf("Baby:%d \n",getpid());
}
else //<0 出错
{
	perror("fork");
	return 0;
}

return 0;

}

四、进程执行任务
exec族函数:在本进程中加载另一个程序 并且从头开始执行 本进程完全由新程序替换

#include <unistd.h>
1.int execl(const char *path, const char *arg, …);
格式:execl(可执行程序的路径,可执行程序的名称,参数1,参数2,参数3,…,NULL);
注意:可执行文件路径 必须是which结果 which ls

说明: 他可以根据指定的文件名或目录名找到对应文件,并用它来取代当前进程的数据段、代码段、堆栈段。
在执行完之后,除了进程号没变以外,其他内容都被替换掉了。

使用: 在linux 中使用 exec 函数族有以下两种情况:
(1) 是自己重生。
(2) 通过调用 fork 创建新进程。让新进程执行其他程序(应用普遍)

////////////////////////////////////////////////////////////////
#include<unistd.h>
#include<stdio.h>

int main()
{
puts(“come …”);
execl("/bin/ls",“ls”,"-l",NULL);
puts(“bye…”);//无法输出 本进程被ls程序替换
}

//练习: 子进程每秒打印一个数,一共打印 10 个,执行完毕,父进程收尸。也尝试一次不阻塞的情形。
//练习: 编程实现 执行ls -l 执行完毕后 打印finished
子进程 ls -l
父 收尸并打印

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

encounter♌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值