第一章: 1.linux系统架构
QQ截图20171120080445.png application 应用程序
系统接口(系统调用、C库函数、其它库函数(图片压缩、转换)
操作系统: 进程管理、内存管理、设备管理、文件管理、网络管理
设备驱动(字符型设备驱动、块设备驱动、网络设备驱动)
硬件(键盘、鼠标、显示器、硬盘、内存、USB、串口)
2.错误处理函数
errno 一个整数、标志当前内核中的错语的一个整数
perror 打印当前程序的错语信息(自动根据系统的errno来区分当前到底是哪一个错误)
strerror 用于把一个errno转换成对应的错语的描述字符串
strerror.c 包含头文件<errno.h> 把 int的整数转换成字符串 man error
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void)
{
if(open("1.txt", O_RDONLY) < 0)
{
//printf("%d\n", errno);
printf("%s\n", strerror(errno));
perror("open");
}
return 0;
}
第三章: 对文件的数据进行读写
linux的文件IO(不带缓冲的文件IO、open家族的文件IO)
open 打开或者创建文件,返回的是一个文件描述符
0:代表标准输入(键盘)
1:标准输出(显示器)
2:标准出错(显示器)
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
flags: O_RDONLY:只读的方式打开文件
O_WRONLY:只写方式打开文件
O_RDWR:读写方式打开文件
O_APPEND:追加的方式打开文件
O_CREAT: 如果文件不存在,就创建
O_TRUNC: 如果文件存在就清0
O_NONBLOCK: 以非阻塞的方式打开文件(字符型设备有效)
O_SYNC:每次写入数据到文件、都会等待直到数据真正的写到磁盘中才会继续向下运行
mode:代表文件被创建的权限,一般用八进制整数表示0777 0666 0644
read:读文件
write:写文件
close:关闭一个打开的文件
open write sync 函数
open.c 第一个参数是文件名字 第二个参数: 读|创建 返回一个文件描述符
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
struct task
{
int id;
char file[64];
int threadnum;
int filesize;
};
struct task t = {
1, "1.dat", 5, 1024 * 1024,
};
int main(void)
{
//以读写方式打开文件、如果不存在就创建
int fd = open("1.dat", O_RDWR | O_CREAT, 0777);
if(fd < 0)
{
perror("open");
return -1;
}
//把数据写入到文件中
int ret = write(fd, &t, sizeof(t));
if(ret != sizeof(t))
{
perror("write");
return -1;
}
//把文件指针移动到文件首
lseek(fd, 0, SEEK_SET);
//从文件中读取数据
struct task tmp;
ret = read(fd, &tmp, sizeof(tmp));
if(ret == sizeof(tmp))
{
printf("%d %s %d %d\n", tmp.id, tmp.file, tmp.threadnum, tmp.filesize);
}
close(fd);
return 0;
}
文件的下载:任务 文件名 线程 文件大小
把数据写入到文件中, 以二进制形式写入 write第一个参数: 文件描述符 第二个:地址
lseek:移动文件指针
SEEK_SET 文件首
SEEK_CUR 文件指针当前位置
SEEK_END 文件尾
fsync:把缓存在磁盘队列中的数据刷新到磁盘中
fdopen: 把一个文件描述符转换成FILE类型的指针 int fd-->FILE *fp
fileno: 把一个FILE *fp转换成一个文件描述符fd FILE *fp ---> int fd
write.c
Wirte 函数 不带缓冲的
printf带缓冲的文件IO
#include <stdio.h>
#include <unistd.h>
int main(void)
{
//write(1, "hello", strlen("hello")); //不带缓冲的文件IO
printf("hello");//带缓冲的文件IO
sleep(10);
//write(1, "world", strlen("world"));
printf("world");
return 0;
}
第四章:对文件的属性进行操作(大小、权限、拥有者)、目录
stat.c()
stat参数: 文件属性,地址
文件大小st.st_size
文件类型 S_ISREG(st.st_mode)
S_I 目录的大小是4k
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
int main(void)
{
struct stat st;
//获取文件属性
//stat("1.dat", &st);
//stat("/", &st);
//stat("/dev/input/mouse0", &st);
int fd = open("1.dat", O_RDONLY);
fstat(fd, &st);
printf("文件大小:%ld\n", st.st_size);
if(S_ISREG(st.st_mode))
{
printf("这是一个普通文件\n");
}
else if(S_ISDIR(st.st_mode))
{
printf("这是一个目录文件\n");
}
else if(S_ISCHR(st.st_mode))
{
printf("这是一个字符设备文件\n");
}
return 0;
}
fsata是传递文件描述符(文件已经打开)
1.stat、fstat 获取一个文件的I结点信息(属性)
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* inode number */
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 */
};
获取文件类型:
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.)
fileno.c
fileno把一个FILE类型指针转换成一个文件描述符
strlen是获取字符串的大小
sizeof是获取字符串占据空间的大小
fpopen把一个文件描述符转换成file类型指针
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int main(void)
{
FILE *fp;
fp = fopen("2.txt", "w");
if(fp == NULL)
{
perror("fopen");
return -1;
}
//把一个FILE *转换成一个文件描述符
int fd = fileno(fp);
write(fd, "hello", strlen("hello"));
//write(fd, "hello", sizeof("hello"));
struct stat st;
fstat(fd, &st);
printf("%d\n", st.st_size);
return 0;
}
演示函数fdopen.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void)
{
int fd = open("2.txt", O_RDONLY);
if(fd < 0)
{
perror("open");
return -1;
}
FILE *fp;
fp = fdopen(fd, "r"); //把一个文件描述符转换成一个FILE类型的指针
if(fp == NULL)
{
perror("fdopen");
return -1;
}
char buf[100] = {0};
fgets(buf, sizeof(buf), fp);
printf("%s\n", buf);
fclose(fp);
return 0;
}
fgets读取数据
lsata获取软链接文件
2.access判断一个文件是否存在、是否可读、是否可写、是否可执行
R_OK是否可读
W_OK是否可写
X_OK是否可执行
F_OK是否存在
access.c
access(… … ) F_OK这个文件是否存在 等于0表示存在
#include <stdio.h>
#include <unistd.h>
int main(void)
{
//判断一个文件是否存在
int ret = access("2.txt", F_OK);
if(ret == 0)
{
printf("这个文件存在\n");
}
else
{
printf("这个文件不存在\n");
}
return 0;
}
3.umask修改进程创建文件的屏蔽字
umas.c umask()函数先取反,再与操作
一,创建文件
remove()删除文件函数
二,获取文件属性 st.st_mode 获取文件低九位
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void)
{
struct stat st;
//修改文件屏蔽字
umask(033);
//如果这个文件存在,就删除
if(access("3", F_OK) == 0)
{
remove("3");
}
//创建这个文件,以0777的方式创建
int fd = open("3", O_RDONLY | O_CREAT, 0777);
if(fd < 0)
{
perror("open");
return -1;
}
//获取这个文件的属性
fstat(fd, &st);
printf("文件的权限是%o\n", st.st_mode & 0x1ff);
return 0;
}
4.修改文件长度 truncate ftruncate
truncate.c 修改文件大小
参数: 文件名字 文件大小
用于多线程下载
#include <unistd.h>
#include <sys/types.h>
int main(void)
{
//修改文件大小
truncate("1.txt", 1024 * 1024 * 400);
return 0;
}
5.unlink 删除一个文件
unlink.c access(1.dat F_OK)=0 判断文件存在
unlink()
#include <stdio.h>
#include <unistd.h>
int main(void)
{
if(access("1.dat", F_OK) == 0)
{
printf("删除前存在1.dat\n");
}
else
{
printf("删除前不存在1.dat\n");
}
//删除文件
//unlink("1.dat");
remove("1.dat");
if(access("1.dat", F_OK) == 0)
{
printf("删除后存在1.dat\n");
}
else
{
printf("删除后不存在1.dat\n");
}
return 0;
}
rmdir 删除一个空目录
remove 删除一个文件或者一个空目录
6.mkdir 创建目录
mkdir.c
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
int main(void)
{
//创建一个叫aaa的目录
mkdir("aaa", 0777);
return 0;
}
7.opendir readdir closedir rewinddir 打开目录、读取目录项、关闭目录
opendir.c man 3 dir 读取目录下的文件 目录的递归操作
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
int main(void)
{
//打开目录
DIR *dir = opendir("/");
if(dir == NULL)
{
perror("opendir");
return -1;
}
struct dirent *dent;
while(1)
{
dent = readdir(dir); //读取目录的一项数据
if(dent != NULL)
{
printf("%s\n", dent->d_name);
}
else
break;
}
closedir(dir);
return 0;
}
8.getcwd 获取当前工作路径
getcwd.c getwcd返回一个当前工作路径
#include <stdio.h>
#include <unistd.h>
int main(void)
{
char buf[100] = {0};
//printf("当前工作路径是%s\n", getcwd(NULL, 0));
printf("当前工作路径是%s\n", getcwd(buf, sizeof(buf)));
printf("当前工作路径是%s\n", buf);
//修改当前工作路径
chdir("/opt");
printf("当前工作路径是%s\n", getcwd(buf, sizeof(buf)));
printf("当前工作路径是%s\n", buf);
return 0;
}
9.chdir 修改当前工作路径
第六章:
1. 密码文件的读取 /etc/passwd 存放的是用户的信息
getpwnam 根据用户名获取用户信息
getpwuid 根据用户ID获取用户信息
getpwent 从/etc/passwd文件中读取一行数据
getpwname.c
shell 中false是完全不能使用的
getpwname()函数返回一个结构体 struct passed
#include <stdio.h>
#include <sys/types.h>
#include <pwd.h>
int main(void)
{
struct passwd *pw = NULL;
//pw = getpwnam("root");
pw = getpwuid(1);
if(pw == NULL)
{
perror("getpwnam");
}
else
{
printf("%s:%s:%d:%d:%s:%s:%s\n", pw->pw_name,
pw->pw_passwd,
pw->pw_uid,
pw->pw_gid,
pw->pw_gecos,
pw->pw_dir,
pw->pw_shell);
}
return 0;
}
2. 阴影文件的读取 /etc/shadow 存放的是用户的密码相关的信息
getspnam 根据用户名获取这个用户的密码相关的信息
getspent
getspname.c 获取用户密码
#include <shadow.h>
#include <stdio.h>
int main(void)
{
struct spwd *sp = getspnam("root");
if(sp == NULL)
{
perror("getspnam");
}
else
{
printf("%s\n", sp->sp_pwdp);
}
return 0;
}
3.时间相关的函数
time 获取一个从1970.1.1 0:0:0到当前一个秒数
gettimeofday 获取一个更精确的时间(微秒)
settimeofday 设置系统的当前时间
ctime 把一个time转换成字符串表示的年月日时分秒
asctime 把一个struct tm的结构体转换成字符串的年月日时分秒
localtime 获得一个年月日时分秒的结构体(本地时间)
gmtime 获得一个年月日时分秒的结构体(全球时间)
mktime 把一个struct tm的结构体转换成time_t的秒数
time.c
#include <stdio.h>
#include <time.h>
int main(void)
{
printf("当前时间是%d\n", time(NULL));
time_t t;
time(&t);
printf("当前时间是%d\n", (int)t);
return 0;
}
ctime.c
#include <stdio.h>
#include <time.h>
int main(void)
{
time_t t;
time(&t);
printf("当前时间是%d %s\n", (int)t, ctime(&t));
return 0;
}
localtime.c gmtime(全球时间)
settime()(设置时间) 需要加sudo
#include <stdio.h>
#include <time.h>
int main(void)
{
time_t t = time(NULL);
//struct tm *tm = localtime(&t);
struct tm *tm = gmtime(&t);
printf("%d年%d月%d日 %d:%d:%d\n", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
return 0;
}
settimeofday.c 设置时间为微秒
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
int main(void)
{
struct tm tm;
tm.tm_sec = 30;
tm.tm_min = 30;
tm.tm_hour = 8;
tm.tm_mday = 12;
tm.tm_mon = 4;
tm.tm_year = 2019 - 1900;
time_t t = mktime(&tm);
printf("%d\n", t);
//printf("%s\n", ctime(&t));
struct timeval tv;
tv.tv_sec = t;
tv.tv_usec = 0;
settimeofday(&tv, NULL);
return 0;
}
第七章:进程入门
1.进程正常结束:从main函数直接返回、执行exit结束
1.关闭所有文件、刷新所有缓冲区中的数据到文件中
2.执行进程的清理函数
进程不正常结束:不会执行以上两步。被信号杀死、_exit函数结束
2.进程的清理函数 atexit注册进程的清理函数
atexit.c
#include <stdlib.h>
#include <stdio.h>
void do_quit(void);
int main(void)
{
//注册清理函数
atexit(do_quit);
sleep(5);
printf("进程结束了\n");
return 0;
}
void do_quit(void)
{
printf("进程被清理了\n");
}
3.main的参数:main函数在被系统调用时、系统也是可以传递参数给main函数
int main(int argc, char *argv[]);
int main(int argc, char *argv[], char *env[]);
mian的参数.c
argc 指传递给main函数的命令行中字符串的个数
argv中存放的是命令行中的字符串
#include <stdio.h>
//main的参数
int main(int argc, char *argv[])
{
//argc指传递给main的命令行中字符串的个数
//argv中存放的是命令行中的字符串
// ./kkk 1 2 3 ---> argc = 4 argv[0] ="./kkk" argv[1] = "1" argv[2] = "2" ....
int i;
for(i = 0; i < argc; i++)
{
printf("argv[%d] = %s\n", i, argv[i]);
}
return 0;
}
4. 进程环境变量表:
通过main函数的第三个参数来访问
int main(int argc, char *argv[], char *env[]);
通过linux中定义的一个全局变量 environ来访问
环境变量表.c
声明全局变量 extern 指向系统的环境表
#include <stdio.h>
int main(int argc, char *argv[], char **env)
{
//声明全局变量
//extern char **environ;
int i;
//for(i = 0; environ[i] != NULL; i++)
for(i = 0; env[i] != NULL; i++)
{
//printf("%s\n", environ[i]);
printf("%s\n", env[i]);
}
return 0;
}
5.
获取进程的某一个环境变量 getenv
设置进程的环境变量 setenv
getenv.c 获取环境变量,可获得当前工作路径
getcpwd()相比较
设置进程的环境变量 setenv 修改PWD环境变量
参数
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
char *pwd = getenv("PWD");
printf("%s\n", pwd);
printf("%s\n", getcwd(NULL, 0));
chdir("/opt");
//修改PWD环境变量
setenv("PWD", getcwd(NULL, 0), 1);
pwd = getenv("PWD");
printf("%s\n", pwd);
printf("%s\n", getcwd(NULL, 0));
return 0;
}
6. getrlimit 获取系统对资源的限制值
setrlimit 修改系统对资源的限制值
第八章:
getpid 获取进程的PID
getppid 获取进程的父进程的PID
fork 创建子进程
getpid.c 函数获取当前进程pid
getppid()函数获取父进程pid
#include <stdio.h>
#include <unistd.h>
int main(void)
{
pid_t pid;
//获取当前进程ID
pid = getpid();
pid_t ppid;
//获取当前进程父进程ID
ppid = getppid();
printf("进程ID是%d, 父进程ID是%d\n", pid, ppid);
return 0;
}
fork.c 创建子进程
fork()=0,子进程创建成功
创建指针函数作为子进程,对fork进程封装
read(0,buf,sizeof(buf))读取键盘操作
bzero(buf,sizeof(buf)) buf清零
读取鼠标必须先打开鼠标 mouse = open(“/dev/inout/mice, O_RDONLY”)
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void)
{
int mouse;
mouse = open("/dev/input/mice", O_RDONLY);
if(mouse < 0)
{
perror("open");
return -1;
}
int ret;
char buf[100] = {0};
if(fork() == 0) //子进程
{
while(1)
{
ret = read(0, buf, sizeof(buf));
if(ret > 0)
{
printf("键盘说:%s\n", buf);
bzero(buf, sizeof(buf));
}
}
}
else
{
while(1)
{
ret = read(mouse, buf, sizeof(buf));
if(ret > 0)
{
printf("mouse say: %s\n", buf);
bzero(buf, sizeof(buf));
}
}
}
return 0;
}
fork2.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//创建一个子进程/这个子进程的目的就是为了执行函数fun
int newprocess( void (*fun)(void) )
{
//创建一个子进程
if(fork() == 0)
{
fun(); //在子进程中执行fun函数
exit(0); //子进程退出
}
return 0;
}
void readmouse(void);
int main(void)
{
int ret;
char buf[100] = {0};
//创建子进程用于读取鼠标
newprocess(readmouse);
while(1)
{
//读取键盘
ret = read(0, buf, sizeof(buf));
if(ret > 0)
{
printf("键盘说:%s\n", buf);
bzero(buf, sizeof(buf));
}
}
}
void readmouse(void)
{
int ret;
int mouse;
char buf[100] = {0};
mouse = open("/dev/input/mice", O_RDONLY);
if(mouse < 0)
{
perror("open");
return -1;
}
while(1)
{
ret = read(mouse, buf, sizeof(buf));
if(ret > 0)
{
printf("mouse say:%s\n", buf);
bzero(buf, sizeof(buf));
}
}
}
第十章:信号
1.linux内核提供的一种机制、用于通知系统中的应用程序发生了某种事件
2.kill -l这个命令来列出linux支持的信号
SIGINT 中断信号 ctrl+c
SIGQUIT 退出信号 ctrl+\
SIGFPE 浮点异常 除0
SIGSEGV 段错误 当系统中检测到段错误发生时
signal.c
#include <stdio.h>
#include <signal.h>
int main(void)
{
char *ptr = NULL;
*ptr = 10; //段错语
int a = 1/0; //浮点异常
printf("%d\n", a);
printf("lksjdflksjdflksdjflskdjf\n");
return 0;
}
3.进程收到信号后会做出三种反映:
1. 按照默认的方式处理这个信号(每个信号都有一个默认动作)
2. 忽略这个信号(signal(信号,SIG_IGN));
3. 执行一个信号处理函数(signal(信号,函数));
注册函数signal() 参数:SIGIGN忽略信号 第二个参数可以是个函数
signal2.c
#include <stdio.h>
#include <signal.h>
void sig_int(int sig);
int main(void)
{
//忽略SIGINT信号
// signal(SIGINT, SIG_IGN);
//注册SIGINT的信号处理函数
signal(SIGINT, sig_int);
while(1)
{
sleep(1);
printf("i am running\n");
}
return 0;
}
void sig_int(int sig)
{
printf("sigint产生了\n");
}
4. kill 发出信号
raise 产生一个信号(发送一个信号给自己)
alarm 设置一个定时器,时间到来后会自动发出SIGALRM信号
pause 让进程暂停下来、可以被任意信号唤醒
abort 产生一个SIGABRT的信号 (用于终止进程、不正常终止)
kill.c 发送一个信号
#include <sys/types.h>
#include <signal.h>
//kill 发送一个信号
int main(void)
{
//创建子进程
if(fork() == 0)
{
int count = 5;
while(count > 0)
{
count--;
printf("距离下班还有%d秒\n", count);
sleep(1);
}
printf("下班了\n");
kill(getppid(), SIGINT); //发送一个SIGINT信号给父进程
exit(0);
}
else
{
while(1)
{
printf("我是父进程,,,我还有上班\n");
sleep(1);
}
}
return 0;
}
定时器信号. alarm()函数, 产生信号 进程默认退出
pause()函数 产生暂停信号,直到另一个信号产生.
alarm.c
#include <stdio.h>
int main(void)
{
printf("程序将在5秒后结束\n");
//5秒之后自动发出SIGALRM信号
alarm(5);
pause(); //程序暂停了
#if 0
while(1)
{
;;;;;
}
#endif
return 0;
}
sleep.c signal()防止进程被信号杀死
#include <stdio.h>
#include <signal.h>
void sig_proc(int sig);
int main(void)
{
//signal(SIGALRM, SIG_IGN);
signal(SIGALRM, sig_proc);
alarm(5);
printf("程序开始睡30秒\n");
sleep(30);
printf("程序退出\n");
return 0;
}
void sig_proc(int sig)
{
printf("sig\n");
}
第十一章:线程:
pthread_create:创建一个线程
pthread_self:获取线程的ID
pthread_join:等待其它线程结束(回收线程的资源)
pthread_cancel 通知一个线程结束
pthread_cleanup_push 为线程注册清理函数
pthread_cleanup_pop
pthread_create.c 线程的封装
编译的时候要链接线程库 -lpthread
设置信号处理函数signal()
通知一个线程结束 pthread_cancel 得先拿到线程的id号
然后等待线程结束 pthread_join
为线程注册清理函数 pthread_cleanup_push() 参数 :执行一个清理函数 arg
必须调用pthread_cleanup_pop(1)函数
#include <pthread.h>
#include <stdio.h>
#include <signal.h>
int quit = 0;
void sig_quit(int sig);
void thread_quit(void *arg);
void *do_playmusic(void *arg);
int main(void)
{
pthread_t tid[3];
signal(SIGINT, sig_quit);
//创建一个线程
pthread_create(&tid[0], NULL, do_playmusic, (void *)"李香兰");
pthread_create(&tid[1], NULL, do_playmusic, (void *)"霸王别姬");
pthread_create(&tid[2], NULL, do_playmusic, (void *)"北京北京");
while(quit == 0)
{
printf("打印----\n");
sleep(1);
}
int i;
for(i = 0; i < 3; i++)
{
//通知这个线程退出
pthread_cancel(tid[i]);
//等待这个线程退出
pthread_join(tid[i], NULL);
}
return 0;
}
void sig_quit(int sig)
{
quit = 1;
}
void *do_playmusic(void *arg)
{
char *music = arg;
//为线程注册清理函数
pthread_cleanup_push(thread_quit, arg);
while(1)
{
printf("播放音乐---%s\n", music);
sleep(1);
}
pthread_cleanup_pop(1);
}
void thread_quit(void *arg)
{
printf("%s被清理了\n", (char *)arg);
}
==2017年3月2日11:27:20==
同步与互斥:
互斥锁:用于实现多个线程对同一个资源的访问是原子性
pthread_mutex_init
pthread_mutex_destroy
pthread_mutex_lock
pthread_mutex_unlock
int pthread_setcancelstate(int state, int *oldstate);
int pthread_setcanceltype(int type, int *oldtype);
创建互斥锁pthread_mutex.c
先进行初始化
用完之后进行解散
最后要进行销毁
缺点:效率比不佳锁低
可能多个线程一起使用一个变量
nanaosleep() 睡眠纳米级
有可能线程在锁的时候被通知取消。因此要取消线程的状态
如果一个锁加两次,就会出现死锁现象
#include <stdio.h>
#include <signal.h>
#include <pthread.h>
#include <time.h>
int quit = 0;
int tick = 0;
pthread_mutex_t mutex;
void sig_quit(int sig);
void *thread(void *arg);
int main(void)
{
int i;
pthread_t tids[100];
signal(SIGINT, sig_quit);
pthread_mutex_init(&mutex, NULL);
for(i = 0; i < 100; i++)
{
pthread_create(&tids[i], NULL, thread, NULL);
}
while(quit == 0)
{
pause();
}
for(i = 0; i < 100; i++)
{
pthread_cancel(tids[i]);
pthread_join(tids[i], NULL);
}
pthread_mutex_destroy(&mutex);
return 0;
}
void *thread(void *arg)
{
struct timespec t;
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
while(1)
{
//加两次锁会导致死锁
pthread_mutex_lock(&mutex); //加锁
//pthread_mutex_lock(&mutex); //加锁
tick++;
t.tv_sec = 0;
t.tv_nsec = 10;
nanosleep(&t, NULL);
printf("tick = %d\n", tick);
//pthread_mutex_unlock(&mutex); //解锁
pthread_mutex_unlock(&mutex); //解锁
}
}
void sig_quit(int sig)
{
quit = 1;
}
如何防止死锁:1. 所有的锁只能加一次
2. 加锁顺序要一致
读写锁:
读锁与读锁之间是不互斥的
读锁与写锁是互斥的
写锁与写锁是互斥的
pthread_rwlock_init
pthread_rwlock_destroy
pthread_rwlock_rdlock
pthread_rwlock_wrlock
pthread_rwlock_unlock
条件变量:解决生产者与消费者之间同步的问题
pthread_cond_init
pthread_cond_destroy
pthread_cond_wait 让线程等待条件满足
pthread_cond_signal 通知等待的线程继续运行
pthread_cond_broadcast 通知所有等待的线程继续运行
第十四章:高级IO
1. 非阻塞型IO
对于一些低速设备,linux对它们的读写操作使用的是阻塞型机制(由这些设备的驱动来决定的)。
block.c 阻塞型设备:如鼠标键盘,网络
fcntl() 以键盘为例把键盘设置为非阻塞
例:把键盘输入与 socket消息
#include <stdio.h>
#include <unistd.h>
#include <unistd.h>
#include <fcntl.h>
int main(void)
{
int ret;
char buf[100];
//把键盘设置为非阻塞
fcntl(0, F_SETFL, O_NONBLOCK);
while(1)
{
//阻塞型:如果没有数据可读,会阻塞,直到有数据可读为址
ret = read(0, buf, sizeof(buf));
if(ret > 0)
{
printf("%s\n", buf);
}
else
{
printf("没有数据\n");
sleep(1);
}
}
return 0;
}
2. 把一个阻塞型的设备以非阻塞的方式进行读写操作
以键盘为例:
int fcntl(int fd, int cmd, ... /* arg */ );
fcntl(0, F_SETFL, O_NONBLOCK);
3. 一个程序中同时要读写两个以上的阻塞型设备、此时可以采用IO多路转换的方式
poll
select
4. 异步IO机制
1. 把设备设置为异步方式
fcntl(fd, F_SETFL, O_ASYNC);
2. 把进程的PID告诉给设备驱动
fcntl(fd, F_SETOWN, getpid());
3. 设置进程对SIGIO信号的处理函数
signal(SIGIO, procdat); //procdat是一个函数,用于读取设备数据
第十五章:进程间通信
1.无名管道:只能用于有亲缘关系的进程之间的通信
pipe
它是一种单双工的通信
管道pipe.c
无名pipe() 创建一个管道int fd=1,单双通,亲缘之间的通讯
#include <stdio.h>
int main(void)
{
int fd[2];
char buf[100] = {0};
//创建一个无名管道
pipe(fd);
if(fork() == 0) //创建一个子进程
{
//子进程把数据写入到管道的写端
write(fd[1], "hello", strlen("hello"));
}
else
{
//父进程
read(fd[0], buf, sizeof(buf));
printf("%s\n", buf);
}
}
2.命名管道:能用于任意的两个进程之间的通信
mkfifo
它是一种单双工的通信
mkdir fifo
#### fifo1.c fifo2.c
mkfifo()函数创建管道文件
协同作用,你一句我一句(读取管道和键盘阻塞)
打开读管道open(RFIFO, RDONLY)
### poll.c监听机制 poll() 参数 监听个数 -1 函数中有结构体{ }
selet机制
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>
#define RFIFO "rfifo"
#define WFIFO "wfifo"
int main(void)
{
int ret;
char buf[100];
//创建管道
mkfifo(RFIFO, 0777);
mkfifo(WFIFO, 0777);
int rfd, wfd;
//打开RFIFO管道的读端
rfd = open(RFIFO, O_RDONLY);
if(rfd < 0)
{
perror("open");
return -1;
}
//打开WFIFO管道的写端
wfd = open(WFIFO, O_WRONLY);
if(wfd < 0)
{
perror("open");
return -1;
}
// fcntl(0, F_SETFL, O_NONBLOCK);
// fcntl(rfd, F_SETFL, O_NONBLOCK);
struct pollfd pfd[2];
pfd[0].fd = 0;
pfd[1].fd = rfd;
pfd[0].events = POLLIN;
pfd[1].events = POLLIN;
while(1)
{
//监听0与rfd是否可读
if(poll(pfd, 2, -1) > 0)
{
if(pfd[0].revents == POLLIN)
{
//读键盘
bzero(buf, sizeof(buf));
ret = read(0, buf, sizeof(buf)); //阻塞
if(ret > 0)
{
//把数据写入到WFIFO的写端
write(wfd, buf, ret);
}
}
if(pfd[1].revents == POLLIN)
{
//读管道RFIFO
bzero(buf, sizeof(buf));
ret = read(rfd, buf, sizeof(buf)); //阻塞
if(ret > 0)
{
printf("fifo2说:%s\n", buf);
}
}
}
}
return 0;
}
fifo2.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>
#define RFIFO "rfifo"
#define WFIFO "wfifo"
int main(void)
{
int ret;
char buf[100];
//创建管道
mkfifo(RFIFO, 0777);
mkfifo(WFIFO, 0777);
int rfd, wfd;
//打开RFIFO管道的写端
wfd = open(RFIFO, O_WRONLY);
if(wfd < 0)
{
perror("open");
return -1;
}
rfd = open(WFIFO, O_RDONLY);
if(rfd < 0)
{
perror("open");
return -1;
}
struct pollfd pfd[2];
pfd[0].fd = 0;
pfd[1].fd = rfd;
pfd[0].events = POLLIN;
pfd[1].events = POLLIN;
while(1)
{
//监听0与rfd是否可读
if(poll(pfd, 2, -1) > 0)
{
if(pfd[0].revents == POLLIN)
{
//读键盘
bzero(buf, sizeof(buf));
ret = read(0, buf, sizeof(buf)); //阻塞
if(ret > 0)
{
//把数据写入到WFIFO的写端
write(wfd, buf, ret);
}
}
if(pfd[1].revents == POLLIN)
{
//读管道RFIFO
bzero(buf, sizeof(buf));
ret = read(rfd, buf, sizeof(buf)); //阻塞
if(ret > 0)
{
printf("fifo2说:%s\n", buf);
}
}
}
}
return 0;
}
3.消息队列
ftok 通过一个文件来生成一个key
msgget 创建消息队列
msgrcv 从消息队列中取消息
msgsnd 把消息发送到消息队列中
msgctl 对消息队列进行操作(销毁、设置)
msgsnd.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <fcntl.h>
#define MSGFILE "msgfile"
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[100]; /* message data */
};
struct student
{
int age;
char name[20];
};
int main(void)
{
//创建msgfile这个文件
close(open(MSGFILE, O_RDONLY | O_CREAT, 0777));
//根据msgfile生成一个key
key_t key = ftok(MSGFILE, 1);
//创建消息队列
int msgid = msgget(key, IPC_CREAT | 0777);
if(msgid == -1)
{
perror("msgget");
return -1;
}
// int a = 30;
struct msgbuf msg;
msg.mtype = 1;
//strcpy(msg.mtext, "lsdjflksdjflkdsjflskdjf");
// memcpy(msg.mtext, &a, sizeof(a));
struct student stu = {
20, "aaa"
};
memcpy(msg.mtext, &stu, sizeof(stu));
//向消息队列中发送消息
//msgsnd(msgid, &msg, strlen(msg.mtext), 0);
//msgsnd(msgid, &msg, sizeof(a), 0);
msgsnd(msgid, &msg, sizeof(stu), 0);
return 0;
}
msgrcv.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <fcntl.h>
#define MSGFILE "msgfile"
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[100]; /* message data */
};
struct student
{
int age;
char name[20];
};
int main(void)
{
//创建msgfile这个文件
close(open(MSGFILE, O_RDONLY | O_CREAT, 0777));
//根据msgfile生成一个key
key_t key = ftok(MSGFILE, 1);
//获取到消息队列
int msgid = msgget(key, IPC_CREAT | 0777);
if(msgid == -1)
{
perror("msgget");
return -1;
}
struct msgbuf msg;
struct student stu;
//从消息队列中取消息
if(msgrcv(msgid, &msg, sizeof(msg.mtext), 1, 0) > 0)
{
//printf("%s\n", msg.mtext);
//printf("%d\n", *((int *)msg.mtext));
memcpy(&stu, msg.mtext, sizeof(stu));
printf("%d %s\n", stu.age, stu.name);
}
return 0;
}
```
### 4.共享存储
```
ftok
shmget 创建共享内存
shmat 获取到共享内存的地址
shmdt 释放共享内存的地址
shmctl 销毁共享内存
```
### 5.信号量
网友评论