文件IO

一、标准IO操作

man 1 系统命令

man 2 系统调用

man 3 库函数

标准IO:c库提供的一组函数接口,具有缓存机制,提高编程效率。围绕着流(FILE*)来进行操作。

FILE *fp;
//FILE是在stdio.h中定义的结构体类型,封装了与文件有关的信息,如文件句柄、位置指针及缓冲区等,缓冲文件系统为每个被使用的文件在内存中开辟一个缓冲区,用来存放文件的有关信息,这些信息被保存在一个FILE结构类型的变量中,fp是一个指向FILE结构体类型的指针变量 

1.函数

1.fopen()

FILE *fopen(const char *path, const char *mode)//当打开文件时出现了错误,fopen函数都将返回NULL
/*
FILE定义位置/usr/include/libio.h
path:文件路径
mode:打开文件的操作权限
	r:只读方式打开,文件必须存在
	r+:读写方式打开,文件必须存在
	w:只写方式打开,可自动创建文件
	w+:读写方式打开,可自动创建文件
	a:只写方式(追加方式),可自动创建文件
	a+:读写方式打开(追加方式),文件必须存在
*/
    //w w+ 打开文件后文件内容自动清除,无论是否存在读/写文件操作,都会自动清除

2.fread()

读出文件,正确返回读取的数据个数,错误返回小于0的数

3.fwrite()

写入文件,正确返回写入的数据个数,错误或读取到文件末尾返回0

4.fclose()

关闭文件,若文件关闭成功则返回0,否则返回非0

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
int fclose(FILE *fp);
/*
ptr,数组的首地址
size,数据所占字节数
nmemb,最多允许读取的数据块个数(每个数据块size个字节),函数返回的是实际读到的数据块个数
stream,文件指针,fopen的返回值
用户指定的内存块大小,最小为1字节,最大为整个文件
*/
#include<stdio.h>
#include <string.h>
int main()
{
	FILE *fp;
	fp=fopen("1.txt","r");
	if(fp==NULL)
	{
		printf("fopen faild\n");
		return -1;
	}
    char buf[20]={0};
    int ret=0;
   	ret=fread(buf,sizeof(char),sizeof(buf),fp);
    //返回值:正确返回写入的数据个数,错误或读取到文件末尾返回0
     if(ret1<=0)
		if(feof(fp1)==0)
	{
		printf("fread faild\n");
		return -1;
	}
    puts(buf);// 等价于fwrite(buf,sizeof(char),ret,stdout);
}

5.feof()

判断是否文件末尾(非0为真)

int feof(FILE *stream);判断是否文件末尾(非0代表末尾)
int ferror(FILE *stream);判断是否出错(非0代表出错)
    stream:文件流指针
//函数原型:int feof(FILE *fp);
//函数功能:检测流上的文件结束符,如果文件结束,则返回非0值,否则返回0,文件结束符只能被clearerr()函数清除


//将文件1.txt的内容写入到2.txt
#include<stdio.h>
#include <string.h>
int main()
{
	FILE *fp1,*fp2;
	fp1=fopen("1.txt","r");
	fp2=fopen("2.txt","w+");
	if(fp1==NULL||fp2==NULL)
	{printf("fopen faild\n");return -1;}
	char buf1[32]={0};
	int ret1=0;
   	ret1=fread(buf1,sizeof(char),sizeof(buf1),fp1);
    if(ret1<=0)
		if(feof(fp1)==0)
		{printf("fread faild\n");return -1;}
	puts(buf1);
	int ret2=0;
	ret2=fwrite(buf1,sizeof(char),ret1,fp2);
	if(ret2<=0)
	{printf("fwrite faild\n");return -1;}
fclose(fp1);
fclose(fp2);
}

6.fseek()

文件流读写位置的定位

int fseek(FILE *stream, long  offset,int whence);
FILE *stream, 文件流指针
long  offset,偏移量,读写位置初始位置的设置
int whence,基础定位位置
    SEEK_SET,开头
	SEEK_CUR,当前
	SEEK_END,末尾
void rewind(FILE *stream);//直接定义在文件开头等价于fseek(fp,0,SEEK_SET)

