复习:
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
-
#include <dirent.h> struct dirent *readdir(DIR *dirp);
功能:读取目录
返回值:成功struct dirent类型指针 到达结尾或失败返回NULLstruct 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
} -
#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;
}
进程系统调用
一、进程查看及创建
-
#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
父 收尸并打印