一、标准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;
}
1924

被折叠的 条评论
为什么被折叠?