struct data
{int num;char sex;};
int main()
{
	struct data stu;
	stu.num=12;
	stu.sex='m';
	FILE *fp=fopen("1.txt","w+");
	if(fp==NULL)
	{printf("error\n");return -1;}
	if(fwrite(&stu,sizeof(stu),1,fp)<0)
	{printf("error\n");return -1;}
	struct data stu1;
	fclose(fp);
	fp=fopen("1.txt","r+");
	if(fp==NULL)
	{printf("error\n");return -1;}
	if(fread(&stu1,sizeof(stu1),1,fp)<0)
	{printf("error\n");return -1;}
	printf("%d-----%c\n",stu1.num,stu1.sex);
	fclose(fp);
}

7.fgetc()和fputc()

/*
fgetc()函数
函数原型:int fgetc (FILE *fp);
函数功能:用于从一个以只读或读写方式打开的文件上读字符,从fp所值的文件中读取一个字符,并将位置指针指向下一个字符,若读取成功,则返回该字符,
若读取不成功则返回EOF(EOF是一个符号常量,stdio.h中定义为-1)

fputc()函数
函数原型:int fputc(int c, FILE *fp);
fp是由函数fopen()返回的文件指针,c是要输出的字符(尽管C定义为int型,但只写入低字节)
函数功能:该函数的功能是将字符c写到文件指针fp所指的文件上中,若写入错误返回EOF,否则返回字符c
*/
int fgetc(FILE *stream);
int fputc(int c, FILE *stream);
int main()
{
	char ch[100];
	ch=fgetc(stdin);
	if(ch==EOF)
		if(feof(stdin)==0)
		{printf("fgetc error\n");return -1;}
	if(fputc(ch,stdout)==EOF)
	{printf("fgetc error\n");return -1;}
}

8.fgets()和fputs()

/*
fgets()函数
函数原型:char *fgets(char *s,int n,FILE *fp);
函数功能:该函数从fp所指的文件中读取字符串并在字符串末尾添加‘\0’,然后存入s,最多读n-1个字符,当读到回车换行符、到达文件尾或读满n-1个字符时,就停止读取
函数返回该字符串的首地址,即指针s的值,读取失败返回空指针NULL
(与gets()不同的是,fgets()从指定的流读取字符串,读到换行符时将换行符也作为字符串的一部分读到字符串中来)fgets若没遇到换行符,会接着从前一次的位置继续读入n-1个字符,只要是文本流没关闭。

fputs()函数
函数原型:fputs(_In_z_ const char * _Str, _Inout_ FILE * _File);
str是要输出的字符串,fp是文件指针,字符串末尾'\0'不输出
函数功能:将字符串输出到指针fp所指的文件中
(与puts()不同的是,fputs()不会在写入文件的字符串末尾加上换行符'\n')
*/
//遇到换行符返回
char *fgets(char *s, int size, FILE *stream);
	char *s, 字符串首地址
	int size, 需要读取字符串大小
	FILE *stream, 文件流指针
 //当文件一行的数据大于size,读到的字节数为size
 //当文件一行的数据小于size,遇到换行符结束,换行符也被读取到了数组
int fputs(const char *s, FILE *stream);
int main()
{
	char ch[32]={0};
	FILE *fp=fopen("1.txt","r+");
	if(fp==NULL)
		return -1;
	fgets(ch,sizeof(ch),fp);
	if(fputs(ch,stdout)==EOF)
	{printf("fgets error\n");return -1;}
}

9.fflush()

/*函数原型:int fflush(FILE *fp);
函数功能:清除缓冲区的内容,对于输出流来说,fflush函数将已经写到缓冲区但尚未写入文件的所有数据写到文件中,对输入流来说,其结果是未定义的,
如果在写的过程中发生错误,则返回EOF,否则返回0
*/
fflush(stdin);//刷新标准输入缓冲区,把输入缓冲区里的东西丢弃[非标准]
fflush(stdout);//刷新标准输出缓冲区,把输出缓冲区里的东西打印到标准输出设备上
fflush(NULL);//将清洗所有的输出流

10.ftell()

函数 ftell 用于得到文件位置指针当前位置相对于文件首的偏移字节数。在随机方式存取文件时,由于文件位置频繁的前后移动,程序不容易确定文件的当前位置。

2.预定义流

不需要用fopen打开,在一个进程开始运行的时候就已经存在了

1.stdin:标准输入(看作文件)

2.stdout:标准输出(屏幕)

3.stderr:标准错误输出流

//以下语句功能相同
puts(buf);
printf("%s\n",buf);
fwrite(buf,sizeof(char),ret,stdout);

gets(str);
scanf("%s",str);
fread(str,sizeof(char),sizeof(str),stdin);

