In Linux systems, processes can receive a variety of signals, such as SIGINT or SIGKILL. Each signal is sent in different situations and each has different behavior.
+
In this article, we’ll talk about SIGINT, SIGTERM, SIGQUIT, and SIGKILL. We’ll also see the difference between them.
+
Introduction to Signals
The signals are a method of communication between processes. When a process receives a signal, the process interrupts its execution and a signal handler is executed.
+
How the program behaves usually depends on the type of signal received. After handling the signal, the process may or may not continue its normal execution.
+
The Linux kernel can send signals, for instance, when a process attempts to divide by zero it receives the SIGFPE signal.
+
We can also send signals using the kill program. Let’s run a simple script in the background and stop it:
Alternatively, we can send signals in a terminal using key combinations. For instance, Ctrl+C sends SIGINT, Ctrl+S sends SIGSTOP, and Ctrl+Q sends SIGCONT.
+
Each signal has a default action, but a process can override the default action and handle it differently, or ignore it. However, some signals can’t be ignored nor handled differently and the default action is always executed.
+
We can handle signals in bash using the trap command. For instance, we can add trap date SIGINT in a script and it will print the date when SIGINT is received.
+
SIGINT
SIGINT is the signal sent when we press Ctrl+C. The default action is to terminate the process. However, some programs override this action and handle it differently.
+
One common example is the bash interpreter. When we press Ctrl+C it doesn’t quit, instead, it prints a new and empty prompt line. Another example is when we use gdb to debug a program. We can send SIGINT with Ctrl+C to stop the execution and return it to the gdb’s interpreter.
+
We can think of SIGINT as an interruption request sent by the user. How it is handled usually depends on the process and the situation.
+
Let’s write handle_sigint.sh using the trap command to handle SIGINT and print the current date:
+
1 2 3 4 5 6 7
#!/bin/bash
trapdate SIGINT
read input echo User input: $input echo Exiting now
+
+
We use read input to wait for the user interaction. Now, let’s run our script and let’s press Ctrl+C:
We can see the script didn’t exit. We can now terminate the script by writing some input:
+
1 2 3 4 5
$ ./handle_sigint.sh ^CSat Apr 10 15:32:07 -03 2021 live long and prosper User input: live long and prosper Exiting now
+
+
If we want to use a signal to terminate it, we can’t use SIGINT with this script. We should use SIGTERM, SIGQUIT, or SIGKILL instead.
+
SIGTERM and SIGQUIT
The SIGTERM and SIGQUIT signals are meant to terminate the process. In this case, we are specifically requesting to finish it. SIGTERM is the default signal when we use the kill command.
+
The default action of both signals is to terminate the process. However, SIGQUIT also generates a core dump before exiting.
+
When we send SIGTERM, the process sometimes executes a clean-up routine before exiting.
+
We can also handle SIGTERM to ask for confirmation before exiting. Let’s write a script called handle_sigterm.sh to terminate only If the user sends the signal twice:
SIGTERM_REQUESTED=0 handle_sigterm() { if [ $SIGTERM_REQUESTED -eq 0 ]; then echo"Send SIGTERM again to terminate" SIGTERM_REQUESTED=1 else echo"SIGTERM received, exiting now" exit 0 fi }
trap handle_sigterm SIGTERM
TIMEOUT=$(date +%s) TIMEOUT=$(($TIMEOUT + 60))
echo"This script will exit in 60 seconds" while [ $(date +%s) -lt $TIMEOUT ]; do sleep 1; done echo Timeout reached, exiting now
+
+
Now, let’s run it on background executing $ ./handle_sigterm.sh &. Then, we run $ kill <PID> twice:
+
1 2 3 4 5 6 7
$ ./handle_sigterm.sh & [1] 6092 $ kill 6092 Send SIGTERM again to terminate $ kill 6092 SIGTERM received, exiting now [1]+ Done ./handle_sigterm.sh
+
+
As we can see, the script exited after it received the second SIGTERM.
+
SIGKILL
When a process receives SIGKILL it is terminated. This is a special signal as it can’t be ignored and we can’t change its behavior.
+
We use this signal to forcefully terminate the process. We should be careful as the process won’t be able to execute any clean-up routine.
+
One common way of using SIGKILL is to first send SIGTERM. We give the process some time to terminate, we may also send SIGTERM a couple of times. If the process doesn’t finish on its own, then we send SIGKILL to terminate it.
+
Let’s rewrite the previous example to try to handle SIGKILL and ask for confirmation:
+
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#!/bin/bash
SIGKILL_REQUESTED=0 handle_sigkill() { if [ $SIGKILL_REQUESTED -eq 0 ]; then echo"Send SIGKILL again to terminate" SIGKILL_REQUESTED=1 else echo"Exiting now" exit 0 fi }
trap handle_sigkill SIGKILL
read input echo User input: $input
+
+
Now, let’s run it on a terminal, and let’s send SIGKILL only once with $ kill -SIGKILL <pid>:
+
1 2 3
$ ./handle_sigkill.sh Killed $
+
+
We can see it terminate right away without asking to re-send the signal.
+
How SIGINT Relates to SIGTERM, SIGQUIT and SIGKILL
Now that we understand more about signals, we can see how they relate to each other.
+
The default action for SIGINT, SIGTERM, SIGQUIT, and SIGKILL is to terminate the process. However, SIGTERM, SIGQUIT, and SIGKILL are defined as signals to terminate the process, but SIGINT is defined as an interruption requested by the user.
+
In particular, if we send SIGINT (or press Ctrl+C) depending on the process and the situation it can behave differently. So, we shouldn’t depend solely on SIGINT to finish a process.
+
As SIGINT is intended as a signal sent by the user, usually the processes communicate with each other using other signals. For instance, a parent process usually sends SIGTERM to its children to terminate them, even if SIGINT has the same effect.
+
In the case of SIGQUIT, it generates a core dump which is useful for debugging.
+
Now that we have this in mind, we can see we should choose SIGTERM on top of SIGKILL to terminate a process. SIGTERM is the preferred way as the process has the chance to terminate gracefully.
+
As a process can override the default action for SIGINT, SIGTERM, and SIGQUIT, it can be the case that neither of them finishes the process. Also, if the process is hung it may not respond to any of those signals. In that case, we have SIGKILL as the last resort to terminate the process.
+
Conclusion
In this article, we learned about signals and the difference between SIGINT, SIGTERM, SIGQUIT, and SIGKILL. Also, we briefly learned how to handle signals in bash.
+
We saw how SIGINT sometimes doesn’t kill the process as it may a different meaning. On the other hand, the SIGKILL signal will always terminate the process.
+
We also learned that SIGQUIT generates a core dump by default and that SIGTERM is the preferred way to kill a process.
Luckily, Ranger ships with rifle, a file launcher that is good at automatically finding out which program to use for what file type. —— knowing from Ranger’s doc
package and import、static and final、enums、unit test、inheritance and polymorphism、
+
higher order functions、big decimal、interface、concurrency and multithreading
+
Reasons to learn Java
Java is general-purpose, which means that Java powers a wide range of applications. As a Java developer,
+
+
you can build web applications using Spring boot,
+
you can build applications on Android,
+
you can automate tasks using Selenium,
+
you can develop cloud native applications,
+
you can integrate microservices,
+
you can …
+
+
Java can run on any machine, it’s well known for its “write once, run anywhere”
+
+
This is because the Java Virtual Machine, the JVM, which is responsible for executing compiled Java code, can be installed on any platform
+
+
Java is the No.1 language for developing enterprise applications
+
To run Java code, you need
+
A Java Compiler - to compile your code.
+
+
A Java Runtime - to run the compiled code.
+
So, this is why JDK provides a Compiler、a Runtime、and a lot of other things.
+
+
+
+
Roadmap
+
Install a JDK(Java Development Kit) on your machine => from Amazon Corretto is recomended.
+
Amazon Corretto is just a distribution which once installed, does the work of setting up a JDK on your machine. Basically all you need to do is install it and it’s going to do the heavy lifting.
Finished ! you’ve just installed a JDK, now your computer can compile and run Java code.
+
+
Download a text editor to write code
+
I use vim & vscode(the extension pack for java is must).
+
Now you have everything you need to start building Java applications!
+
+
Write & Run your first Java code
+
1 2
# Every java file needs to follow the naming convention -> CamelCase touch HelloJava.java
+
1 2 3 4 5
// In java, you must write all of your code in a class. // And the ClassName needs to be the same as your FileName. // So here we create a class. classHelloJava { }
+
All right, so what’s next is the main() method, which is the entry point of a Java application.
+
And inside main, we’re going to print the message “Hello Java”
+
1 2 3 4 5 6 7 8 9 10 11 12
classHelloJava { // not understand this line is doesn't matter now. publicstaticvoidmain(String[] args) { System.out.println("Hello Java"); } }
/* Notice: the semicolon is really important, which means end of statement. every statement in Java, every line of code, needs a semicolon at the end. so if you forget your semicolon, your code is not gonna run. */
+
Now we’re ready to compile and run our Java code.
+
1 2 3 4 5
# The javac command compiles your javaCode into byteCode javac <FileName>.java # The java command executes the compiled code java <FileName>
+
For example
+
+
+
+
See you in workbook 1.1
+
Variable
+
Store data inside variable.
+
+
Java is strongly typed
which means that variables can only store values of their type.
+
Java is case sensitive
eg: people is not the same as People
+
The convention to naming a variable
lowerCamelCase
+
eg: int peopleOnBus = 20;
+
Update the value inside a variable
Just set it equal to a new value, or use +=、-=、…
+
See you in workbook 2.1
+
Use variables to store numeric data
Types: int and long
We can use int and long variables to store whole numbers.
+
You should know the difference between int and long and when to use int vs long.
intpeople=3; intwallet=20; System.out.println(wallet / people); // 6, which is not the result we want.
// So we need to make sure that at least one of these values is stored as a decimal, // then Java's going to know to return a decimal result. intpeople=3; doublewallet=20; System.out.println(wallet / people); // 6.666666666666667, this is the result we want.
+
+
Golden Rule
If precision is important, use double for math calculations.
+
Type: String
We can use the type String to store text.
+
1
Stringsentence="Hello world !";
+
+
String unlike int in memory,
+
no matter what you store in the integer variable, it’s always 4 bytes,
+
but with String, empty text alone takes up 24 bytes, and the more text that you add to a string, the more memory it takes up.
+
+
You can use the + operator to join two String values.
+
1 2 3
Stringsentence="His name is: "; Stringplaceholder="Harry"; System.out.println(sentence + placeholder);
+
+
You can use the + operator to blend values into a string.
+
1 2 3
doublepoints=50; Stringannouncement=" points for Gryffindor"; System.out.println(points + announcement); // 50.0 points for Gryffindor
+
+
+
Type: char
We can use the char type to store single characters.
+
1
chargender='F';
+
+
We can join a String value with a char value using the + operator.
+
1
System.out.println("Gender: " + 'F');
+
+
+
1
Stringgender="F";
+
+
It seems that String is more flexible than char,
+
so why not always use String ?
+
The answer is【memory】and【performance】!
+
char consumes less memory, and char is faster than String !
+
Summarize
There are 6 core data types (we didn’t cover boolean yet).
+
+
+
+
Data Type
+
Value
+
Amount of Memory (Bytes)
+
Valid Range of Values
+
+
+
+
int
+
Whole numbers
+
4
+
From: -2147483648 To: 2147483647
+
+
+
long
+
Very large whole numbers
+
8
+
From: -9223372036854775808 To: 9223372036854775807
Then you might be asking that when to use if vs switch ?
+
The only thing you can really do with switch is compare one variable against a list of values.
+
if statement is more flexible so that suitable for complex conditions, such as when you need to compare multiple variables, they give you the flexibility to evaluate compound conditions using logical operators.
+
1 2 3 4 5
if (temperature >= 80 && humidity >= 60) { System.out.println("It's too hot and humid\n"); } else { System.out.println("It's comfortable\n"); }
+
+
See you in workbook 3.6 and 3.7
+
As you write more and more code inside main(), you’ll notice that it becomes increasingly cluttered and messy.
+
And the more code you write, the more unreadable that it becomes.
+
Functions
A function is a grouping of code, it performs a task, and obviously it’s reusable.
+
Some functions rely on parameters to perform their task.
+
Some functions will return a final value.
+
+
Instead of writing all of your code inside of a single code block,
+
you can split it up into functions and call them as needed.
+
Let’s begin organizing your code using functions!
+
Define functions and call them
See video on udemy.
+
Notice
Functions’ name needs to represent the task it’s performing.
+
Notice
public means the function can be publicly accessed from any package or subfolder,
+
but because we’ve got only one package with a single class in it,
+
it doesn’t really matter what level of access you specify.
+
Notice
You can notice that main() is also a function, it has a very similar signature to our functions.
+
Every function has a specific task that it performs, the main() function performs the task of running our application.
+
The main() function has a very specific signature that the JVM looks for when you run your app, when it finds main() function, it executes it.
+
Parameters
Functions with parameters expect to receive values.(these functions rely on parameters to perform their task)
+
Functions【without】parameters【do not expect】to receive values.
+
Parameters are essentially just variables.
+
Arguments
A value that you pass into a function is known as an argument.
+
Parameters and Arguments makes functions completely reusable!
+
Notice
When a function defines parameters, then in order to call it,
+
you need to pass in a matching number of arguments based on the position of these parameters.
+
Return Values
+
You can return values from a function.
+
+
Notice
Bad practice: Your function handles the final result.
+
Good practice: Your function return the final result.
+
Notice
Whenever you specify a return type,
+
you need to make sure that something gets returned no matter what gets passed in.
+
Terminate the runTime
1
System.exit(0);
+
+
See you in workbook 4.3
+
Doc Comments
+
Can be used to describe what a function does.
+
+
If you’re working in a team of developers, you should have a document for every function.
+
How to write Doc Comments ?
See udemy
+
See you in workbook 4.4
+
Scope
+
The scope of a variable determines its life span.
+
+
The take home message is: You can never access a variable outside the scope that it was defined in.
+
Global Scope
Please against using global variables,
+
instead, you should keep everything local and use parameters,
+
because when you have too many global variables, you start losing track of what’s really going on(such as from your debugger in vscode).
+
+
Please watch the video on udemy.
+
+
Built-in Functions
The JDK provides so many built-in functions that you can call out of the box.
+
But to be honest, a good developer never memorizes code!
+
Instead, a good developer uses the internet! to read documentation. to find resources.
+
See you in workbook 4.5
+
Loops
For Loops: designed to run code a specific number of times.
+
While Loops: designed to run code an unknown number of times.
+
You will use the break and continue keywords to gain full control over your loops.
+
For Loops
1 2
for (inti=0; i < 3; i ++) { }
+
+
While Loops
What is a while loop ?
+
A while loop keeps running while a condition is true.
+
1 2
while (condition) { }
+
+
See you in workboook 5.8 ~ 5.10
+
continue
The continue keyword skips a run in the loop and continues with the next one.
+
break
The break keyword when invoked, breaks the loop entirely.
+
Nested Loops
A nested loop is a loop inside of another loop.
+
eg: useful when working with 2D arrays.
+
1 2 3 4
for (inti=0; i < 3; i++) { for (intj=0; j < 3; j++) { } }
+
+
+
Arrays、2D_Arrays
+
Looping Arrays、Updating Arrays
+
Arrays
Sometimes values can be closely related and creating one variable after another can be very messy such as below.
Although an array can hold many values, all of them have to share the same type.
+
For example as below, integerspoints to an array of integer values.
+
1
int[] integers = { 1, 2, 3 };
+
+
Talk is cheap
1
String[] kindoms = { "qwe", "asd", "zxc" };
+
+
The truth is:
+
the variable kingdoms doesn’t store the array directly
+
instead
+
it stores a [reference] that points to it
+
+
1 2 3
// You can try to compile and run this line of code, // you will get a hashcode representation of the reference. System.out.println(kingdoms);
+
+
and each element is stored at an index
+
+
what will happen if I try to access an element outside the range of the array ?
+
1
System.out.println(kingdoms[3]);
+
+
+
+
Java throws an ArrayIndexOutOfBoundsException, in essence, crashing our application, telling us that we have an error in our code —— “Index 3 is out of bounds”
+
Preparing to loop arrays
The length of an array indicates the number of items it contains.
// If you want to access each element in the array // Would you perfer to index them all individually ? numbers[0] numbers[1] numbers[2] numbers[3] numbers[4] numbers[5] // or use some kind of loop that iterates through every single element in the array ? // I think the looping approach would be more efficient.
+
+
1 2
// Print the elements of an integer array using a loop int[] numbers = {22, 24, 26, 29, 30};
+
+
for 循环
1 2 3 4
// autoComplete a for loop in vscode: fori<ENTER> for (inti=0; i < numbers.length; i++) { System.out.println(numbers[i]); }
+
+
foreach 循环
foreach simplifies looping through an array without the need for a counter or a counter increment or anything.
+
1 2 3 4
// autoComplete a foreach loop in vscode: fore<ENTER> for (int number : numbers) { System.out.println(number); }
+
+
It just automatically iterates through every single number inside of the numbers array.
+
So as you can see, foreach is much cleaner and concise than the traditional for loop,
+
but the traditional for loop is more flexible because the counter i give us more control over the looping process.
+
I’m a bit tired of using loops to print an array,
+
Java has a function called toString, it takes your array as an argument, and it returns a string that we can print.
package and import、static and final、enums、unit test、inheritance and polymorphism、
higher order functions、big decimal、interface、concurrency and multithreading
Reasons to learn Java
Java is general-purpose, which means that Java powers a wide range of applications. As a Java developer,
you can build web applications using Spring boot,
you can build applications on Android,
you can automate tasks using Selenium,
you can develop cloud native applications,
you can integrate microservices,
you can …
Java can run on any machine, it’s well known for its “write once, run anywhere”
This is because the Java Virtual Machine, the JVM, which is responsible for executing compiled Java code, can be installed on any platform
Java is the No.1 language for developing enterprise applications
To run Java code, you need
A Java Compiler - to compile your code.
A Java Runtime - to run the compiled code.
So, this is why JDK provides a Compiler、a Runtime、and a lot of other things.
Roadmap
Install a JDK(Java Development Kit) on your machine => from Amazon Corretto is recomended.
Amazon Corretto is just a distribution which once installed, does the work of setting up a JDK on your machine. Basically all you need to do is install it and it’s going to do the heavy lifting.
Finished ! you’ve just installed a JDK, now your computer can compile and run Java code.
Download a text editor to write code
I use vim & vscode(the extension pack for java is must).
Now you have everything you need to start building Java applications!
Write & Run your first Java code
1 2
# Every java file needs to follow the naming convention -> CamelCase touch HelloJava.java
1 2 3 4 5
// In java, you must write all of your code in a class. // And the ClassName needs to be the same as your FileName. // So here we create a class. classHelloJava { }
All right, so what’s next is the main() method, which is the entry point of a Java application.
And inside main, we’re going to print the message “Hello Java”
1 2 3 4 5 6 7 8 9 10 11 12
classHelloJava { // not understand this line is doesn't matter now. publicstaticvoidmain(String[] args) { System.out.println("Hello Java"); } }
/* Notice: the semicolon is really important, which means end of statement. every statement in Java, every line of code, needs a semicolon at the end. so if you forget your semicolon, your code is not gonna run. */
Now we’re ready to compile and run our Java code.
1 2 3 4 5
# The javac command compiles your javaCode into byteCode javac <FileName>.java # The java command executes the compiled code java <FileName>
For example
See you in workbook 1.1
Variable
Store data inside variable.
Java is strongly typed
which means that variables can only store values of their type.
Java is case sensitive
eg: people is not the same as People
The convention to naming a variable
lowerCamelCase
eg: int peopleOnBus = 20;
Update the value inside a variable
Just set it equal to a new value, or use +=、-=、…
See you in workbook 2.1
Use variables to store numeric data
Types: int and long
We can use int and long variables to store whole numbers.
You should know the difference between int and long and when to use int vs long.
intpeople=3; intwallet=20; System.out.println(wallet / people); // 6, which is not the result we want.
// So we need to make sure that at least one of these values is stored as a decimal, // then Java's going to know to return a decimal result. intpeople=3; doublewallet=20; System.out.println(wallet / people); // 6.666666666666667, this is the result we want.
Golden Rule
If precision is important, use double for math calculations.
Type: String
We can use the type String to store text.
1
Stringsentence="Hello world !";
String unlike int in memory,
no matter what you store in the integer variable, it’s always 4 bytes,
but with String, empty text alone takes up 24 bytes, and the more text that you add to a string, the more memory it takes up.
You can use the + operator to join two String values.
1 2 3
Stringsentence="His name is: "; Stringplaceholder="Harry"; System.out.println(sentence + placeholder);
You can use the + operator to blend values into a string.
1 2 3
doublepoints=50; Stringannouncement=" points for Gryffindor"; System.out.println(points + announcement); // 50.0 points for Gryffindor
Type: char
We can use the char type to store single characters.
1
chargender='F';
We can join a String value with a char value using the + operator.
1
System.out.println("Gender: " + 'F');
1
Stringgender="F";
It seems that String is more flexible than char,
so why not always use String ?
The answer is【memory】and【performance】!
char consumes less memory, and char is faster than String !
Summarize
There are 6 core data types (we didn’t cover boolean yet).
Data Type
Value
Amount of Memory (Bytes)
Valid Range of Values
int
Whole numbers
4
From: -2147483648 To: 2147483647
long
Very large whole numbers
8
From: -9223372036854775808 To: 9223372036854775807
Then you might be asking that when to use if vs switch ?
The only thing you can really do with switch is compare one variable against a list of values.
if statement is more flexible so that suitable for complex conditions, such as when you need to compare multiple variables, they give you the flexibility to evaluate compound conditions using logical operators.
1 2 3 4 5
if (temperature >= 80 && humidity >= 60) { System.out.println("It's too hot and humid\n"); } else { System.out.println("It's comfortable\n"); }
See you in workbook 3.6 and 3.7
As you write more and more code inside main(), you’ll notice that it becomes increasingly cluttered and messy.
And the more code you write, the more unreadable that it becomes.
Functions
A function is a grouping of code, it performs a task, and obviously it’s reusable.
Some functions rely on parameters to perform their task.
Some functions will return a final value.
Instead of writing all of your code inside of a single code block,
you can split it up into functions and call them as needed.
Let’s begin organizing your code using functions!
Define functions and call them
See video on udemy.
Notice
Functions’ name needs to represent the task it’s performing.
Notice
public means the function can be publicly accessed from any package or subfolder,
but because we’ve got only one package with a single class in it,
it doesn’t really matter what level of access you specify.
Notice
You can notice that main() is also a function, it has a very similar signature to our functions.
Every function has a specific task that it performs, the main() function performs the task of running our application.
The main() function has a very specific signature that the JVM looks for when you run your app, when it finds main() function, it executes it.
Parameters
Functions with parameters expect to receive values.(these functions rely on parameters to perform their task)
Functions【without】parameters【do not expect】to receive values.
Parameters are essentially just variables.
Arguments
A value that you pass into a function is known as an argument.
Parameters and Arguments makes functions completely reusable!
Notice
When a function defines parameters, then in order to call it,
you need to pass in a matching number of arguments based on the position of these parameters.
Return Values
You can return values from a function.
Notice
Bad practice: Your function handles the final result.
Good practice: Your function return the final result.
Notice
Whenever you specify a return type,
you need to make sure that something gets returned no matter what gets passed in.
Terminate the runTime
1
System.exit(0);
See you in workbook 4.3
Doc Comments
Can be used to describe what a function does.
If you’re working in a team of developers, you should have a document for every function.
How to write Doc Comments ?
See udemy
See you in workbook 4.4
Scope
The scope of a variable determines its life span.
The take home message is: You can never access a variable outside the scope that it was defined in.
Global Scope
Please against using global variables,
instead, you should keep everything local and use parameters,
because when you have too many global variables, you start losing track of what’s really going on(such as from your debugger in vscode).
Please watch the video on udemy.
Built-in Functions
The JDK provides so many built-in functions that you can call out of the box.
But to be honest, a good developer never memorizes code!
Instead, a good developer uses the internet! to read documentation. to find resources.
See you in workbook 4.5
Loops
For Loops: designed to run code a specific number of times.
While Loops: designed to run code an unknown number of times.
You will use the break and continue keywords to gain full control over your loops.
For Loops
1 2
for (inti=0; i < 3; i ++) { }
While Loops
What is a while loop ?
A while loop keeps running while a condition is true.
1 2
while (condition) { }
See you in workboook 5.8 ~ 5.10
continue
The continue keyword skips a run in the loop and continues with the next one.
break
The break keyword when invoked, breaks the loop entirely.
Nested Loops
A nested loop is a loop inside of another loop.
eg: useful when working with 2D arrays.
1 2 3 4
for (inti=0; i < 3; i++) { for (intj=0; j < 3; j++) { } }
Arrays、2D_Arrays
Looping Arrays、Updating Arrays
Arrays
Sometimes values can be closely related and creating one variable after another can be very messy such as below.
Although an array can hold many values, all of them have to share the same type.
For example as below, integerspoints to an array of integer values.
1
int[] integers = { 1, 2, 3 };
Talk is cheap
1
String[] kindoms = { "qwe", "asd", "zxc" };
The truth is:
the variable kingdoms doesn’t store the array directly
instead
it stores a [reference] that points to it
1 2 3
// You can try to compile and run this line of code, // you will get a hashcode representation of the reference. System.out.println(kingdoms);
and each element is stored at an index
what will happen if I try to access an element outside the range of the array ?
1
System.out.println(kingdoms[3]);
Java throws an ArrayIndexOutOfBoundsException, in essence, crashing our application, telling us that we have an error in our code —— “Index 3 is out of bounds”
Preparing to loop arrays
The length of an array indicates the number of items it contains.
// If you want to access each element in the array // Would you perfer to index them all individually ? numbers[0] numbers[1] numbers[2] numbers[3] numbers[4] numbers[5] // or use some kind of loop that iterates through every single element in the array ? // I think the looping approach would be more efficient.
1 2
// Print the elements of an integer array using a loop int[] numbers = {22, 24, 26, 29, 30};
for 循环
1 2 3 4
// autoComplete a for loop in vscode: fori<ENTER> for (inti=0; i < numbers.length; i++) { System.out.println(numbers[i]); }
foreach 循环
foreach simplifies looping through an array without the need for a counter or a counter increment or anything.
1 2 3 4
// autoComplete a foreach loop in vscode: fore<ENTER> for (int number : numbers) { System.out.println(number); }
It just automatically iterates through every single number inside of the numbers array.
So as you can see, foreach is much cleaner and concise than the traditional for loop,
but the traditional for loop is more flexible because the counter i give us more control over the looping process.
I’m a bit tired of using loops to print an array,
Java has a function called toString, it takes your array as an argument, and it returns a string that we can print.
Luckily, Ranger ships with rifle, a file launcher that is good at automatically finding out which program to use for what file type. —— knowing from Ranger’s doc
In Linux systems, processes can receive a variety of signals, such as SIGINT or SIGKILL. Each signal is sent in different situations and each has different behavior.
In this article, we’ll talk about SIGINT, SIGTERM, SIGQUIT, and SIGKILL. We’ll also see the difference between them.
Introduction to Signals
The signals are a method of communication between processes. When a process receives a signal, the process interrupts its execution and a signal handler is executed.
How the program behaves usually depends on the type of signal received. After handling the signal, the process may or may not continue its normal execution.
The Linux kernel can send signals, for instance, when a process attempts to divide by zero it receives the SIGFPE signal.
We can also send signals using the kill program. Let’s run a simple script in the background and stop it:
Alternatively, we can send signals in a terminal using key combinations. For instance, Ctrl+C sends SIGINT, Ctrl+S sends SIGSTOP, and Ctrl+Q sends SIGCONT.
Each signal has a default action, but a process can override the default action and handle it differently, or ignore it. However, some signals can’t be ignored nor handled differently and the default action is always executed.
We can handle signals in bash using the trap command. For instance, we can add trap date SIGINT in a script and it will print the date when SIGINT is received.
SIGINT
SIGINT is the signal sent when we press Ctrl+C. The default action is to terminate the process. However, some programs override this action and handle it differently.
One common example is the bash interpreter. When we press Ctrl+C it doesn’t quit, instead, it prints a new and empty prompt line. Another example is when we use gdb to debug a program. We can send SIGINT with Ctrl+C to stop the execution and return it to the gdb’s interpreter.
We can think of SIGINT as an interruption request sent by the user. How it is handled usually depends on the process and the situation.
Let’s write handle_sigint.sh using the trap command to handle SIGINT and print the current date:
1 2 3 4 5 6 7
#!/bin/bash
trapdate SIGINT
read input echo User input: $input echo Exiting now
We use read input to wait for the user interaction. Now, let’s run our script and let’s press Ctrl+C:
We can see the script didn’t exit. We can now terminate the script by writing some input:
1 2 3 4 5
$ ./handle_sigint.sh ^CSat Apr 10 15:32:07 -03 2021 live long and prosper User input: live long and prosper Exiting now
If we want to use a signal to terminate it, we can’t use SIGINT with this script. We should use SIGTERM, SIGQUIT, or SIGKILL instead.
SIGTERM and SIGQUIT
The SIGTERM and SIGQUIT signals are meant to terminate the process. In this case, we are specifically requesting to finish it. SIGTERM is the default signal when we use the kill command.
The default action of both signals is to terminate the process. However, SIGQUIT also generates a core dump before exiting.
When we send SIGTERM, the process sometimes executes a clean-up routine before exiting.
We can also handle SIGTERM to ask for confirmation before exiting. Let’s write a script called handle_sigterm.sh to terminate only If the user sends the signal twice:
SIGTERM_REQUESTED=0 handle_sigterm() { if [ $SIGTERM_REQUESTED -eq 0 ]; then echo"Send SIGTERM again to terminate" SIGTERM_REQUESTED=1 else echo"SIGTERM received, exiting now" exit 0 fi }
trap handle_sigterm SIGTERM
TIMEOUT=$(date +%s) TIMEOUT=$(($TIMEOUT + 60))
echo"This script will exit in 60 seconds" while [ $(date +%s) -lt $TIMEOUT ]; do sleep 1; done echo Timeout reached, exiting now
Now, let’s run it on background executing $ ./handle_sigterm.sh &. Then, we run $ kill <PID> twice:
1 2 3 4 5 6 7
$ ./handle_sigterm.sh & [1] 6092 $ kill 6092 Send SIGTERM again to terminate $ kill 6092 SIGTERM received, exiting now [1]+ Done ./handle_sigterm.sh
As we can see, the script exited after it received the second SIGTERM.
SIGKILL
When a process receives SIGKILL it is terminated. This is a special signal as it can’t be ignored and we can’t change its behavior.
We use this signal to forcefully terminate the process. We should be careful as the process won’t be able to execute any clean-up routine.
One common way of using SIGKILL is to first send SIGTERM. We give the process some time to terminate, we may also send SIGTERM a couple of times. If the process doesn’t finish on its own, then we send SIGKILL to terminate it.
Let’s rewrite the previous example to try to handle SIGKILL and ask for confirmation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#!/bin/bash
SIGKILL_REQUESTED=0 handle_sigkill() { if [ $SIGKILL_REQUESTED -eq 0 ]; then echo"Send SIGKILL again to terminate" SIGKILL_REQUESTED=1 else echo"Exiting now" exit 0 fi }
trap handle_sigkill SIGKILL
read input echo User input: $input
Now, let’s run it on a terminal, and let’s send SIGKILL only once with $ kill -SIGKILL <pid>:
1 2 3
$ ./handle_sigkill.sh Killed $
We can see it terminate right away without asking to re-send the signal.
How SIGINT Relates to SIGTERM, SIGQUIT and SIGKILL
Now that we understand more about signals, we can see how they relate to each other.
The default action for SIGINT, SIGTERM, SIGQUIT, and SIGKILL is to terminate the process. However, SIGTERM, SIGQUIT, and SIGKILL are defined as signals to terminate the process, but SIGINT is defined as an interruption requested by the user.
In particular, if we send SIGINT (or press Ctrl+C) depending on the process and the situation it can behave differently. So, we shouldn’t depend solely on SIGINT to finish a process.
As SIGINT is intended as a signal sent by the user, usually the processes communicate with each other using other signals. For instance, a parent process usually sends SIGTERM to its children to terminate them, even if SIGINT has the same effect.
In the case of SIGQUIT, it generates a core dump which is useful for debugging.
Now that we have this in mind, we can see we should choose SIGTERM on top of SIGKILL to terminate a process. SIGTERM is the preferred way as the process has the chance to terminate gracefully.
As a process can override the default action for SIGINT, SIGTERM, and SIGQUIT, it can be the case that neither of them finishes the process. Also, if the process is hung it may not respond to any of those signals. In that case, we have SIGKILL as the last resort to terminate the process.
Conclusion
In this article, we learned about signals and the difference between SIGINT, SIGTERM, SIGQUIT, and SIGKILL. Also, we briefly learned how to handle signals in bash.
We saw how SIGINT sometimes doesn’t kill the process as it may a different meaning. On the other hand, the SIGKILL signal will always terminate the process.
We also learned that SIGQUIT generates a core dump by default and that SIGTERM is the preferred way to kill a process.