本周对Linux系统编程进行了系统的学习。罗列了各类函数,对其功能、传参、返回值进行了总结整理。open、write、fork、wait、pthread等。
1)open
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int open(const char *pathname, int flags, mode_t mode);
1.1)功能描述:
打开和创建文件(建立一个文件描述符,其他的函数可以通过文件描述符对指定文件进行读取与写入的操作。)
1.2)参数说明:
pathname: 打开文件的路径;
flags : 打开标志;
mode : 只要使用 O_CREAT标志,就要使用这个参数,设置权限;
1.3)返回值:
成功返回文件描述符;
失败返回-1;
1.4)例码:
fd = open("./data.txt", O_CREAT|O_WRONLY|O_TRUNC, 0777);
2)close
#include<unistd.h>
int close(int fd)
2.1)功能描述
关闭一个已经打开的文件;
2.2)参数说明
fd:需要关闭的文件描述符;
2.3)返回值
成功:返回0;
失败:返回-1, 并设置errno
2.4)例码
close(fd);
3)write
#include<unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
3.1)功能描述
把参数buf所指的内存,写入count个字节,到参数fd所指的文件内。
3.2)参数说明
fd : 文件描述符;
buf: 写数据的起始地址;
count : 数据的大小;
3.3)返回值
成功:返回实际写进去的字节数;
失败: -1, 并设置errno;
(ssize_t类型)
3.4)例码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int fd;
int ret;
fd = open("./data.txt", O_CREAT|O_WRONLY|O_TRUNC, 0777);
if(fd < 0)
{
perror("open");
return -1;
}
int a[5] = {1,2,3,4,5};
ret = write(fd, a, sizeof(a)); //从a开始写入4个字节到fd文件内;
if(ret < 0)
{
perror("write");
close(fd);
return -1;
}
printf("写入成功!\n");
close(fd);
return 0;
}
4)read
#include<unistd.h>
ssize_t read(int fd, void *buf, size_t count);
4.1)功能描述
把参数fd所指的文件,传送count 个字节,到buf 指针所指的内存中。
4.2)参数说明
d :文件描述符;
buf: 存放数据的起始地址;
count: 读数据的大小;
4.3)返回值
>0 : 实际读到的字节数;
=0 : 到达文件结尾;
-1 : 出错;
4.4)例码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int fd;
int ret;
fd = open("./data.txt", O_RDONLY);
if(fd < 0)
{
perror("open");
return -1;
}
int a[10];
int i = 0;
while(1)
{
ret = read(fd, &a[i], sizeof(int));
//把fd指向的文件,传送4个字节,到a[i]指向的地址中;
if(0 == ret) //到达文件结尾
{
break;
}
i ++;
}
close(fd);
for(int j=0; j<i; j++)
{
printf("%d ", a[j]);
}
printf("\n");
return 0;
}
5)lseek
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
5.1)功能描述
write和read函数本身自带移动文件指针的功能,所以打开一个有n字节的文件,read/write会自动从n字节后读写。
如果要人为改变这个文件指针就用lssek函数。
5.2)参数说明
fd:文件描述符
offset:文件偏移量
whence:文件偏移相对位置
参数 offset为负数时向文件开头偏移,正数向文件末尾偏移,0则为不偏移
参数 offset 的含义取决于参数 whence:
如果 whence 为SEEK_SET,offset相对于文件开头进行偏移
如果 whence 为 SEEK_END,offset相对于文件末尾进行偏移
如果 whence 为 SEEK_CUR,offset相对文件当前位置进行偏移
例如:
lseek(fd, 0, SEEK_SET)为光标开头偏移0,即光标置于最开头。
lseek(fd, 0, SEEK_END)为光标末端偏移0,即光标置于最末尾。
5.3)返回值
若成功,返回目前的读写位置,也就是距离文件开头多少个字节;
若失败,返回-1, 并设置errno;
5.4)例码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int fd;
int ret;
fd = open("./data.txt", O_RDONLY);
if(fd < 0)
{
perror("open");
return -1;
}
ret = lseek(fd, 5, SEEK_SET);
printf("ret = %d\n", ret);
char buff[1024];
ret = read(fd, buff, 1024);
buff[ret] = '\0';
printf("buff: %s\n", buff);
return 0;
}
6)access
#include <unistd.h>
int access(const char *pathname, int mode);
6.1)功能描述
判断用户是否具有访问某个文件的权限(或判断某个文件是否存在)
6.2)参数说明
pathname:表示要测试的文件的路径;
mode:表示测试的模式可能的值有:
R_OK:是否具有读权限;
W_OK:是否具有可写权限;
X_OK:是否具有可执行权限;
F_OK:文件是否存在;
6.3)返回值
如果指定的方式有效,则此函数返回0,否则返回-1;
6.4)例码
#include <stdio.h>
#include <unistd.h>
#define FILENAME "./data.txt"
int main(int argc, char*argv[])
{
if(argc < 2)
{
printf("参数错误!\n");
return -1;
}
int ret ;
ret = access(argv[1], F_OK);
if(ret == 0)
{
printf("%s 存在!\n", argv[1]);
}
else
{
printf("%s不存在!\n", argv[1]);
return -1;
}
ret = access(argv[1], R_OK);
if(ret == 0)
{
printf("%s有读权限\n", argv[1]);
}
else
{
printf("%s没有读权限!\n", argv[1]);
}
return 0;
}
7)fork
7.1)功能描述
分裂一个子进程出来,一旦调用fork函数,创建一个子进程,会将父进程的所有资源,包括:代码段、数据段、堆、栈、缓冲区以及cpu的状态都拷贝一份给子进程,所以子进程是从fork的下一行代码开始执行的。
7.2)返回值
父进程:返回子进程的 进程号;
子进程:返回0;
失败:返回-1;
8)exit
8.1)功能描述
无论在程序哪里调用,都表示进程结束;
注意事项:
1、在main函数中 exit 和 return的效果是一样的;
2、在其他子函数中,return表示本函数结束, exit仍然表示进程结束;
9)getpid、getppid
9.1)功能描述
getpid返回当前进程标识,
getppid返回父进程标识。
10)wait
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
10.1)功能描述
进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。
注意事项:
1、如果没有子进程,wait是一个非阻塞函数;
2、如果有子进程,wait 是一个阻塞函数,
如果有子进程退出,wait 立马解除阻塞,回收子进程的资源;
3、返回值的范围:0-255;
10.2)参数说明
1、若仅消灭僵尸进程,可以将参数设置为NULL; pid = wait(NULL);
2、参数status用来保存被收集进程退出时的一些状态,是一个指向int类型的指针。
以下是一些系统宏定义:
WIFEXITED(wstatus): 如果子进程是调用exit 正常退出,该宏为真;
WEXITSTATUS(wstatus): 如果上面的宏为真,调用该宏过滤退出返回值;
WIFSIGNALED(wstatus):如果子进程是由于信号退出的,该宏为真;
WTERMSIG(wstatus):如果上面的宏为真,可以调用该宏过滤信号值;
10.3)返回值
如果成功,wait会返回被收集的子进程的进程ID,如果调用进程没有子进程,调用就会失败,此时wait返回-1,同时errno被置为ECHILD。
10.4)例码
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
printf("main...\n");
pid_t ret;
ret = fork();
if(ret > 0)//父进程
{
int status;
printf("wait..\n");
wait(&status);
printf("wait over..\n");
if(WIFEXITED(status)) //如果为真,说明回收的子进程调用exit 或者 _exit 正常退出
{
printf("return value : %d\n", WEXITSTATUS(status));
}
while(1);
}
else if(ret == 0) //子进程
{
for(int i=1; i<=5; i++)
{
sleep(1);
printf("child process, pid=%d, ppid= %d\n", getpid(), getppid());
}
}
return -1;
}
11)system
#include <stdlib.h>
int system(const char *command);
11.1)功能描述
system()会调用fork() 产生子进程,由子进程来调用command字符串所代表的命令。
此命令执行完后随即返回原调用的进程。
在调用system()期间SIGCHLD 信号会被暂时搁置,SIGINT和SIGQUIT 信号则会被忽略。
SIGCHLD:子进程结束运行,其父进程会收到SIGCHLD信号。该信号的默认处理动作是忽略。可以捕捉该信号,在捕捉函数中完成子进程状态的回收。
11.2)参数说明
command:外部可执行程序名字
11.3)返回值
system()函数执行了三步操作:
1.fork一个子进程;
2.在子进程中调用exec函数去执行command;
3.在父进程中调用wait去等待子进程结束。
1、对于fork失败,system()函数返回-1;
2、如果exec执行成功,也即command顺利执行完毕,则返回command通过exit或return返回的值;
(注意,command顺利执行不代表执行成功,比如command:“rm debuglog.txt”,不管文件存不存在该command都顺利执行了);
3、如果exec执行失败,也即command没有顺利执行,比如被信号中断,或者command命令根本不存在,system()函数返回127;
4、如果command为NULL,则system()函数返回1;
11.4)例码
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
printf("main...\n");
pid_t ret;
ret = fork();
if(ret > 0)//父进程
{
int status;
printf("wait..\n");
wait(&status);
printf("wait over..\n");
if(WIFEXITED(status)) //如果为真,说明回收的子进程调用exit 或者 _exit 正常退出
{
printf("return value : %d\n", WEXITSTATUS(status));
}
while(1);
}
else if(ret == 0) //子进程
{
for(int i=1; i<=5; i++)
{
sleep(1);
printf("child process, pid=%d, ppid= %d\n", getpid(), getppid());
}
}
return -1;
}
12)exec
12.1)功能描述
根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段,在执行完之后,原调用进程的内容除了进程号外,其他全部被新程序的内容替换了。另外,这里的可执行文件既可以是二进制文件,也可以是Linux下任何可执行脚本文件。
六种调用形式:
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg , ..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *filename, char *const argv [], char *const envp[]);——系统调用
exec()的六个函数实现的功能都是一样的,只是在传递参数和设置环境方面提供了不同的方式。
12.2)参数说明
带有字母“l”的函数,表明后面的参数列表是要传递给程序的参数列表,参数列表的第一个参数必须是要执行程序,最后一个参数必须是NULL。与v互斥;
带有字母“p”的函数,第一个参数可以是相对路径或程序名,如果无法立即找到要执行的程序,那么就在环境变量PATH指定的路径中搜索。其他函数的第一个参数则必须是绝对路径名;
带有字母“v”的函数,表明程序的参数列表通过一个字符串数组来传递。这个数组和最后传递给程序的main函数的字符串数组argv完全一样。第一个参数必须是程序名,最后一个参数也必须是NULL;
带有字母“e”的函数,用户可以自己设置程序接收一个设置环境变量的数组;
12.3)返回值
成功不返回;
失败返回-1;
12.4)例码
#include <stdio.h>
#include <unistd.h>
int main01()
{
printf("main...\n");
pid_t pid;
pid = fork();
if(pid == 0)
{
execl("/bin/ls", "ls", "-l", NULL);
}
printf("main over...\n");
return 0;
}
int main()
{
//execlp("ls", "ls", "-l", NULL) ;
char *argv[] = {"ls", "-l", NULL};
execv("/bin/ls", argv) ;
return 0;
}
*********分割线*********
以上,为进程相关函数;
以下,为线程相关函数;
13)pthread_create
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
13.1)功能描述
创建线程
PS: 编译时要手动连接到线程库(gcc XX.c -o xx -lpthread)
13.2)参数说明
pthread : 传出新建线程的标识符;
attr: 设置线程属性(一般为NULL);
start_routine: 新线程的入口函数;
arg: 给新建线程传递参数;
13.3)返回值
成功返回0, 失败返回错误编号;
13.4)例码
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
struct student
{
int num;
char name[32];
};
void* pthread_task(void* argv)
{
struct student temp = *((struct student*)argv);
printf("temp = %d %s\n", temp.num, temp.name);
while(1)
{
sleep(1);
printf("new pthread....\n");
}
return NULL;
}
int main()
{
printf("main....\n");
pthread_t pth;
//pthread_task(NULL); //若把该函数当作函数使用,与本来的用途有本质的区别,即进程、线程仍只有一个;
// int a = 10;
struct student s = {1001, "xiaoqi"};
if(0 != pthread_create(&pth, NULL, pthread_task, &s))
{
perror("pthread_create");
return -1;
}
while(1)
{
sleep(1);
printf("main pthread...\n");
}
return 0;
}
14)pthread_self
#include <pthread.h>
pthread_t pthread_self(void);
//Compile and link with -pthread.
14.1)功能描述
获取本进程自身的 ID。
14.2)参数说明
void
14.3)返回值
进程 ID 类型是 pthread_t ,这个类型一般为long long 型,8个字节。
15)pthread_exit
#include <pthread.h>
void pthread_exit(void *retval);
//Compile and link with -pthread.
15.1)功能描述
将单个线程退出
ps: 在线程入口函数中, return 和 pthread_exit 的意义是一样的;
在子函数中, return表示本函数结束, pthread_exit 仍然表示当前线程结束!!!
在线程中谨慎使用 exit !
15.2)参数说明
retval表示线程退出状态,通常传NULL;
15.3)返回值
可以指定返回值,以便其他线程通过pthread_join()函数获取该线程的返回值;
15.4)例码
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
int * function()
{
printf("function...\n");
static int a = 10;
// return &a;
pthread_exit(NULL);
//exit(0);
}
void* pthread_task(void* argv)
{
function();
for(int i=1; i<=5; i++)
{
sleep(1);
printf("new pthread....\n");
}
pthread_exit(NULL);
//return NULL;
}
int main()
{
printf("main....\n");
pthread_t pth;
if(0 != pthread_create(&pth, NULL, pthread_task, NULL))
{
perror("pthread_create");
return -1;
}
while(1)
{
sleep(1);
printf("main pthread...\n");
}
return 0;
}
16)pthread_join
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
//Compile and link with -pthread.
16.1)功能描述
子线程合入主线程,主线程阻塞等待子线程结束,然后回收子线程资源。
16.2)参数说明
thread: 线程标识符,即线程ID,标识唯一线程。
retval: 用户定义的指针,用来存储被等待线程的返回值。
16.3)返回值
0代表成功。 失败,返回的则是错误号。
16.4)例码
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
struct student
{
int num;
char name[32];
};
void* pthread_task(void* argv)
{
//static int a = 10;
for(int i=1; i<=5; i++)
{
sleep(1);
printf("new pthread....\n");
}
//pthread_exit((void*)123);
struct student *p = NULL;
p = (struct student*)malloc(sizeof(struct student));
assert(p!=NULL);
p->num = 1001;
strcpy(p->name, "xiaoqi");
pthread_exit(p);
}
int main()
{
printf("main....\n");
pthread_t pth;
if(0 != pthread_create(&pth, NULL, pthread_task, NULL))
{
perror("pthread_create");
return -1;
}
struct student *p = NULL;
printf("join...\n");
pthread_join(pth, (void**)&p);
printf("join over...p = %p\n", p);
printf(" %d %s\n", p->num, p->name);
free(p);
return 0;
}
17)pthread_detach
int pthread_detach(pthread_t thread);
17.1)功能描述
主线程与子线程分离,子线程结束后,资源自动回收。
线程分离状态:指定该状态,线程主动与主控线程断开关系。线程结束后,其退出状态不由其他线程获取,而直接自己自动释放。网络、多线程服务器常用。
17.2)参数说明
pthread_t thread: 需要分离线程的线程号;
17.3)返回值
线程分离的状态,0是成功,非0是失败;
17.4)例码
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void* pthread_task(void* argv)
{
pthread_detach(pthread_self());
for(int i=1; i<=5; i++)
{
sleep(1);
printf("new pthread....\n");
}
return NULL;
}
int main()
{
printf("main....\n");
pthread_t pth;
if(0 != pthread_create(&pth, NULL, pthread_task, NULL))
{
perror("pthread_create");
return -1;
}
//pthread_detach(pth); //设置线程的分离属性
while(1)
{
sleep(1);
printf("main pthread...\n");
}
return 0;
}
*********分割线*********
初始化线程属性:
int pthread_attr_init(pthread_attr_t *attr);
销毁线程属性所占用的资源:
int pthread_attr_destroy(pthread_attr_t *attr); 成功:0;失败:错误号;
线程分离状态的函数:
设置线程属性,分离or非分离
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
获取程属性,分离or非分离
int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate);
参数:attr:已初始化的线程属性
detachstate: PTHREAD_CREATE_DETACHED(分离线程)
PTHREAD _CREATE_JOINABLE(非分离线程)
18)线程同步_互斥锁
1、定义互斥锁: pthread_mutex_t mutex;
2、初始化互斥锁:
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //宏定义,跟上两行有同样的效果
ps : 第1 、2步(定义和初始化)在创建线程之前完成!
3、加锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
4、解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
5、销毁互斥锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
19)条件变量
1、定义条件变量 和互斥锁,并初始化 (在创建线程之前完成);
2、生产者
(1)加锁
(2)操作公共资源
(3) 解锁
(4) 发送信号通知所有阻塞在条件变量上的消费者线程
3、 消费者
(1)加锁
(2) while(没有资源)
调用wait 函数,阻塞在条件变量上,等待被唤醒。若有资源,直接消费;
(3) 解锁
条件变量相关函数:
(0)条件变量初始化
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
(1)发送信号函数
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
(2)阻塞函数
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
ps:没有资源,会解锁,然后阻塞等待;
如果被唤醒, 会申请加锁;