stdout – 标准输出设备 stdout。
stderr – 标准错误输出设备
两者默认向屏幕输出。
但如果用转向标准输出到磁盘文件,则可看出两者区别。stdout输出到磁盘文件,stderr在屏幕。

在默认情况下,stdout是行缓冲的,他的输出会放在一个buffer里面,只有到换行的时候,才会输出到屏幕。而stderr是无缓冲的,会直接输出。

//作业:文件行数,文件大小
//统计文件行数
int main()
{
	FILE *fp1=fopen("/home/linux/MyProgram/sjj5.c","r+");
	if(fp1==NULL)
		return -1;
	int num=0;
	char tmp[1024]={0};//行缓存大小1kb=1024byte;tem[]小于1024
	while(fgets(tmp,sizeof(tmp),fp1)!=NULL)
			num++;
    //fgets遇到\n会结束,另一种写法是判断tmp[strlen(buf)-1]=='\n';	
    fclose(fp1);
	printf("一共有%d行\n",num);
}
////统计文件大小
int main()
{
	FILE *fp1=fopen("1.txt","r");
	if(fp1==NULL)
		return -1;
	int num=0;
    //法一
	//while(fgetc(fp1)!=EOF)
	//		num++;
    //法二
   // char buf[1024];
    //while(1)
   // {
      //  num=fread(buf,sizeof(char),sizeof(buf),fp);
     //   num+=num;
      //  if(ret!=sizeof(buf))//或可写成if(num==0) break;
     //   { ret+=ret; break;}
  //  }
//法三
fseek(fp 0,SEEK_END);
sum=ftell(fp);//函数 ftell 用于得到文件位置指针当前位置相对于文件首的偏移字节数。在随机方式存取文件时,由于文件位置频繁的前后移动,程序不容易确定文件的当前位置。
  fclose(fp1);
	printf("字数为:%d\n",num);
}

//在C语言库函数中有stat函数,可以获取文件的基本信息,其中就有文件大小。
#include <sys/stat.h>//包含头文件。
int file_size(char* filename)//获取文件名为filename的文件大小。
{
struct stat statbuf;
int ret;
ret =stat(filename,&statbuf);//调用stat函数
if(ret != 0) return -1;//获取失败。
return statbuf.st_size;//返回文件大小。
}

二、标准IO缓存

1.全缓存

//更改文件流的缓存类型
int setvbuf(FILE *stream, char *buf, int mode, size_t size);
FILE *stream, 文件流指针
char *buf, 缓存首地址,默认为NULL
int mode, 
	_IONBF unbuffered,无缓存
	_IOLBF line buffered  1024byte
	_IOFBF fully buffered  4096byte
size_t size,当第二个参数为NULL时,此参数缺省为0.buf对应sizeof(buf)
//The  function  setvbuf()  returns  0  on  success.
if(setvbuf(stdout,NULL, _IOFBF, 0)!=0)
    fprintf(stderr,"setvbuf error\n");

2.行缓存

标准输入与标准输出

//比较区别
int main()//直接死循环,不会打印
{
	printf("hello");
    //fflush(stdout);刷新行缓存,加入之后打印完之后在死循环,等价于printf("\n")
	while(1);
}
int main()//会打印,之后在死循环
{
	printf("hello\n");
	while(1);
}
//内存hello-->行缓存区域-->标准输出(stdout)F
/*行缓存输出条件:
1.换行符\n,
2.缓存区域溢出,
3.强制刷新fflush(stdout),
4.程序退出
5.其他函数要使用缓冲区如fread();行缓存大小1kb=1024byte;
fflush(stdout);//刷新行缓存
//如何计算行缓存区的大小?
1.FILE结构体中 _IO_BUF_END-_IO_BUF_BASE
2.一个一个试

3.无缓存

标准错误输出

三、文件输入输出

1.fprintf(),sprintf()

int fprintf(FILE *stream, const char *format, ...);
FILE *stream, 
const char *format,输入到流的内容f
fprintf(stderr,"setvbuf error\n");

int sprintf(char *str, const char *format, ...);
char *str, 数组首地址
const char *format,要输入数组的内容
char str[32];
int l=2,t=6,h=9;
sprintf(str,"l%dt%dh%d",l,t,h);//应用于数据的发送,将数据按照固定格式转换成字符串的形式发送

2.fscanf(),sscanf()

//将数据按照固定格式输入输出
int main()
{
	char str[32];
	int t=4,h=8,l=4;
	FILE *fp=fopen("1.txt","w+");
	sprintf(str,"t%dh%dl%d",t,h,l);//按照固定格式输入到str
	fwrite(str,sizeof(char),strlen(str),fp);//将str中的内容写到1.txt中
	fseek(fp,0,SEEK_SET);//将文件指针指向文件起始位置
	char str1[32]={0};
	fread(str1,sizeof(char),sizeof(str1),fp);//将1.txt的内容读到str1中
	int t1,t2,t3;
	sscanf(str1,"t%dh%dl%d",&t1,&t2,&t3);//将str1的数据输出给对应的变量
	printf("tem:%d hum:%d light:%d\n",t1,t2,t3);
    fclose(fp);
	return 0;
}

四、系统调用与库函数

1.系统调用

用户空间进程访问内核的接口
把用户空间从底层的硬件编程中解救出来
极大的提高了系统的安全性
是操作系统的一部分

2.库函数

库函数为了实现某个功能而封装起来的API集合。提供统一的编程接口

3.文件IO

//什么是文件IO?
1.不带缓存IO
2.IO函数是POSIX和XPG3的组成部分
3.通过文件描述符来访问文件(标准IO采用文件流指针访问)
//文件描述符是一个非负整数,是文件IO操作文件的唯一接口
int fd;
//预定义
0---STDIN_FILENO---标准输入
1---STDOUT_FILENO---标准输出
2---STDERR_FILENO---标准错误输出

4.文件IO基本函数

1.open()

int open(const char *pathname, int flags);//只打开文件
int open(const char *pathname, int flags, mode_t mode);//判断文件是否存在,不在则创建
	const char *pathname, 文件名 
	int flags, 打开的操作权限O_RDONLY,O_WRONLY,O_RDWR,O_APPEND,O_CREAT等
	mode_t mode,被创建文件的属性权限(可读,可写,可执行),只有在flags有O_CREAT时才会有mode参数,用八进制表示(777),会受到掩码(umask)的影响
        umask是chmod配套的,总共为4位(gid/uid,属主,组权,其它用户的权限)为了控制默认权限,不要使默认的文件和目录具有全权而设的。
open()and creat() return the new file descriptor,or -1 if an error occurred.
//void perror(const char*s);将上一个函数发生的错误原因输出到标准错误中stderr;s为用户的提示信息,会先被打印出来,后面会紧跟着错误原因字符串,此错误原因依照全局变量errno的值来决定要输出的字符串。在库函数中有个errno变量,每个errno值对应着以字符串表示的错误类型。当你调用"某些"函数出错时,该函数已经重新设置了errno的值。perror函数只是将你输入的一些信息和errno所对应的错误一起输出。

2.read()

ssize_t read(int fd, void *buf, size_t count);
	int fd, open的返回值,文件描述符
	void *buf, 数组首地址
	size_t count,数据个数
//返回值:0文件末尾,-1出错,成功字节数

文件IO不带缓存,通常用来操作linux上的特殊文件类型。open(),read(),write(),close()。例如:字符设备与块设备

标准IO带缓存,效率高,通常用来操作普通文件。fopen(),fread(),fwrite(),fclose()。

作业:1.文件的加密

//文件加密
#include <fcntl.h>
#define SIZE 10
void VersionFun(char *buf,int len)
{
	int i=0;
	for(i=0;i<len/2;i++)
	{
		buf[i]=buf[i]^buf[len-1];
		buf[len-1]=buf[i]^buf[len-1];
		buf[i]=buf[i]^buf[len-1];
	}
}
int main(int argc,char *argv[])
{
	if(argc!=2)
		fprintf(stderr,"Usage:%s <filename>\n",argv[0]);
	int fd=open(argv[1],O_RDWR);
	if(fd==-1)
	{
		perror("open");
		return -1;
	}
	//文件加密方法:异或,加减一个数,逆序
	char buf[SIZE]={0};
	int ret=0;
	ret=read(fd,buf,SIZE);
	if(ret==-1)
	{
		perror("read");
		return -1;
	}

	VersionFun(buf,SIZE);
	lseek(fd,0,SEEK_SET);
	if(write(fd,buf,SIZE)==-1)
	{
		perror("write");
		return -1;
	}
	close(fd);
}

2.文件的复制

//实现文件的复制
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
	if(argc!=3)
	{fprintf(stderr,"Usage:<filename1> <filename2>\n",argv[0],argv[1]); return -1;}
	struct stat statbuf;
	stat(argv[2],&statbuf);
	int size=statbuf.st_size;
	int fp1=open(argv[1],O_RDWR|O_TRUNC|O_CREAT,0664);
	int fp2=open(argv[2],O_RDONLY);
	if(fp1<0||fp2<0)
	{perror("open"); return -1;}
	char buf[size];
	int ret1=read(fp2,buf,sizeof(buf));
	if(ret1<0)
	{perror("read"); return -1;}
	if(write(fp1,buf,ret1)<0)
	{perror("read"); return -1;}
	memset(buf,0,sizeof(buf))
	close(fp1);
	close(fp2);
	return 0;	
}

3.stat()

//将参数path所指的文件状态, 复制到参数buf所指的结构中 
int stat(const char *path, struct stat *buf);
struct stat {
    dev_t st_dev;//文件的设备编号
    ino_t st_ino;//节点
    mode_t st_mode;//文件的类型和存取的权限
    nlink_t st_nlink;//连到该文件的硬连接数目,刚建立的文件值为1
    uid_t st_uid;//用户ID
    gid_t st_gid;//组ID
    dev_t st_rdev;//(设备类型)若此文件为设备文件,则为其设备编号
    off_t st_size;//文件字节数(文件大小)
    unsigned long st_blksize;//块大小(文件系统的I/O 缓冲区大小)
    unsigned long st_blocks;//块数
    time_t st_atime;//最后一次访问时间
	time_t st_mtime;//最后一次修改时间
	time_t st_ctime;//最后一次改变时间(指属性)
};
int main(int argc,char **argv )
{
    struct stat file;
    if(stat(argv[1],&file)==-1)
    {perror("stat); return -1;} 
}

4.目录操作

1.opendir()

//DIR *opendir(const char *name);
int main()
{
    DIR *d=opendir(".");
    if(d==NULL)
    {perror("opendir"); return -1;}
    
//struct dirent *readdir(DIR *dirp);
struct dirent *dir;
while(dir=readdir(d))
{
    if(dir->d_name[0]!='.')//不打印隐藏文件
    {
		printf("%s",dir->d_name)
            strcat(argv[1],dir->d_name)
         FileStat(argv[1]);
    }
}
}

2.readdir()

//struct dirent *readdir(DIR *dirp);
struct dirent *dir;
while(dir=readdir(d))
{
    if(dir->d_name[0]!='.')//不打印隐藏文件
		printf("%s",dir->d_name)
}

5.文件属性函数

stat()
lstat()
fstat())
//可以用获取改文件的属性, 比如: 大小, 文件类型, 链接数, 文件属性权限等.
int stat(const char *path, struct stat *buf);
	path: 文件路径
	buf:   文件属性结构体的首地址.
	返回: 错误 -1  正确 0
lstat(): 可区分链接文件与普通文件 参数与stat() 一样
int fstat(int fd, struct stat *buf);
	fd: 文件描述符
	buf:   文件属性结构体的首地址.
//在获取时间时要使用的两个函数time();localtime();

6.目录操作

opendir()
DIR *opendir(const char *name);
	name:目录名
	返回值:正确返回 目录流指针  错误NULL
readdir()
struct dirent *readdir(DIR *dirp);
	dirp:目录流指针
	返回值: 正确返回目录结构体(包含了目录信息描述);错误与读取到目录流尾部 都返回NULL.

7.readlink()

//获取链接文件的路径
size_t  readlink(const  char  *path,  char *buf,size_t bufsiz);
const  char  *path,  链接文件名或路径
char *buf,获取到链接文件的链源文件的接路径
size_t bufsiz,存放路径字符串的空间大小
格式如下:
  printf("\033[字背景颜色;字体颜色m 字符串 \033[0m" );
例子:
	printf("\033[1m\033[45;33m HELLO_WORLD \033[0m\n");
颜色代码: QUOTE: 
字背景颜色范围: 40--49  颜色: 30—39             
 40: 黑                           30: 黑                 
 41: 红                           31: 红                
 42: 绿                           32: 绿                
 43: 黄                           33: 黄                
 44: 蓝                           34: 蓝               
 45: 紫                           35: 紫                 
 46: 深绿                       36: 绿                 
 47: 白色                       37: 白色
ANSI控制码:
 QUOTE: 
   \033[0m   关闭所有属性 关闭之前通过SNSI控制码获得的属性 遇到\033[0m会关闭.可以看作\033...\033[0m是配对的。
   \033[1m   设置高亮度      
   \033[4m   下划线      
   \033[5m   闪烁      
   \033[7m   反显      
  \033[8m   消隐    
  \033[30m   --   \033[37m   设置前景色     
  \033[40m   --   \033[47m   设置背景色      
  \033[nA   光标上移n行     
  \03[nB   光标下移n行      
  \033[nC   光标右移n行     
  \033[nD   光标左移n行

五、进程

1.静态库和动态库

1.静态库

1.静态库:在程序编译时链接。但是生成可执行文件包含了库,所以大小较大。
生成指令:ar 用法:ar crs + 库名 +目标文件.o文件
	库名:以lib开头,文件类型.a
	r:在库中插入模块(替换),当插入模块存在则替换
	c:无论是否存在,都创建库
	s:创建目标文件索引,创建较大的库时可节约时间
使用过程:gcc test.c -L./ -lmyfun;
	-L./:告诉编译器在当前目录下去寻找库(静态库的路径)
	-lmyfun:链接的库名,注意:没有lib开头,也不需要后缀.a
	
	gcc -c test.c //生成.o文件
	ar crs libmylib.o test.o fun1.o fun2.o... 
	gcc test.c -L./ -lmylib;

2.动态库

1.动态库:在程序执行时链接。但是生成可执行文件没有包含库,所以大小较小。
生成指令:采用gcc来生成动态库,选项-shared
    gcc -fPIC -shared -o libfun.so fun.o;
	动态库以.so结尾。-fPIC:生成与地址无关的链接库。-shared 生成动态库的选项。
使用过程:由于动态库是在执行时链接,所以要把库放到系统默认路径(/usr/lib)下。
    gcc -fPIC -c fun1.c... -o fun.o //生成.o文件
    gcc -fPIC -shared -o libfun.so fun.o;//生成动态库
	gcc test.c -L./ -lfun //编译文件
    sudo cp  libfun.so /usr/lib//将动态库放到系统默认路径(/usr/lib)下。

2.进程基础

1.进程是程序运行的一个过程,过程包括资源的调度,销毁等。资源分配的
2.进程与程序的区别:
	程序是有序指令的集合,是静态的。
	进程是程序执行的过程,是动态的。
3.进程结构:
	每一个进程都有一段4G的虚拟内存空间(虚拟内存由系统以及cpu决定,与物理内存无关)
	正文段:包含程序中的具体代码
	数据段:全局变量,静态变量
	堆:程序手动开辟的空间
	栈:局部变量
	PCB(task struct结构体):进程控制块
	进程的标识ID(进程ID号)进程的调度过程 资源分配等

3.进程调度

1.cpu分配时间片,通过时间片来切换
2.并发现象:多个应用程序并不是一起运行的,而是切换运行的过程,只是时间太短,感觉不到卡顿
3.并行:真正实现同一时间运行多个进程(处理器多核)
4.调度算法:1.用队列来实现 2.优先级算法  3.短时间算法
top命令的NI列代表优先级:越小越高

4.进程的状态

1.查看所有进程状态:ps aux
USER PID %CPU %MEM VSZ RSS TTY  STAT START
2.动态查看进程状态:top

3.状态:
运行态(就绪态)R:当前正在运行的进程
	R+:代表前台进程   R:后台进程(使进程运行到后台 ./a.out &)
等待态:
	可中断S:进程进入阻塞状态,需要由信号来唤醒(比如阻塞函数getchar,需要输入一个字符)
	不可中断D:不能通过信号唤醒(如硬件中断,硬件故障)。
停止态T:进程处于暂停,可以发生信号进入停止态(kill -18 进程号)将停止恢复到后台 T->R
	jobs:查看后台进程;   fg pid:R->R+; bg pid:T->R;
僵尸态Z:进程已经结束,资源未回收,不应该存在

5.进程的优先级

top命令的NI列代表优先级:越小越高,取值范围-20~19
nice 命令:可以修改进程的优先级
	用法:nice -n 进程名   eg:nice --9 ./a.out
renice 指令: 可以修改正在运行进程的优先级
	用法: renice n -p pid
	n:  代表要修改的优先级 级数 (与nice的区别没有 符号-)
	-p:代表进程的pid号
	-g: 修改同组所有进程的优先级
	-u:修改该用户所有进程的优先级

6.进程函数

1.fork()和vfork()

//创建一个新的进程
 pid_t fork(void);
 pid_t pid=fork();
返回值 :如p1=fork,成功会返回两个值p1,p2,p2是p1的子进程
pid<0:代表出错;
=0 代表子进程;
>0 代表父进程,此时pid为子进程的id号
重点:子进程会完整拷贝父进程的所有资源到4g虚拟内存空间,拷贝后父进程与子进程资源独立,改变其中一个资源数据,另一个不会改变。
    互不影响。
    
pid_t vfork(void);
特点:
    1.与父进程共享资源
    2.子进程先“结束”再运行父进程,子进程必须要有返回信号,要调用exit()函数。“结束”不是代表终止进程,而是有结束信号,包括return模块结束信号和exit进程结束信号
    3.子进程即使有延时,父进程也需要等待
    注意:通常vfork的使用都需要配合exec函数族来使用
int main()
{
    int a=10;
	pid_t pid=vfork();
	if(pid<0)
	{ perror("vfork");return -1; }
	else if(pid==0)
	{ a=19;printf("child a=%d\n",a); 
     exit(0); //内核判断子进程结束
    }
	else
	{ sleep(1); printf("parents a=%d\n",a);exit(0); }
}
打印结果:
child a=19
parents a=19
//体现了vfock的进程间内存共享
//
COW(copy-on-write)机制:
    fork后物理内存还没有实现完整的拷贝,当子进程或父进程要修改数据时就拷贝一份物理内存!给子进程.(如果全程只是读取则不会拷贝)

2.getpid()和getppid

//获取进程的进程号
pid_t getpid(void);//返回进程号
pid_t getppid(void);//返回父进程进程号
//父进程和子进程不能确定谁先执行,都在争抢cpu资源,若父进程先执行,之后处于停止态,子进程会成为“孤儿进程”,由init(上帝)进程接管。
//可以在父进程中执行睡眠一段时间(sleep(1)),可提高子进程先执行的概率。

3.exit()

void exit(int status);//进程退出  //相当于man的return 0,规范化
exit(0):代表正常结束进程
exit(0):非错误结束,通常使用exit(1)exit(-1)
//在子进程中:return返回与exit结束进程  给内核的信号是不同的。
//return只是代表函数模块结束(不能代表进程结束)。
//exit可以终止整个进程
_exit():直接退出,不会将缓存内容传给内核
exit():会将缓存内容传给内核

4.wait()和waitpid()

pid_t wait(int *status);//等待任意子进程退出,并且回收资源,处理僵尸进程
	int *status:获取子进程退出的状态,通常为NULL,不考虑返回状态
on success, returns the process ID of  the  terminated child; on error, -1 is returned.正确返回子进程ID
    
    
pid_t waitpid(pid_t pid, int *status, int options);//等待进程退出,并且回收资源.
 pid == -1: 等待任意子进程退出,等同于wait().
当 pid > 0:    等待指定子进程退出(子进程id号为传入的pid值)
当 pid == 0:  等待同组任意子进程退出
当 pid < -1:   等待组号为 pid绝对值中的任意子进程退出.
status: 获取进程的退出状态,  通常也为NULL.
optinos:  WNOHANG 表示用非阻塞方式回收资源
返回值:如果有WNOHANG参数(非阻塞) 当没有子进程退出时 该函数立即返回0.正确返回子进程id号, 错误返回 -10

5.exec函数族

作用:在子进程去运行一个全新的进程.子进程之前拷贝的父进程资源被新进程完全替换(数据段,正文段,,)
int execl(const char *path, const char *arg, ...);
	path:可执行文件位置(包含可执行文件名) 或 shell命令的路径(包含命令名)
	arg: 第一个参数是可执行文件 或 者shell命令
	后面是传递给执行文件 或 shell命令的参数, 必须以NULL结尾.!!
eg:	execl("/bin/ls","ls","-l",NULL)==-1)。shell命令
	 execl("./test","test","hello","world",NULL)。带传参的可执行文件,相当于./test hello world

int execlp(const char *file, const char *arg, ...);
	file:写可执行文件名 和 shell命令名
	rg:第一个参数是可执行文件 或 者shell命令
	后面是传递给执行文件 或 shell命令的参数, 必须以NULL结尾.!!不带路径,默认路径
 eg:  execlp("ls","ls","-a",NULL)==-1)  
        execlp("test","test","hello","world",NULL)
int execv(const char *path, char *const argv[]);
eg:	char *argv[]={"test","hello","student",NULL}
	execv("./test",argv);
int execvp(const char *file, char *const argv[]);
eg:	char *argv[]={"test","hello","student",NULL};
	execv("test",argv);

l: 代表以列表方式传递参数   例如: {"ls" ,"-l" ,NULL}
p:  代表第一个参数不需要写路径,直接写执行文件名 或 shell命令名
v: 不用列表,而是用指针数组的方式传参. 例如: char *argv[] = {"ls", "-l", NULL};

7.守护进程

类似于windows下的服务进程。
1.创建子进程,父进程退出。变成“孤儿”进程,脱离终端控制。
2.创建新的会话。
子进程自己作为新会话的“组长”,脱离之前的会话期,让子进程完全独立。
	进程组:子进程在fork后会拷贝父进程的进程组,会话期。
		   多个进程属于一个组内,组长的id 进程组id
	会话期:多个进程组的集合,通常,会话期的时间:开始于用户的登陆,结束于用户退出。
	接口函数:stepid()
    pid_t setpid(void)
    return value:-1错误

3.设置根目录或者'/tmp'为当前的工作目录
	修改目录:int chdir(const char *path); o success,1 error
	const char *path:要修改的目录
4.修改文件掩码,使文件变得灵活,可以设置任何权限。
	修改掩码: mode_t umask(mode_t mask);
	mode_t mask:需要设置的掩码,守护进程通常为umask(0);
5.关闭文件描述符,关闭从父进程继承下来的文件描述符,由于守护进程不会用到,避免空间浪费
    close();
	int get dtablesize(void);获取当前已打开文件描述符的个数

六、线程

1.线程基础

1.轻量级的进程:线程同样可以实现程序的并发,但是线程的资源都是共享主进程的。
	一个进程多个线程共享的资源:可执行的指令、静态数据(全局变量)、打开的文件描述符、信号处理函数、当前工作目录、用户ID、组ID。
	私有资源:线程ID、PC(程序计数器)和相关寄存器、堆栈、错误号(errno)、信号掩码优先权、执行状态和属性等。
2.进程是资源分配的最小单位,而线程是程序执行的基本单位!!
3.特点:共享进程空间,大大提高了任务切换的效率。多线程编程通过第三方线程库来实现(因此编译时需要去链接第三方库 -lpthread)。

2.线程函数

1.pthread_create()

//线程创建函数
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
	pthread_t *thread, 线程id号,标识线程
	const pthread_attr_t *attr,设置线程属性(缺省为结合属性,即NULL;分离属性)
	void *(*start_routine) (void *),线程函数指针,指向返回值为void *,参数为void *的线程函数。
	void *arg,传递给线程函数的参数。如果不传参则为NULL。
返回值:正确返回0

2.pthread_join()

//等待指定线程退出函数,回收相应的线程资源。
int pthread_join(pthread_t thread, void **retval);
	pthread_t thread, 线程ID
	void **retval,获取线程退出状态,不关心退出状态,传值为NULL

3.pthread_exit()

//线程退出函数,线程中不能直接使用exit()退出
void pthread_exit(void *retval);
	void *retval,线程退出状态,没有退出状态,传值为NULL

4.pthread_self()

//返回线程ID
pthread_t pthread_self(void);
#include<stdio.h>
#include<stdlib.h>
#include <pthread.h>
void *ptherad_fun1(void *arg)
{
	while(1)
	{
		printf("hello ");
		sleep(1);
	}
	pthread_exit(NULL);
}
void *ptherad_fun2(void *arg)
{
	while(1)
	{
		printf("%s\n",(char *)arg);
		sleep(1);
	}
	pthread_exit("fun1 quit sucess");
}
int main()
{
	pthread_t tid1,tid2;
	if(pthread_create(&tid1,NULL,ptherad_fun1 ,NULL)!=0)
	{	perror("pthread_create"); 	exit(-1); }

	if(pthread_create(&tid2,NULL,ptherad_fun2 ,"everyone")!=0)
	{	perror("pthread_create"); 	exit(-1); }
#if 0	
	while(1)
	{
		printf("world\n");
		sleep(1);
	}
#endif
	if(pthread_join(tid1,NULL)!=0) //保证主进程不会结束
	{
		perror("pthread_wait");
		exit(-1);
	}

	if(pthread_join(tid2,NULL)!=0)
	{
		perror("pthread_wait");
		exit(-1);
	}
	return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

吼哈哟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值